1. 程式人生 > >資料庫表設計、 資料庫分層、myslq水平拆分、oracle表分割槽

資料庫表設計、 資料庫分層、myslq水平拆分、oracle表分割槽

資料庫表設計

資料庫表結構設計方法及原則(li)
  資料庫設計的三大正規化:為了建立冗餘較小、結構合理的資料庫,設計資料庫時必須遵循一定的規則。在關係型資料庫中這種規則就稱為正規化。正規化是符合某一種設計要求的總結。要想設計一個結構合理的關係型資料庫,必須滿足一定的正規化。

  在實際開發中最為常見的設計正規化有三個:第一正規化是最基本的正規化。如果資料庫表中的所有欄位值都是不可分解的原子值,就說明該資料庫表滿足了第一正規化;第二正規化在第一正規化的基礎之上更進一層。第二正規化需要確保資料庫表中的每一列都和主鍵相關,而不能只與主鍵的某一部分相關(主要針對聯合主鍵而言)。也就是說在一個數據庫表中,一個表中只能儲存一種資料,不可以把多種資料儲存在同一張資料庫表中;第三正規化需要確保資料表中的每一列資料都和主鍵直接相關,而不能間接相關。總結一下,就是:第一正規化(確保每列保持原子性);第二正規化(確保表中的每列都和主鍵相關);第三正規化(確保每列都和主鍵列直接相關,而不是間接相關)。

  在目前的企業資訊系統中,資料庫還是最佳的資料儲存方式,雖然已經有很多的書籍在指導我們進行資料庫設計,但應該那種方式是設計資料庫的表結構的最好方法、設計時應遵從什麼樣的原則、四個正規化如何能夠用一種方式達到順暢的應用等是我一直在思考和總結的問題,下文是我針對這幾個問題根據自己的設計經歷準備總結的一篇文章的提綱,歡迎大家一塊進行探討,集思廣益。其中提到了領域建模的概念,但未作詳細解釋,希望以後能夠有時間我們針對這個命題進行深入探討。

  1.不應該針對整個系統進行資料庫設計,而應該根據系統架構中的元件劃分,針對每個元件所處理的業務進行元件單元的資料庫設計;不同元件間所對應的資料庫表之間的關聯應儘可能減少,如果不同元件間的表需要外來鍵關聯也儘量不要建立外來鍵關聯,而只是記錄關聯表的一個主鍵,確保元件對應的表之間的獨立性,為系統或表結構的重構提供可能性。

//注意他這裡說的是"不要建立外來鍵關聯",建立外來鍵關聯的語句是:
//foreign key(member_id) references member (id);
//我們幾乎沒有用到這條語句,因為我們就是這樣做的,用到外來鍵時,只是記錄關聯表的主鍵,而非在資料庫級別上建立外來鍵。
//也不知道是歪打正著,還是前輩DBA過於強大,已經考慮好了。
  2.採用領域模型驅動的方式和自頂向下的思路進行資料庫設計,首先分析系統業務,根據職責定義物件。物件要符合封裝的特性,確保與職責相關的資料項被定義在一個物件之內,這些資料項能夠完整描述該職責,不會出現職責描述缺失。並且一個物件有且只有一項職責,如果一個物件要負責兩個或兩個以上的職責,應進行分拆。

// 領域模型驅動的方式,目前用的還不是很熟,考慮的不夠多。因為經常的資料庫中的表只是拿來做儲存用而已,
//特別是小需求,要加什麼欄位,找到相關表加上去就行了,不太考慮領域模型。這個在中文站老業務表裡很常見
  3.根據建立的領域模型進行資料庫表的對映,此時應參考資料庫設計第二正規化:一個表中的所有非關鍵字屬性都依賴於整個關鍵字。關鍵字可以是一個屬性,也可以是多個屬性的集合,不論那種方式,都應確保關鍵字能夠保證唯一性。在確定關鍵字時,應保證關鍵字不會參與業務且不會出現更新異常,這時,最優解決方案為採用一個自增數值型屬性或一個隨機字串作為表的關鍵字。

  4.由於第一點所述的領域模型驅動的方式設計資料庫表結構,領域模型中的每一個物件只有一項職責,所以物件中的資料項不存在傳遞依賴,所以,這種思路的資料庫表結構設計從一開始即滿足第三正規化:一個表應滿足第二正規化,且屬性間不存在傳遞依賴。

//資料庫三正規化記不得的同學去查資料溫習一下。
//個人認為第三正規化的目的是儘量減少資料冗餘,保證相同的資料只存在一份。
//第三正規化其實我們遵守的並不是很嚴格,特別是老的資料庫表中會有冗餘欄位。這個要看情況決定吧。
  5.同樣,由於物件職責的單一性以及物件之間的關係反映的是業務邏輯之間的關係,所以在領域模型中的物件存在主物件和從物件之分,從物件是從1-N或N-N的角度進一步完善主物件的業務邏輯,所以從物件及物件關係對映為的表及表關聯關係不存在刪除和插入異常。

//最後一句看不懂,可能是"所以表及表關聯關係不應該出現刪除和插入異常。"?
  6.在對映後得出的資料庫表結構中,應再根據第四正規化進行進一步修改,確保不存在多值依賴。這時,應根據反向工程的思路反饋給領域模型。如果表結構中存在多值依賴,則證明領域模型中的物件具有至少兩個以上的職責,應根據第一條進行設計修正。第四正規化:一個表如果滿足BCNF,不應存在多值依賴。 

複製程式碼
//第四正規化我們遵守的並不多吧。
//例如:
//VAS_WP_CONFIG.config_name欄位的值包括:adv(廣告主題)/glare(炫彩滾動主題)/theme_simple(普通主題)/theme_cartoon(動畫主題)/ theme_none(不顯示背景主題)
//cate_background(類目背景)/video(公司視訊)/board_cartoon(動畫招牌)/board_simple(普通招牌)等。
//如果遵守第四正規化,則需要新增一張VAS_WP_CONFIG_NAME表,儲存配置名稱列舉值,而VAS_WP_CONFIG.config_name欄位改為VAS_WP_CONFIG.config_name_id。
//這樣做更利於擴充套件,不會因為每個人的理解不一致而向VAS_WP_CONFIG.config_name欄位裡設定亂七八糟的值,但是這樣需要維護更多的小表,造成資料值表的數量膨脹,DBA可能會覺得管理上有更多的困難。
//我們採用潛規則約定、java列舉類等其它方式來進行保證。但有時候效果並不是很好,經常發現舊資料庫表中列舉欄位的值五花八門,不全是約定的。
複製程式碼
  7.在經過分析後確認所有的表都滿足二、三、四正規化的情況下,表和表之間的關聯儘量採用弱關聯以便於對錶欄位和表結構的調整和重構。並且,我認為資料庫中的表是用來持久化一個物件例項在特定時間及特定條件下的狀態的,只是一個儲存介質,所以,表和表之間也不應用強關聯來表述業務(資料間的一致性),這一職責應由系統的邏輯層來保證,這種方式也確保了系統對於不正確資料(髒資料)的相容性。當然,從整個系統的角度來說我們還是要盡最大努力確保系統不會產生髒資料,單從另一個角度來說,髒資料的產生在一定程度上也是不可避免的,我們也要保證系統對這種情況的容錯性。這是一個折中的方案。

  8.應針對所有表的主鍵和外來鍵建立索引,有針對性的(針對一些大資料量和常用檢索方式)建立組合屬性的索引,提高檢索效率。雖然建立索引會消耗部分系統資源,但比較起在檢索時搜尋整張表中的資料尤其時表中的資料量較大時所帶來的效能影響,以及無索引時的排序操作所帶來的效能影響,這種方式仍然是值得提倡的。

