数据库拆分过程及挑战
來自:http://www.tianshouzhi.com/api/tutorials/dragon/362
互聯網當下的數據庫拆分過程基本遵循的順序是:垂直拆分、讀寫分離、分庫分表(水平拆分)。每個拆分過程都能解決業務上的一些問題,但同時也面臨了一些挑戰。
1 垂直拆分
對于一個剛上線的互聯網項目來說,由于前期活躍用戶數量并不多,并發量也相對較小,所以此時企業一般都會選擇將所有數據存放在一個數據庫 中進行訪問操作。
舉例來說,對于一個電商系統,其用戶模塊和產品模塊的表剛開始都是位于一個db_eshop庫中。
其中:user表和user_account表屬于用戶模塊,product_category表和product表屬于產品模塊
剛開始,可能公司的技術團隊規模比較小,因此整個技術團隊共同維護db_eshop庫。隨著公司業務的發展,技術團隊人員也得到了擴張,劃分為不同的技術小組,不同的小組負責不同的業務模塊。例如A小組負責用戶模塊,B小組負責產品模塊。此時數據庫也迎來了第一次拆分:垂直拆分。
這里的垂直拆分,指的是將一個包含了很多表的數據庫,根據表的功能的不同,拆分為多個小的數據庫,每個庫包含部分表。下圖演示將上面提到的db_eshop庫,拆分為db_user庫和db_product庫。
關于垂直拆分,還有另一種說法,將一個包含了很多字段的大表拆分為多個小表,每個表包含部分字段。而筆者認為,根據表功能的不同的對數據庫進行拆分,這種情況更加常見。
2 讀寫分離
隨著后續的市場推廣力度不斷加強,用戶數量和并發量不斷上升。這時如果僅靠一個數據庫來支撐所有訪問壓力,幾乎是在 自尋死路 。以產品庫為例,可能庫中包含了幾萬種商品,并且每天新增幾十種,而產品庫每天的訪問了可能有幾億甚至幾十億次。數據庫讀的壓力太大,單臺mysql實例扛不住,此時大部分 Mysql DBA 就會將數據庫設置成 讀寫分離狀態 ,也就是一個 Master 節點(主庫)對應多個 Salve 節點(從庫)。可以將slave節點的數據理解為master節點數據的全量備份。
master節點只有一個且可讀可寫,slave節點有多個且只可以讀。新增產品時,應用將數據寫入master主庫,主庫將數據同步給多個slave從庫。當查詢產品時,應用選擇某個salve節點讀取數據。
讀寫分離的優點:
? 這樣通過配置多個slave節點,可以有效的避免過大的訪問量對單個庫造成的壓力。
讀寫分離的挑戰:
1、對于DBA而言,需要配置數據庫主從同步
? 關于如何配置數據庫的主從同步,這個目前方案已經很成熟。以mysql為例:
? 可以參考官方文檔:https://dev.mysql.com/doc/refman/5.7/en/replication.html,
? 筆者也寫了一篇文章介紹如何通過mysql_multi的方式配置主從同步:http://www.tianshouzhi.com/api/tutorials/mysql。
2、對于開發人員而言,必須要對sql類型進行判斷,如果是select等讀請求,就走從庫,如果是insert、update、delete等寫請求,就走主庫。此外還有一些其他的問題要考慮:
- **主從數據同步延遲問題:**因為數據是從master節點通過網絡同步給多個slave節點,因此必然存在延遲。因此有可能出現我們在master節點中已經插入了數據,但是從slave節點卻讀取不到的問題。對于一些強一致性的業務場景,要求插入后必須能讀取到,因此對于這種情況,我們需要提供一種方式,讓讀請求也可以走主庫,而主庫上的數據必然是最新的。
- **事務問題:**如果一個事務中同時包含了讀請求(如select)和寫請求(如insert),如果讀請求走從庫,寫請求走主庫,由于跨了多個庫,那么jdbc本地事務已經無法控制,屬于分布式事務的范疇。而分布式事務非常復雜且效率較低。因此對于讀寫分離,目前主流的做法是,事務中的所有sql統一都走主庫,由于只涉及到一個庫,jdbc本地事務就可以搞定。
- **高可用的考慮:**例如master配置了多個slave節點,如果其中某個slave節點掛了,那么之后的讀請求,我們應用將其轉發到正常工作的slave節點上。另外,如果新增了slave節點,應用也應該感知到,可以將讀請求轉發到新的slave節點上。
3 分庫分表
? 經過垂直分區后的 Master/Salve 模式完全可以承受住難以想象的高并發訪問操作,但是否可以永遠 高枕無憂 了?答案是否定的,一旦業務表中的數據量大了,從維護和性能角度來看,無論是任何的 CRUD 操作,對于數據庫而言都是一件極其耗費資源的事情。即便設置了索引, 仍然無法掩蓋因為數據量過大從而導致的數據庫性能下降的事實 ,因此這個時候 Mysql DBA 或許就該對數據庫進行 水平分區 (sharding,即分庫分表 )。經過水平分區設置后的業務表,必然能夠將原本一張表維護的海量數據分配給 N 個子表進行存儲和維護。
水平分表從具體實現上又可以分為3種:只分表、只分庫、分庫分表,下圖展示了這三種情況:
只分表:
? 將db庫中的user表拆分為2個分表,user_0和user_1,這兩個表還位于同一個庫中。 適用場景:如果庫中的多個表中只有某張表或者少數表數據量過大,那么只需要針對這些表進行拆分,其他表保持不變。
只分庫:
? 將db庫拆分為db_0和db_1兩個庫,同時在db_0和db_1庫中各自新建一個user表,db_0.user表和db_1.user表中各自只存原來的db.user表中的部分數據。
分庫分表:
? 將db庫拆分為db_0和db_1兩個庫,db_0中包含user_0、user_1兩個分表,db_1中包含user_2、user_3兩個分表。下圖演示了在分庫分表的情況下,數據是如何拆分的:假設db庫的user表中原來有4000W條數據,現在將db庫拆分為2個分庫db_0和db_1,user表拆分為user_0、user_1、user_2、user_3四個分表,每個分表存儲1000W條數據。
分庫的好處:
? 降低單臺機器的負載壓力
分表的好處:
? 提高數據操作的效率。舉個例子說明,比如user表中現在有4000w條數據,此時我們需要在這個表中增加(insert)一條新的數據,insert完畢后,數據庫會針對這張表重新建立索引,4000w行數據建立索引的系統開銷還是不容忽視的。但是反過來,假如我們將這個表分成4 個table呢,從user_0一直到user_3,4000w行數據平均下來,每個子表里邊就只有1000W行數據,這時候我們向一張 只有1000W行數據的table中insert數據后建立索引的時間就會下降,從而提高DB的運行時效率,提高了DB的并發量。當然分表的好處還不知這些,還有諸如寫操作的鎖操作等,都會帶來很多顯然的好處。
分庫分表的挑戰主要體現在4個方面:基本的數據庫增刪改功能,分布式id,分布式事務,動態擴容,下面逐一進行講述。
挑戰1:基本的數據庫增刪改功能
對于開發人員而言,雖然分庫分表的,但是其還是希望能和單庫單表那樣的去操作數據庫。例如我們要批量插入四條用戶記錄,并且希望根據用戶的id字段,確定這條記錄插入哪個庫的哪張表。例如1號記錄插入user_1表,2號記錄插入user_2表,3號記錄插入user_3表,4號記錄插入user_0表,以此類推。sql如下所示:
insert into user(id,name) values (1,”tianshouzhi”),(2,”huhuamin”), (3,”wanghanao”),(4,”luyang”)這樣的sql明顯是無法執行的,因為我們已經對庫和表進行了拆分,這種sql語法只能操作mysql的單個庫和單個表。所以必須將sql改成4條如下所示,然后分別到每個庫上去執行。
insert into user_1(id,name) values (1,”tianshouzhi”)insert into user_2(id,name) values (2,”huhuamin”)insert into user_3(id,name) values (3,”wanghanao”)insert into user_0(id,name) values (4,”luyang”)具體流程可以用下圖進行描述:
解釋如下:
? sql解析:首先對sql進行解析,得到需要插入的四條記錄的id字段的值分別為1,2,3,4
? sql路由:sql路由包括庫路由和表路由。庫路由用于確定這條記錄應該插入哪個庫,表路由用于確定這條記錄應該插入哪個表。
? sql改寫:因為一條記錄只能插入到一個庫中,而上述批量插入的語法將會在 每個庫中都插入四條記錄,明顯是不合適的,因此需要對sql進行改寫,每個庫只插入一條記錄。
? sql執行:一條sql經過改寫后變成了多條sql,為了提升效率應該并發的到不同的庫上去執行,而不是按照順序逐一執行
? 結果集合并:每個sql執行之后,都會有一個執行結果,我們需要對分庫分表的結果集進行合并,從而得到一個完整的結果。
挑戰2:分布式id
? 在分庫分表后,我們不能再使用mysql的自增主鍵。因為在插入記錄的時候,不同的庫生成的記錄的自增id可能會出現沖突。因此需要有一個全局的id生成器。目前分布式id有很多中方案,其中一個比較輕量級的方案是twitter的snowflake算法。
挑戰3:分布式事務
? 分布式事務是分庫分表繞不過去的一個坎,因此涉及到了同時更新多個數據庫。例如上面的批量插入記錄到四個不同的庫,如何保證要么同時成功,要么同時失敗。關于分布式事務,mysql支持XA事務,但是效率較低。柔性事務是目前比較主流的方案,柔性事務包括:最大努力通知型、可靠消息最終一致性方案以及TCC兩階段提交。但是無論XA事務還是柔性事務,實現起來都是非常復雜的。
挑戰4:動態擴容
? 動態擴容指的是增加分庫分表的數量。例如原來的user表拆分到2個庫的四張表上。現在我們希望將分庫的數量變為4個,分表的數量變為8個。這種情況下一般要伴隨著數據遷移。例如在4張表的情況下,id為7的記錄,7%4=3,因此這條記錄位于user_3這張表上。但是現在分表的數量變為了8個,而7%8=0,而user_0這張表上根本就沒有id=7的這條記錄,因此如果不進行數據遷移的話,就會出現記錄找不到的情況。本教程后面將會介紹一種在動態擴容時不需要進行數據遷移的方案。
4、總結
? 在上面我們已經看到了,讀寫分離和分庫分表帶來的好處,但是也面臨了極大的挑戰。如果由業務開發人員來完成這些工作,難度比較大。因此就有一些公司專門來做一些數據庫中間件,對業務開發人員屏蔽底層的繁瑣細節,開發人員使用了這些中間件后,不論是讀寫分離還是分庫分表,都可以像操作單庫單表那樣去操作。
總結
以上是生活随笔為你收集整理的数据库拆分过程及挑战的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HashMap和Hashtable
- 下一篇: Aurora的注册