//索引目前都是DBA根據具體的SQL來建立的,不過開發寫SQL時,也應該適當考慮一下欄位的索引。
  9.儘量少採用儲存過程,目前已經有很多技術可以替代儲存過程的功能如"物件/關係對映"等,將資料一致性的保證放在資料庫中,無論對於版本控制、開發和部署、以及資料庫的遷移都會帶來很大的影響。但不可否認,儲存過程具有效能上的優勢,所以,當系統可使用的硬體不會得到提升而效能又是非常重要的質量屬性時,可經過平衡考慮選用儲存過程。

//目前都是杜絕使用儲存過程的,我覺得用起來比較方便,對於我們來說,主要原因是會給DBA帶來管理方面的麻煩,
//因為時間一長,儲存過程的邏輯和使用場景,往往沒人能瞭解,容易產生更多問題
  10.當處理表間的關聯約束所付出的代價(常常是使用性上的代價)超過了保證不會出現修改、刪除、更改異常所付出的代價,並且資料冗餘也不是主要的問題時,表設計可以不符合四個正規化。四個正規化確保了不會出現異常,但也可能由此導致過於純潔的設計,使得表結構難於使用,所以在設計時需要進行綜合判斷,但首先確保符合四個正規化,然後再進行精化修正是剛剛進入資料庫設計領域時可以採用的最好辦法。

  11.設計出的表要具有較好的使用性,主要體現在查詢時是否需要關聯多張表且還需使用複雜的SQL技巧。我感覺遵守的正規化越多,就越使SQL複雜,具體情況具體分析。設計出的表要儘可能減少資料冗餘,確保資料的準確性,有效的控制冗餘有助於提高資料庫的效能

  因此,考慮了以上條件之後,表設計約定規則如下:

複製程式碼
//規則1:表必須要有主鍵。
//規則2:一個欄位只表示一個含義。
//規則3:總是包含兩個日期欄位:gmt_create(建立日期),gmt_modified(修改日期),且這兩個欄位不應該包含有額外的業務邏輯。
//規則4:MySQL中,gmt_create、gmt_modified使用DATETIME型別。
//規則5:禁止使用複雜資料型別(陣列,自定義型別等)。
//規則6: MySQL中,附屬表拆分後,附屬表id與主表id保持一致。不允許在附屬表新增主鍵欄位。
//規則7: MySQL中,存在過期概念的表,在其設計之初就必須有過期機制,且有明確的過期時間。過期資料必須遷移至歷史表中。
//規則8: MySQL中,不再使用的表,必須通知DBA予以更名歸檔。
//規則9: MySQL中,線上表中若有不再使用的欄位,為保證資料完整,禁止刪除。
//規則10: MySQL中,禁止使用OCI驅動,全部使用THI驅動。
複製程式碼
關於MySQL的部分學習筆記總結:

一、事務跟儲存引擎

  1.四種事務隔離級別:read uncommited, read commited(大多數db預設的),repeatable read(mysql預設), seriazable。

  2.mysql是預設的auto commited, 也就是說每次查詢預設都是自動提交的(show variables like 'autocommited')。mysql可以通過set transaction isolatioin level命令來設定隔離級別,例如:set session transaction isolation level read commited。

  3.mysql中像innodb採用mvcc(多版本併發控制)來處理併發。mvcc只工作在read commited,repeatable read這兩種事務隔離級別上。read uncommited隔離級別不相容mvcc是因為在該級別得下的查詢,不讀取符合當前事務版本的資料行,而是最新版本的資料行。seriazable隔離級別不相容MVCC,因為該級別下的讀操作會對每個返回行進行加鎖。

  4.選擇儲存引擎,併發選用myisam,事務選擇innodb,myisam比innodb更容易出錯,出錯了恢復的時間也比較長。只有myisam支援全文檢索。

  5.把表從一種儲存引擎轉到另一種引擎:

//  1.    alter table mytable engine=falcon;  操作費時,可能會佔用伺服器的所有i/o處理能力。
//  2.    create table innodb_table like myisam_table;
//        alter table innodb_table engine=innodb;
//        insert into innodb_table select * from myisam_table;
二、資料型別

  1.儘可能的要把field定義為Not NULL, mysql比較難優化使用了可空列的查詢,它會使索引,索引統計更加複雜。可空列需要更多的儲存空間,還需要mysql內部進行特殊處理,當可空列被索引時,每條記錄都需要一個格外的位元組。 即使要在表中儲存"沒有值"的欄位,考慮使用0,特殊欄位或者空字串來代替。

  2.datetime與timestamp能儲存同樣的資料:精確度為秒,但是timestamp使用的空間只有datetime的一半,還能儲存時區,擁有特殊的自動更新能力。但是timestamp儲存的時間範圍要比datetime要小得多。mysql能儲存的最細的時間粒度為秒

  3.mysql支援很多種別名,如bool,integer,nummeric.

  4.float與double型別支援使用標準的浮點運算進行近似計算。 Decimal型別儲存精確的小數,在>=mysql5.0,mysql伺服器自身進行了decimal的運算,因為CPU不支援直接對它進行運算,所以慢一點。

  5.mysql會把text與blob型別的列當成有實體的物件來進行儲存。他們有各自的資料型別家族(tinytext,smalltext,text,mediumtext,longtext; blob類似); mysql對blob與text列排序方式和其他型別有所不同,它不會按照字串的完整長度來排序。而只是按照max_sort_length規定的若干個位元組來進行排序。

  6.採用enum來代替字串型別。mysql在內部把每個列舉值都儲存為整數。enum在內部是按照數字進行排序的,而不是按照字串。enum最不好的就是字串列表是固定的,新增和刪除必須使用alter table。

  7.ip地址,一般會採用varchar(15)列來儲存。事實上,IP地址是個無符號的32位整數,而不是字串。mysql提供了inet_aton()和inet_nota()函式在證書與ip地址之間進行轉換。

三、索引

  1.聚集索引不僅僅是一種單獨的索引型別,而且是一種儲存資料的方式。Innodb引擎的聚集索引實際上在同樣的結構中儲存了B-Tree索引和資料行。當表有聚集索引時,它的資料行實際上儲存在索引的葉子上。注意是儲存引擎來實現索引。

  2.myisam與innodb資料佈局:myisam索引樹(無論是主鍵索引還是非主鍵索引)葉子節點都是指向的資料行,而innodb中聚集索引,主鍵索引樹葉子節點就帶得有資料的內容,而非主鍵索引樹中葉子節點指向主鍵值,而不是資料的位置。

  3.mysql有兩種產生排序結果的方式:使用檔案排序,或者掃描有序的索引。目前只有myisam支援全文索引。

  4.myisam表有表級鎖;myisam表不支援事務,實際上,myisam並不保證單條命令完成;myisam只快取了mysql程序內部的索引,並儲存在鍵快取區內。OS快取了表的資料;行被緊密的儲存在一起,磁碟上的資料有很小的磁碟佔用和快速的全表掃描。

  5.innodb支援事務和四種事務隔離級別;在mysql5.0中,只有innodb支援外來鍵;支援行級鎖與mvcc;所有的innodb表都是按照主鍵聚集的;所有索引(出開主鍵)都是按主鍵引用行;索引沒有使用字首壓縮,因此索引可能比myisam大很多;資料轉載緩慢;阻塞auto_increment,也就是用表級鎖來產生每個auto_increment。

四、MYSQL效能分析

  1.mysql提供了一個benchmark(int 迴圈次數,char* 表示式); 可以分析表示式執行所花時間。 例如:

// select BENCHMARK(10000,SHA1('aaaaaaaaaaaaaaaa'))
  2.mysql有兩種查詢日誌:普通日誌和慢速日誌。

五、MYSQL高階特性

  1.在mysql中,只有myisam儲存引擎支援全文索引。myisam全文索引是一種特殊的具有兩層結構的B樹。

  2.儲存引擎事務在儲存引擎內部被賦予acid屬性,分散式(XA)是一種高層次事務,它可以歷喲內部個兩段提交的方式將acid屬性擴充套件到儲存引擎外部,甚至資料庫外部。階段1:通知所有提交者準備提交 階段2:通知所有參與者進行真正提交。

  3.mysql 的字符集和校對規則有 4 個級別的預設設定:伺服器級、資料庫級、表級和欄位級。Mysql4.1 開始支援 SQL 的子查詢。

複製程式碼
/******************************************/
/*   資料庫全名 = 
[email protected]
:3318【mysql】 */ /* 表名稱 = task_new */ /******************************************/ CREATE TABLE `task_new` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主鍵', `task_name` varchar(128) NOT NULL COMMENT '任務名稱', `image` varchar(128) DEFAULT NULL COMMENT '任務圖示', `description` varchar(1024) NOT NULL COMMENT '任務描述', `content` varchar(1024) NOT NULL COMMENT '任務內容', `finished_message` varchar(128) DEFAULT NULL COMMENT '任務完成提示資訊', `task_scope` int(11) NOT NULL COMMENT '任務範圍, 0-平臺任務, 1-遊戲任務', `series_task` int(11) NOT NULL DEFAULT '0' COMMENT '任務型別: 系列任務,單獨任務', `task_type` int(11) NOT NULL DEFAULT '0' COMMENT '任務型別: 固定任務, 推廣任務, 日常任務', `pre_task` varchar(128) DEFAULT NULL COMMENT '前置任務', `post_task` varchar(128) DEFAULT NULL COMMENT '後置任務', `task_status` int(11) NOT NULL COMMENT '任務狀態, 待稽核、未開始、生效中、已暫停、已完成、稽核未通過', `auto_task` tinyint(4) NOT NULL DEFAULT '1' COMMENT '是否手動任務, 0-否, 1-是', `is_required` tinyint(4) NOT NULL COMMENT '是否必須任務', `event_type` varchar(64) DEFAULT NULL COMMENT '關心的事件型別', `task_target` bigint(20) DEFAULT '0' COMMENT '任務目標', `reset_num` int(11) NOT NULL COMMENT '重置次數', `reset_cycle` int(11) NOT NULL COMMENT '重置週期', `task_interval` int(11) NOT NULL COMMENT '任務間隔', `xiaoer` bigint(20) unsigned NOT NULL COMMENT '建立人', `review_id` bigint(20) unsigned NOT NULL COMMENT '稽核人ID', `last_start_time` datetime DEFAULT NULL COMMENT '上次生效時間', `gmt_create` datetime NOT NULL COMMENT '建立時間', `gmt_modified` datetime NOT NULL COMMENT '修改時間', `start_time` datetime NOT NULL COMMENT '開始時間', `end_time` datetime NOT NULL COMMENT '結束時間', `start_condition` varchar(1024) NOT NULL COMMENT '任務觸發條件', `end_condition` varchar(1024) NOT NULL COMMENT '任務完成條件', `enable` tinyint(4) NOT NULL DEFAULT '1' COMMENT '是否可用', `rule` varchar(4096) NOT NULL COMMENT '任務規則', `priority` int(11) NOT NULL DEFAULT '1' COMMENT '任務優先順序', `progress_rule` varchar(2048) NOT NULL DEFAULT '' COMMENT '進度計算規則', `order_no` int(11) DEFAULT '1' COMMENT '排序號', `classification` int(11) DEFAULT '0' COMMENT '0:預設分類\n1:玩遊戲\n2:抽獎', `level` int(11) DEFAULT '0' COMMENT '針對同一個分類,不同的等級', `ext1` longtext COMMENT '擴充套件欄位1(UU中使用該欄位指示按鈕跳轉)', `ext2` longtext COMMENT '擴充套件欄位2,暫時預留', `channel` int(11) DEFAULT '0' COMMENT '任務渠道:0-uu或者1-game_box', `consecutive_day` int(11) DEFAULT '1' COMMENT '連續完成任務的天數', `activity` varchar(256) DEFAULT 'default' COMMENT '任務所屬的活動名字', `device` text COMMENT '機型', `packages` text COMMENT '應用', PRIMARY KEY (`id`), KEY `name_channel` (`task_name`,`channel`), KEY `activity` (`activity`(255)) ) ENGINE=InnoDB AUTO_INCREMENT=1194 DEFAULT CHARSET=utf8 COMMENT='任務表'; --------------------------------------------------------------------------------------- 資料庫表設計總結 一、實體與表對應關係 表<=>實體,欄位<=>屬性。 二、表與表的關係(實體間的關係):一對一、一對多、多對多 一對一:一條記錄只對應其他表中的一條記錄有關係 學生基本資訊表t_student,成績表t_studentScore含有一個外來鍵studentId。基本資訊表中的studentId和成績表中的studentId就是一對一的關係。 一對多:A表一條記錄對應B表中多條記錄有關係,B表的記錄不被A表記錄共享(有關係)。 班級表和學生表,一個班級有多個學生,對班級來說就是一對多的關係。 多對多:A表一條記錄和B表多條記錄有關係,B表的一條記錄也和A表的多條記錄有關係(互相共享)。 學生表和科目表,學生可以選擇多個科目,每個科目可以被多個學生選擇。 三、基本表的完整性 (1)原子性。欄位是不可再分解的。 (2)原始性。記錄是原始資料(基礎資料)的記錄。 (3)穩定性。結構是相對穩定的,表中的記錄是要長期儲存的。 (4)演繹性。由基本表與程式碼表中的資料,可以派生出所有的輸出資料。 四、其他常用表 1.中間表 中間表是針對多對多關係的。就比如做公交查詢系統,裡面有兩個表,分別是車站表t_busstation、線路表t_road,根據常識,一個站有多個線路經過,而每個線路又有多個車站,怎麼才能將兩個表聯絡起來呢,如果是一對一,一對多,我們一個表, 兩個表就可以將他們實現了。但是多對多呢,這樣我們就必須藉助中間表用來連線兩個表。一般中間表只有一個自增主鍵+兩個表的主鍵。中間表是沒有屬性的因為它不是一個基本表。 2.臨時表 臨時表是那些以#號開頭為名字的資料表,它主要是用來存放臨時資料的,當用戶斷開連線但沒有清除臨時表裡的資料時,系統會自動把臨時表裡的資料清空。臨時表是放在系統資料庫 tempdb中的,而不是當前資料庫。 臨時表分兩種:本地臨時表和全域性臨時表。 a.本地臨時表 本地臨時表是以#開頭的,只對當前的資料庫使用者可見,而其他的使用者是不可見的。當資料庫例項斷開後當然也就丟失了資料了,不管是顯式清空還是系統回收。 b.全域性臨時表 以“##”開頭的,而且是對所有的使用者都是可見,當你斷開資料庫例項連線時,只要還有別的系統專案在引用它,連著資料庫,那麼資料就存在,只有當別的系統也全部斷開連線時,系統才會清除全域性臨時表的資料。 建立臨時表的語句: 複製程式碼 本地臨時表: create table #student ( studentID int , studentName nvarchar (40), classID int ) 複製程式碼 複製程式碼 全域性臨時表: create table ##student ( studentID int , studentName nvarchar (40). classID int ) 複製程式碼 也可以用SQL語句完成: select * from employee into #student 五、三大正規化 第一正規化:如果每列(或者每個屬性)都是不可再分的最小資料單元(也稱為最小的原子單元),則滿足第一正規化.比如一個工人的基本資訊表,裡面有工人的工號,性別,年齡,這些屬性都是不可分割的,所以這個表就符合了第一正規化。 第二正規化: 就是在第一正規化的基礎上延伸,使之表裡的每個欄位都與主鍵發生關係。假如一個關係滿足第一正規化,並且除了主鍵以外的其它欄位,都依賴於該主鍵,則滿足第二正規化. 例如:訂單表(訂單編號、產品編號、定購日期、價格、……),"訂單編號"為主鍵,"產品編號"和主鍵列沒有直接的關係,即"產品編號"列不依賴於主鍵列,這個列我們就可以把它刪除。 第三正規化:在第二正規化的基礎上更進一步,也就是為了實現表裡的列都與主鍵列直接相關,不是間接相關。這個我們可以用“Armstrong 公理”中的傳遞規則來推理。 定義: 設U是關係模式R 的屬性集,F 是R 上成立的只涉及U 中屬性的函式依賴集。若X→Y 和 Y→Z在R 上成立,則X →Z 在R 上成立。因此我們就來看在網上搜索到的例子:例如:訂單表(訂單編號,定購日期,顧客編號,顧客姓名,……),初看該表沒有問題,滿足第二正規化,每列都和主鍵列"訂單編號"相關,再細看你會發現"顧客姓名"和"顧客編號"相關,"顧客編號"和"訂單編號"又相關,最後經過傳遞依賴,"顧客姓名"也和"訂單編號"相關。為了滿足第三正規化,應去掉"顧客姓名"列,放入客戶表中。 這裡其實就是為了說明資料庫的表裡步要出現冗餘,在顧客表裡已經有了"顧客姓名"了,而在訂單表裡就別出現了,而直接根據顧客編號相關聯就可以,否則造成資源浪費。 三大正規化延伸: 第一正規化:1NF是對屬性的原子性約束,要求屬性具有原子性,不可再分解; 第二正規化:2NF是對記錄的惟一性約束,要求記錄有惟一標識,即實體的惟一性; 第三正規化:3NF是對欄位冗餘性的約束,即任何欄位不能由其他欄位派生出來,它要求欄位沒有冗餘。 其實在設計資料庫的時候我們最多的要遵循的就是第三正規化,但是並不是越滿足第三正規化資料庫就設計的越完美,這種錯誤是錯誤的。有時候增加點冗餘相反的會提高訪問速率,因此在實際的設計過程中應降低對正規化的要求。 以前對資料冗餘並不是很瞭解,在百度知道里的定義是這樣的:在一個數據集合中重複的資料稱為資料冗餘. 但是不是說我們表的主鍵在其他表裡重複出現就是冗餘,這不是,而是為了連線兩個表。只有非鍵欄位就是既不是主鍵外來鍵等約束的鍵如果重複出現,就會形成資料冗餘。資料冗餘也包括重複性冗餘和派生冗餘。比如工人表裡有"基本工資","獎金"兩列,然後還有一個"總工資"的列,這個總工資就是派生冗餘。低階的重複性冗餘一定要避免,杜絕,但是像派生冗餘還是提倡的因為它能提高訪問的效率。 個人總結: 事物的屬性對應表的屬性,將一張表看作一個事物。如,書的屬性有價格、重量、等等。(一般表都有Id來區分每條記錄) ----------------------------------------------------------------------------

資料庫分層

資料倉庫分層的原因

1通過資料預處理提高效率,因為預處理,所以會存在冗餘資料

2如果不分層而業務系統的業務規則發生變化,就會影響整個資料清洗過程,工作量巨大

3通過分層管理來實現分步完成工作,這樣每一層的處理邏輯就簡單了



標準的資料倉庫分層:ods(臨時儲存層),pdw(資料倉庫層),mid(資料集市層),app(應用層)

ods:歷史儲存層,它和源系統資料是同構的,而且這一層資料粒度是最細的,這層的表分為兩種,一種是儲存當前需要載入的資料,一種是用於儲存處理完後的資料。

pdw:資料倉庫層,它的資料是乾淨的資料,是一致的準確的,也就是清洗後的資料,它的資料一般都遵循資料庫第三正規化,資料粒度和ods的粒度相同,它會儲存bi系統中所有歷史資料

mid:資料集市層,它是面向主題組織資料的,通常是星狀和雪花狀資料,從資料粒度將,它是輕度彙總級別的資料,已經不存在明細的資料了,從廣度來說,它包含了所有業務數量。從分析角度講,大概就是近幾年

app:應用層,資料粒度高度彙總,倒不一定涵蓋所有業務資料,只是mid層資料的一個子集。



資料倉庫的目的是構建面向分析的整合化資料環境,為企業提供決策支援。資料倉庫的context也可以理解為:資料來源,資料倉庫,資料應用



資料倉庫可以理解為中間整合化資料管理的一個平臺

etl(抽取extra,轉化transfer,裝載load)是資料倉庫的流水線,也可以認為是資料倉庫的血液。

資料倉庫的儲存並不需要儲存所有原始資料,因為比如你儲存冗長的文字資料完全沒必要,但需要儲存細節資料,因為需求是多變的,而且資料倉庫是匯入資料必須經過整理和轉換使它面向主題,因為前臺資料庫的資料是基於oltp操作組織優化的,這些可能不適合做分析,面向主題的組織形式才有利於分析。

多維資料模型就是說可以多維度交叉查詢和細分,應用一般都是基於聯機分析處理(online analytical process OLAP),面向特定需求群體的資料集市會基於多位資料模型構建

而報表展示就是將聚合資料和多維分析資料展示到報表,提供簡單和直觀的資料。

元資料,也叫解釋性資料,或者資料字典,會記錄資料倉庫中模型的定義,各層級之間的對映關係,監控資料倉庫的資料狀態和etl的任務執行狀態。一般通過元資料資料庫來統一儲存和管理元資料。


------------------------------------------------------------------------------
-基本查詢
select id,c_mmcode,c_mmroomname,c_parentId from s_mmroom;
--層次化查詢
select id,c_mmcode,c_mmroomname,c_parentId from s_mmroom 
start with c_parentId='0' connect by prior id = c_parentId;
--使用level節點
select level,id,c_mmcode,c_mmroomname,c_parentId from s_mmroom
start with c_parentId='0' connect by prior id = c_parentId;
--查詢層次數
select count(distinct level) from s_mmroom 
start with c_parentId='0' connect by prior id = c_parentId;
---查詢結果的層次化
select level,LPAD(' ',2*LEVEL-1)||' '||c_mmcode||' '||c_mmroomname,c_parentId from s_mmroom
start with c_parentId='0' connect by prior id = c_parentId;
--從非根節點開始遍歷 
select level,LPAD(' ',2*LEVEL-1)||' '||c_mmcode||' '||c_mmroomname,c_parentId from s_mmroom
start with c_parentId='00000000000000000000000000000000' connect by prior id = c_parentId;
--在START WITH中使用子查詢  下面這個查詢使用子查詢來選擇指定節點的。然後傳給START WITH子句。
select level,lpad(' ',2*level-1)||' '||c_mmroomname from s_mmroom
start with c_parentId=(select id from s_mmroom where c_mmroomname like '%中國船級社%')
connect by prior id =c_parentId;
--從下向上遍歷樹  不一定非要按照從父節點到子節點的順序從上至下遍歷樹;也可以從某個子節點開始,從下而上遍歷。實現的方法是交換父節點和子節點在CONNECT BY PRIOR子句中的順序
select level,lpad(' ',2*level-1)||' '||c_mmroomname from s_mmroom
start with c_parentId='00000000000000000000000000000000' connect by prior id = c_parentId;
--從層次化查詢中刪除節點和分支   略 表名後面加where
--在層次化查詢中加入其它條件      略 表名後面加where
------------------------------------------------------------------
BLOCK(塊)

block 是oracle中最小的分配單位,也是最小的I/O單位,可以在建立資料庫時設定block的大小,可以設為任意的大小,但為了支援與相容方面,建議現實中設為2的冪,例如:

2KB、4KB、8KB,最大為32KB。



EXTENT(區)

extent 是由邏輯連續的block組成的,注意是邏輯上的,一般來講,檔案本身在磁碟上不是連續的,extent的大小可能是一個block的大小,也可能大到2GB,



SEGMENT(段)

segment 是由一個或多個extent組成的,segment就是佔用儲存空間的資料庫物件,例如表、索引等,segment在tablespace中,但可以包含這個tablespace中的多個數據檔案中的資料



TABLESPACE(表空間)

ORACLE資料庫是由一個或多個TABLESPACE構成,什麼是TABLESPACE? 一種邏輯上的儲存容器,包括一個或多個datafile(資料檔案)。tablespace可以包含多個segment(表段,索引段等),但一個segment只能屬於一個tablespace。

                                              

上圖中圓柱形代表datafile(user_data01.db和user_data02.dbf),T1,T2,I1是3個segment(可能是2個表和1個索引),這個USER_DATA  TABLESPACE分配了4個extent,T1和I1各有一個extent。如果這個TABLESPACE需要更多的空間,可以調整已分配給該TABLESPACE的datafile的大小,也可以增加第三個datafile。

要說明的是上述所討論的概念除了block都是邏輯上的概念,所謂連續的也是邏輯上連續的。
-------------------------------------------------------------------------------

myslq水平拆分

mysql的水平拆分和垂直拆分
1,水平分割:

例:QQ的登入表。假設QQ的使用者有100億,如果只有一張表,每個使用者登入的時候資料庫都要從這100億中查詢,會很慢很慢。如果將這一張表分成100份,每張表有1億條,就小了很多,比如qq0,qq1,qq1...qq99表。

使用者登入的時候,可以將使用者的id%100,那麼會得到0-99的數,查詢表的時候,將表名qq跟取模的數連線起來,就構建了表名。比如123456789使用者,取模的89,那麼就到qq89表查詢,查詢的時間將會大大縮短。

這就是水平分割。

 

2,垂直分割:

垂直分割指的是:表的記錄並不多,但是欄位卻很長,表佔用空間很大,檢索表的時候需要執行大量的IO,嚴重降低了效能。這時需要把大的欄位拆分到另一個表,並且該表與原表是一對一的關係。

例如學生答題表tt:有如下欄位:

Id name 分數 題目 回答

其中題目和回答是比較大的欄位,id name 分數比較小。

如果我們只想查詢id為8的學生的分數:select 分數 from tt where id = 8;雖然知識查詢分數,但是題目和回答這兩個大欄位也是要被掃描的,很消耗效能。但是我們只關心分數,並不想查詢題目和回答。這就可以使用垂直分割。我們可以把題目單獨放到一張表中,通過id與tt表建立一對一的關係,同樣將回答單獨放到一張表中。這樣我們插敘tt中的分數的時候就不會掃描題目和回答了。

 

3,其他要點:

1)存放圖片、檔案等大檔案用檔案系統儲存。資料庫只儲存路徑,圖片和檔案存放在檔案系統,甚至單獨存放在一臺伺服器(圖床)。

2)資料引數配置。

最重要的引數就是記憶體,我們主要用的innodb引擎,所以下面兩個引數調的很大:

innodb_additional_mem_pool_size=64M

innodb_buffer_pool_size=1G

對於MyISAM,需要調整key_buffer_size,當然調整引數還是要看狀態,用show status語句可以看到當前狀態,以決定該調整哪些引數。

 

4,合理的硬體資源和作業系統

如果機器的記憶體超過4G,那麼應當採用64位作業系統和64位MySQL。

 

 

案例:

    簡單購物系統暫設涉及如下表:

1.產品表(資料量10w,穩定)

2.訂單表(資料量200w,且有增長趨勢)

3.使用者表 (資料量100w,且有增長趨勢)

以mysql為例講述下水平拆分和垂直拆分,mysql能容忍的數量級在百萬靜態資料可以到千萬

 

垂直拆分:

解決問題:

表與表之間的io競爭

不解決問題:

單表中資料量增長出現的壓力

方案:

把產品表和使用者表放到一個server上

訂單表單獨放到一個server上

 

水平拆分:

解決問題:

單表中資料量增長出現的壓力

不解決問題:

表與表之間的io爭奪

 

方案:

使用者表通過性別拆分為男使用者表和女使用者表

訂單表通過已完成和完成中拆分為已完成訂單和未完成訂單

產品表 未完成訂單放一個server上

已完成訂單表盒男使用者表放一個server上

女使用者表放一個server上
-----------------------------------------------------------------------------
目前很多網際網路系統都存在單表資料量過大的問題,這就降低了查詢速度,影響了客戶體驗。為了提高查詢速度,我們可以優化sql語句,優化表結構和索引,不過對那些百萬級千萬級的資料庫表,即便是優化過後,查詢速度還是滿足不了要求。這時候我們就可以通過分表降低單次查詢資料量,從而提高查詢速度,一般分表的方式有兩種:水平拆分和垂直拆分,兩者各有利弊,適用於不同的情況。

水平拆分 
水平拆分是指資料錶行的拆分,表的行數超過200萬行時,就會變慢,這時可以把一張的表的資料拆成多張表來存放。 
這裡寫圖片描述 
這裡寫圖片描述 
通常情況下,我們使用取模的方式來進行表的拆分;比如一張有400W的使用者表users,為提高其查詢效率我們把其分成4張表users1,users2,users3,users4 
通過用ID取模的方法把資料分散到四張表內Id%4+1 = [1,2,3,4] 
然後查詢,更新,刪除也是通過取模的方法來查詢。

例:QQ的登入表。假設QQ的使用者有100億,如果只有一張表,每個使用者登入的時候資料庫都要從這100億中查詢,會很慢很慢。如果將這一張表分成100份,每張表有1億條,就小了很多,比如qq0,qq1,qq1…qq99表。

使用者登入的時候,可以將使用者的id%100,那麼會得到0-99的數,查詢表的時候,將表名qq跟取模的數連線起來,就構建了表名。比如123456789使用者,取模的89,那麼就到qq89表查詢,查詢的時間將會大大縮短。

另外部分業務邏輯也可以通過地區,年份等欄位來進行歸檔拆分;進行拆分後的表,只能滿足部分查詢的高效查詢需求,這時我們就要在產品策劃上,從介面上約束使用者查詢行為。比如我們是按年來進行歸檔拆分的,這個時候在頁面設計上就約束使用者必須要先選擇年,然後才能進行查詢;在做分析或者統計時,由於是自己人的需求,多點等待其實是沒關係的,並且併發很低,這個時候可以用union把所有表都組合成一張檢視來進行查詢,然後再進行查詢。

水平拆分的優點: 
◆表關聯基本能夠在資料庫端全部完成; 
◆不會存在某些超大型資料量和高負載的表遇到瓶頸的問題; 
◆應用程式端整體架構改動相對較少; 
◆事務處理相對簡單; 
◆只要切分規則能夠定義好,基本上較難遇到擴充套件性限制;

水平切分的缺點: 
◆切分規則相對更為複雜,很難抽象出一個能夠滿足整個資料庫的切分規則; 
◆後期資料的維護難度有所增加,人為手工定位資料更困難; 
◆應用系統各模組耦合度較高,可能會對後面資料的遷移拆分造成一定的困難。

垂直拆分 
垂直拆分是指資料表列的拆分,把一張列比較多的表拆分為多張表。表的記錄並不多,但是欄位卻很長,表佔用空間很大,檢索表的時候需要執行大量的IO,嚴重降低了效能。這時需要把大的欄位拆分到另一個表,並且該表與原表是一對一的關係。 
這裡寫圖片描述 
這裡寫圖片描述

通常我們按以下原則進行垂直拆分: 
1,把不常用的欄位單獨放在一張表;, 
2,把text,blob等大欄位拆分出來放在附表中; 
3,經常組合查詢的列放在一張表中;

例如學生答題表tt:有如下欄位: 
Id name 分數 題目 回答 
其中題目和回答是比較大的欄位,id name 分數比較小。

如果我們只想查詢id為8的學生的分數:select 分數 from tt where id = 8;雖然知識查詢分數,但是題目和回答這兩個大欄位也是要被掃描的,很消耗效能。但是我們只關心分數,並不想查詢題目和回答。這就可以使用垂直分割。我們可以把題目單獨放到一張表中,通過id與tt表建立一對一的關係,同樣將回答單獨放到一張表中。這樣我們插敘tt中的分數的時候就不會掃描題目和回答了。

垂直切分的優點 
◆ 資料庫的拆分簡單明瞭,拆分規則明確; 
◆ 應用程式模組清晰明確,整合容易; 
◆ 資料維護方便易行,容易定位;

垂直切分的缺點 
◆ 部分表關聯無法在資料庫級別完成,需要在程式中完成; 
◆ 對於訪問極其頻繁且資料量超大的表仍然存在效能平靜,不一定能滿足要求; 
◆ 事務處理相對更為複雜; 
◆ 切分達到一定程度之後,擴充套件性會遇到限制; 
◆ 過讀切分可能會帶來系統過渡複雜而難以維護。
-----------------------------------------------------------------------
資料庫垂直拆分 水平拆分
        當我們使用讀寫分離、快取後,資料庫的壓力還是很大的時候,這就需要使用到資料庫拆分了。
        
        資料庫拆分簡單來說,就是指通過某種特定的條件,按照某個維度,將我們存放在同一個資料庫中的資料分散存放到多個數據庫(主機)上面以達到分散單庫(主機)負載的效果。 
 
        切分模式: 垂直(縱向)拆分、水平拆分。
 
垂直拆分
 
        專庫專用
 
        一個數據庫由很多表的構成,每個表對應著不同的業務,垂直切分是指按照業務將表進行分類,分佈到不同的資料庫上面,這樣也就將資料或者說壓力分擔到不同的庫上面,如下圖:
        
優點:
        1. 拆分後業務清晰,拆分規則明確。
        2. 系統之間整合或擴充套件容易。
        3. 資料維護簡單。
 
缺點:
        1. 部分業務表無法join,只能通過介面方式解決,提高了系統複雜度。
        2. 受每種業務不同的限制存在單庫效能瓶頸,不易資料擴充套件跟效能提高。
        3. 事務處理複雜。
 
水平拆分
 
        垂直拆分後遇到單機瓶頸,可以使用水平拆分。相對於垂直拆分的區別是:垂直拆分是把不同的表拆到不同的資料庫中,而水平拆分是把同一個表拆到不同的資料庫中。
 
        相對於垂直拆分,水平拆分不是將表的資料做分類,而是按照某個欄位的某種規則來分散到多個庫之中,每個表中包含一部分資料。簡單來說,我們可以將資料的水平切分理解為是按照資料行的切分,就是將表中 的某些行切分到一個數據庫,而另外的某些行又切分到其他的資料庫中,主要有分表,分庫兩種模式,如圖:
                
 
        
優點:
        1. 不存在單庫大資料,高併發的效能瓶頸。
        2. 對應用透明,應用端改造較少。     
        3. 按照合理拆分規則拆分,join操作基本避免跨庫。
        4. 提高了系統的穩定性跟負載能力。
 
缺點:
        1. 拆分規則難以抽象。
        2. 分片事務一致性難以解決。
        3. 資料多次擴充套件難度跟維護量極大。
        4. 跨庫join效能較差。
 
拆分的處理難點
 
兩張方式共同缺點
 
        1. 引入分散式事務的問題。
        2. 跨節點Join 的問題。
        3. 跨節點合併排序分頁問題。
 
針對資料來源管理,目前主要有兩種思路:
 
        A. 客戶端模式,在每個應用程式模組中配置管理自己需要的一個(或者多個)資料來源,直接訪問各個 資料庫,在模組內完成資料的整合。 
        優點:相對簡單,無效能損耗。   
        缺點:不夠通用,資料庫連線的處理複雜,對業務不夠透明,處理複雜。
 
       B. 通過中間代理層來統一管理所有的資料來源,後端資料庫叢集對前端應用程式透明;   
        優點:通用,對應用透明,改造少。   
        缺點:實現難度大,有二次轉發效能損失。
 
拆分原則
    
        1. 儘量不拆分,架構是進化而來,不是一蹴而就。(SOA)
        2. 最大可能的找到最合適的切分維度。
        3. 由於資料庫中介軟體對資料Join 實現的優劣難以把握,而且實現高效能難度極大,業務讀取  儘量少使用多表Join -儘量通過資料冗餘,分組避免資料垮庫多表join。
        4. 儘量避免分散式事務。
        5. 單表拆分到資料1000萬以內。
 
切分方案
    
        範圍、列舉、時間、取模、雜湊、指定等
 
案例分析
 
場景一
建立一個歷史his系統,將公司的一些歷史個人遊戲資料儲存到這個his系統中,主要是寫入,還有部分查詢,讀寫比約為1:4;由於是所有資料的歷史存取,所以併發要求比較高; 
 
分析:
歷史資料
寫多都少
越近日期查詢越頻繁?
什麼業務資料?使用者遊戲資料
有沒有大規模分析查詢?
資料量多大?
保留多久?
機器資源有多少?
 
方案1:按照日期每月一個分片
帶來的問題:1.資料熱點問題(壓力不均勻)
 
方案2:按照使用者取模,  --by Jerome 就這個比較合適了
帶來的問題:後續擴容困難
 
方案3:按使用者ID範圍分片(1-1000萬=分片1,xxx)
帶來的問題:使用者活躍度無法掌握,可能存在熱點問題
 
場景二
建立一個商城訂單系統,儲存使用者訂單資訊。
 
分析:
電商系統
一號店或京東類?淘寶或天貓?
實時性要求高
存在瞬時壓力
基本不存在大規模分析
資料規模?
機器資源有多少?
維度?商品?使用者?商戶?
 
方案1:按照使用者取模,
帶來的問題:後續擴容困難
 
方案2:按使用者ID範圍分片(1-1000萬=分片1,xxx)
帶來的問題:使用者活躍度無法掌握,可能存在熱點問題
 
方案3:按省份地區或者商戶取模
資料分配不一定均勻
 
場景3
上海公積金,養老金,社保系統
 
分析:
社保系統
實時性要求不高
不存在瞬時壓力
大規模分析?
資料規模大
資料重要不可丟失
偏於查詢?
 
方案1:按照使用者取模,
帶來的問題:後續擴容困難
 
方案2:按使用者ID範圍分片(1-1000萬=分片1,xxx)
帶來的問題:使用者活躍度無法掌握,可能存在熱點問題
 
方案3:按省份區縣地區列舉
資料分配不一定均勻
 
 
 
        資料庫問題解決後,應用面對的新挑戰就是拆分應用等
 
參考
        Mycat線上視訊培訓【連結:http://pan.baidu.com/s/1nuR26rZ 密碼:1gr9 (2015)】
        大型網站系統與Java中介軟體實踐.pdf
        MySQL資料庫的演化可以參考:http://mp.weixin.qq.com/s?__biz=MjM5ODI5Njc2MA==&mid=207666963&idx=2&sn=0d0710e071420c6fc6af8d4a3bc3dfe6&scene=1#rd
        http://mp.weixin.qq.com/s?__biz=MjM5ODYxMDA5OQ==&mid=2651959773&idx=1&sn=7e4ad0dcd050f6662dfaf39d9de36f2c&chksm=bd2d04018a5a8d17b92098b4840aac23982e32d179cdd957e4c55011f6a08f6bd31f9ba5cfee&mpshare=1&scene=23&srcid=1220t4ttl8wZaYHlQRzO0xYB#rd【一分鐘掌握資料庫垂直拆分-沈劍】
 
 
垂直拆分
  垂直拆分就是要把表按模組劃分到不同資料庫表中(當然原則還是不破壞第三正規化),這種拆分在大型網站的演變過程中是很常見的。當一個網站還在很小的時候,只有小量的人來開發和維護,各模組和表都在一起,當網站不斷豐富和壯大的時候,也會變成多個子系統來支撐,這時就有按模組和功能把表劃分出來的需求。其實,相對於垂直切分更進一步的是服務化改造,說得簡單就是要把原來強耦合的系統拆分成多個弱耦合的服務,通過服務間的呼叫來滿足業務需求看,因此表拆出來後要通過服務的形式暴露出去,而不是直接呼叫不同模組的表,淘寶在架構不斷演變過程,最重要的一環就是服務化改造,把使用者、交易、店鋪、寶貝這些核心的概念抽取成獨立的服務,也非常有利於進行區域性的優化和治理,保障核心模組的穩定性
  垂直拆分:單表大資料量依然存在效能瓶頸
  水平拆分
  上面談到垂直切分只是把表按模組劃分到不同資料庫,但沒有解決單表大資料量的問題,而水平切分就是要把一個表按照某種規則把資料劃分到不同表或資料庫裡。例如像計費系統,通過按時間來劃分表就比較合適,因為系統都是處理某一時間段的資料。而像SaaS應用,通過按使用者維度來劃分資料比較合適,因為使用者與使用者之間的隔離的,一般不存在處理多個使用者資料的情況,簡單的按user_id範圍來水平切分
  通俗理解:水平拆分行,行資料拆分到不同表中, 垂直拆分列,表資料拆分到不同表中
  垂直與水平聯合切分
  由上面可知垂直切分能更清晰化模組劃分,區分治理,水平切分能解決大資料量效能瓶頸問題,因此常常就會把兩者結合使用,這在大型網站裡是種常見的策略
  案例:
  以mysql為例,簡單購物系統暫設涉及如下表:
  1.產品表(資料量10w,穩定)
  2.訂單表(資料量200w,且有增長趨勢)
  3.使用者表 (資料量100w,且有增長趨勢)
  以mysql為例講述下水平拆分和垂直拆分,mysql能容忍的數量級在百萬靜態資料可以到千萬
  垂直拆分:
  解決問題:
  表與表之間的io競爭
  不解決問題:
  單表中資料量增長出現的壓力
  方案:
  把產品表和使用者表放到一個server上
  訂單表單獨放到一個server上
  水平拆分:
  解決問題:
  單表中資料量增長出現的壓力
  不解決問題:
  表與表之間的io爭奪
  方案:
  使用者表通過性別拆分為男使用者表和女使用者表
  訂單表通過已完成和完成中拆分為已完成訂單和未完成訂單
  產品表 未完成訂單放一個server上
  已完成訂單表盒男使用者表放一個server上
  女使用者表放一個server上(女的愛購物)
 
 
 
《大型網站系統與Java中介軟體實踐》本書圍繞大型網站和支撐大型網站架構的 Java 中介軟體的實踐展開介紹。從分散式系統的知識切入,讓讀者對分散式系統有基本的瞭解;然後介紹大型網站隨著資料量、訪問量增長而發生的架構變遷;接著講述構建 Java 中介軟體的相關知識;之後的幾章都是根據筆者的經驗來介紹支撐大型網站架構的 Java 中介軟體系統的設計和實踐。本節為大家介紹專庫專用,資料垂直拆分。

AD:
51CTO 網+ 第十二期沙龍:大話資料之美_如何用資料驅動使用者體驗

2.2.7 讀寫分離後,資料庫又遇到瓶頸

通過讀寫分離以及在某些場景用分散式儲存系統替換關係型資料庫的方式,能夠降低主庫的壓力,解決資料儲存方面的問題。不過隨著業務的發展,我們的主庫也會遇到瓶頸。我們的網站演進到現在,交易、商品、使用者的資料還都在一個數據庫中。儘管採取了增加快取、讀寫分離的方式,這個資料庫的壓力還是在繼續增加,因此我們需要去解決這個問題,我們有資料垂直拆分和水平拆分兩種選擇。

2.2.7.1 專庫專用,資料垂直拆分

垂直拆分的意思是把資料庫中不同的業務資料拆分到不同的資料庫中。結合現在的例子,就是把交易、商品、使用者的資料分開,如圖2-20 所示。

 



 

這樣的變化給我們帶來的影響是什麼呢?應用需要配置多個數據源,這就增加了所需的配置,不過帶來的是每個資料庫連線池的隔離。不同業務的資料從原來的一個數據庫中拆分到了多個數據庫中,那麼就需要考慮如何處理原來單機中跨業務的事務。一種辦法是使用分散式事務,其效能要明顯低於之前的單機事務;而另一種辦法就是去掉事務或者不去追求強事務支援,則原來在單庫中可以使用的表關聯的查詢也就需要改變實現了。

對資料進行垂直拆分之後,解決了把所有業務資料放在一個數據庫中的壓力問題。並且也可以根據不同業務的特點進行更多優化。

 

2.2.7.2 垂直拆分後的單機遇到瓶頸,資料水平拆分

與資料垂直拆分對應的還有資料水平拆分。資料水平拆分就是把同一個表的資料拆到兩個資料庫中。產生資料水平拆分的原因是某個業務的資料表的資料量或者更新量達到了單個數據庫的瓶頸,這時就可以把這個表拆到兩個或者多個數據庫中。資料水平拆分與讀寫分離的區別是,讀寫分離解決的是讀壓力大的問題,對於資料量大或者更新量的情況並不起作用。資料水平拆分與資料垂直拆分的區別是,垂直拆分是把不同的表拆到不同的資料庫中,而水平拆分是把同一個表拆到不同的資料庫中。例如,經過垂直拆分後,使用者表與交易表、商品表不在一個數據庫中了,如果資料量或者更新量太大,我們可以進一步把使用者表拆分到兩個資料庫中,它們擁有結構一模一樣的使用者表,而且每個庫中的使用者表都只涵蓋了一部分的使用者,兩個資料庫的使用者合在一起就相當於沒有拆分之前的使用者表。我們先來簡單看一下引入資料水平拆分後的結構,如圖2-21 所示。

我們來分析一下水平拆分後給業務應用帶來的影響。

首先,訪問使用者資訊的應用系統需要解決SQL 路由的問題,因為現在使用者資訊分在了兩個資料庫中,需要在進行資料庫操作時瞭解需要操作的資料在哪裡。

此外,主鍵的處理也會變得不同。原來依賴單個數據庫的一些機制需要變化,例如原來使用Oracle 的Sequence 或者MySQL 表上的自增欄位的,現在不能簡單地繼續使用了。並且在不同的資料庫中也不能直接使用一些資料庫的限制來保證主鍵不重複了。

 



 

最後,由於同一個業務的資料被拆分到了不同的資料庫中,因此一些查詢需要從兩個資料庫中取資料,如果資料量太大而需要分頁,就會比較難處理了。

不過,一旦我們能夠完成資料的水平拆分,我們將能夠很好地應對資料量及寫入量增長的情況。具體如何完成資料水平拆分,在後面分布式資料訪問層的章節中我們將進行更加詳細的介紹。

 

2.2.8 資料庫問題解決後,應用面對的新挑戰

2.2.8.1 拆分應用

前面所講的讀寫分離、分散式儲存、資料垂直拆分和資料水平拆分都是在解決資料方面的問題。下面我們來看看應用方面的變化。

之前解決了應用伺服器從單機到多機的擴充套件,應用就可以在一定範圍內水平擴充套件了。隨著業務的發展,應用的功能會越來越多,應用也會越來越大。我們需要考慮如何不讓應用持續變大,這就需要把應用拆開,從一個應用變為兩個甚至多個應用。我們來看兩種方式。

第一種方式,根據業務的特性把應用拆開。在我們的例子中,主要的業務功能分為三大部分:交易、商品和使用者。我們可以把原來的一個應用拆成分別以交易和商品為主的兩個應用,對於交易和商品都會有涉及使用者的地方,我們讓這兩個系統自己完成涉及使用者的工作,而類似使用者註冊、登入等基礎的使用者工作,可以暫時交給兩系統之一來完成(注意,我們在這裡主要是通過例子說明拆分的做法),如圖2-22 所示,這樣的拆分可以使大的應用變小。

 



 

我們還可以按照使用者註冊、使用者登入、使用者資訊維護等再拆分,使之變成三個系統。不過,這樣拆分後在不同系統中會有一些相似的程式碼,例如使用者相關的程式碼。如何能夠保證這部分程式碼的一致以及如何對其複用是需要解決的問題。此外,按這樣的方式拆分出來的新系統之間一般沒有直接的相互呼叫。而且,新拆出來的應用可能會連線同樣的資料庫。

來看一個具體的例子,如圖2-23 所示。

我們根據業務的不同功能拆分了幾個業務應用,而且這些業務應用之間不存在直接的呼叫,它們都依賴底層的資料庫、快取、檔案系統、搜尋等。這樣的應用拆分確實能夠解決當下的一些問題,不過也有一些缺點。  



 

 

2.2.8.2 走服務化的路

我們再來看一下服務化的做法。圖2-24 是一個示意圖。從中可以看到我們把應用分為了三層,處於最上端的是Web 系統,用於完成不同的業務功能;處於中間的是一些服務中心,不同的服務中心提供不同的業務服務;處於下層的則是業務的資料庫。當然,我們在這個圖中省去了快取等基礎的系統,因此可以說是服務化系統結構的一個簡圖。

 



 

圖2-24 與之前的圖相比有幾個很重要的變化。首先,業務功能之間的訪問不僅是單機內部的方法呼叫了,還引入了遠端的服務呼叫。其次,共享的程式碼不再是散落在不同的應用中了,這些實現被放在了各個服務中心。第三,資料庫的連線也發生了一些變化,我們把與資料庫的互動工作放到了服務中心,讓前端的Web 應用更加註重與瀏覽器互動的工作,而不必過多關注業務邏輯的事情。連線資料庫的任務交給相應的業務服務中心了,這樣可以降低資料庫的連線數。而服務中心不僅把一些可以共用的之前散落在各個業務的程式碼集中了起來,並且能夠使這些程式碼得到更好的維護。第四,通過服務化,無論是前端Web 應用還是服務中心,都可以是由固定小團隊來維護的系統,這樣能夠更好地保持穩定性,並能更好地控制系統本身的發展,況且穩定的服務中心日常釋出的次數也遠小於前端Web 應用,因此這個方式也減小了不穩定的風險。

要做到服務化還需要一些基礎元件的支撐,在後面服務框架的章節我們會具體介紹。

 

 

通過某種特定的條件,將存放在同一個資料庫中的資料分散存放到多個數據庫上,實現分佈儲存,通過路由規則路由訪問特定的資料庫,這樣一來每次訪問面對的就不是單臺伺服器了,而是N臺伺服器,這樣就可以降低單臺機器的負載壓力。提示:sqlserver 2005版本之後,可以友好的支援“表分割槽”。

  垂直(縱向)拆分:是指按功能模組拆分,比如分為訂單庫、商品庫、使用者庫...這種方式多個數據庫之間的表結構不同。

  水平(橫向)拆分:將同一個表的資料進行分塊儲存到不同的資料庫中,這些資料庫中的表結構完全相同。

SQL Server:資料庫/資料表 拆分
▲(縱向拆分)

SQL Server:資料庫/資料表 拆分
▲(橫向拆分)

  1,實現原理:使用垂直拆分,主要要看應用型別是否合適這種拆分方式,如系統可以分為,訂單系統,商品管理系統,使用者管理系統業務系統比較明的,垂直拆分能很好的起到分散資料庫壓力的作用。業務模組不明晰,耦合(表關聯)度比較高的系統不適合使用這種拆分方式。但是垂直拆分方式並不能徹底解決所有壓力問題,例如 有一個5000w的訂單表,操作起來訂單庫的壓力仍然很大,如我們需要在這個表中增加(insert)一條新的資料,insert完畢後,資料庫會針對這張表重新建立索引,5000w行資料建立索引的系統開銷還是不容忽視的,反過來,假如我們將這個表分成100個table呢,從table_001一直到table_100,5000w行資料平均下來,每個子表裡邊就只有50萬行資料,這時候我們向一張只有50w行資料的table中insert資料後建立索引的時間就會呈數量級的下降,極大了提高了DB的執行時效率,提高了DB的併發量,這種拆分就是橫向拆分

  2,實現方法:垂直拆分,拆分方式實現起來比較簡單,根據表名訪問不同的資料庫就可以了。橫向拆分的規則很多,這裡總結前人的幾點,

  (1)順序拆分:如可以按訂單的日前按年份才分,2003年的放在db1中,2004年的db2,以此類推。當然也可以按主鍵標準拆分。

  優點:可部分遷移

  缺點:資料分佈不均,可能2003年的訂單有100W,2008年的有500W。

  (2)hash取模分: 對user_id進行hash(或者如果user_id是數值型的話直接使用user_id的值也可),然後用一個特定的數字,比如應用中需要將一個數據庫切分成4個數據庫的話,我們就用4這個數字對user_id的hash值進行取模運算,也就是user_id%4,這樣的話每次運算就有四種可能:結果為1的時候對應DB1;結果為2的時候對應DB2;結果為3的時候對應DB3;結果為0的時候對應DB4,這樣一來就非常均勻的將資料分配到4個DB中。

  優點:資料分佈均勻

  缺點:資料遷移的時候麻煩;不能按照機器效能分攤資料 。

  (3)在認證庫中儲存資料庫配置

  就是建立一個DB,這個DB單獨儲存user_id到DB的對映關係,每次訪問資料庫的時候都要先查詢一次這個資料庫,以得到具體的DB資訊,然後才能進行我們需要的查詢操作。

  優點:靈活性強,一對一關係

  缺點:每次查詢之前都要多一次查詢,會造成一定的效能損失。

 ------------------------------------------------------------------------------
mysql資料庫的水平拆分與垂直拆分
近端時間在面試,發現很多面試官或者面試都把資料的水平拆分合垂直拆分給搞混了,今天特意寫了一篇部落格來說說水平拆分和垂直拆分希望對程式猿們有所幫助。

資料庫水平與垂直拆分:

垂直(縱向)拆分:是指按功能模組拆分,比如分為訂單庫、商品庫、使用者庫...這種方式多個數據庫之間的表結構不同。

水平(橫向)拆分:將同一個表的資料進行分塊儲存到不同的資料庫中,這些資料庫中的表結構完全相同。

資料表的水平與垂直拆分:

垂直拆分:按欄位功能主次拆分,比如最常見的商品表、商品圖片表、商品詳細資訊...表與表之間的結構不同

水平拆分:同資料庫的水平拆分原理一樣主要是將資料進行拆分儲存到不同的表當中,這些表的結構完全相同。、

 

使用用垂直拆分要主要看系統是否適合這種拆分方式,如系統可分為使用者系統,商品系統、訂單系統等這些業務比較明確的比較適合使用垂直拆分,垂直拆分能很好分散資料庫壓力。業務模組不清晰,模組耦合度較高的系統並不適合垂直拆分。垂直拆分並不能徹底解決所有的壓力問題,例如有一張8000w的訂單表而且訂單隨著時間還在一直增加,操作起這張訂單表壓力依然很大,如我們需要在這個表中增加(insert)一條新的資料,insert完畢後,資料庫會針對這張表重新建立索引,8000w行資料建立索引的系統開銷還是不容忽視的,這類情況就可以使用到水平拆分了,可以將表分成100個table,table_001一直到table_100,8000w資料平均分下來就是80萬的資料(經過實際測試mysql資料量達到400w的時候效能明顯降低,故而應將單個mysql的資料量控制在300W以內),這時候我們向一張只有80w行資料的table中insert資料後建立索引的時間就會呈數量級的下降,極大了提高了DB的執行時效率,提高了DB的併發量,這種拆分就是水平(橫向)拆分

資料庫拆的實現方式:

垂直拆分,拆分方式實現起來比較簡單,根據表名訪問不同的資料庫就可以了這裡不多講。橫向拆分的規則很多,這裡總結了以下幾點:

1、順序拆分:例如訂單表可以按訂單的日期按年份才分,2016年的放在db1中,2017年的db2,以此類推。當然也可以按主鍵標準