之前有被問到過數據庫億萬級數據的優化問題,分表和分庫是其中的一個重要知識點。
分表的概念與策略
對於大型的互聯網應用來說,數據庫單表的記錄行數可能達到千萬級甚至是億級,並且數據庫面臨着極高的並發訪問。采用主從復制(Master-Slave)模式的MySQL架構,只能夠對數據庫的讀進行擴展,而對數據庫的寫入操作還是集中在Master上,並且單個Master掛載的Slave也不可能無限制多,Slave的數量受到Master能力和負載的限制。因此就需要對數據庫的吞吐能力進一步擴展,以滿足高並發訪問與海量數據存儲的需要。
對於訪問極為頻繁且數據量巨大的單表(百萬到千萬級別)來說,我們首先要做的就是減少單表的記錄條數,以便減少數據查詢所需要的時間,提高數據庫的吞吐,這就是分表的概念。在分表之前,首先需要選擇適當的分表策略,使得數據能夠較為均衡地分布到多張表中,且不能影響正常的查詢。
對於互聯網企業來說,大部分數據都是與用戶關聯的,因此,用戶id是最常用的分表字段。因為大部分查詢都需要帶上用戶ID,這樣的分表策略既不會影響正常查詢,又能夠使數據較為均衡地分布到各個表中(有些場景可能會出現冷熱數據分配不均衡的情況)。
假設有一個用來記錄用戶購買信息的訂單表(ORDER),由於ORDER表中的記錄條數太多,需要被拆分為256張表(拆分表的數量一般是2的N次方),拆分的規則是根據USER_ID%256取得對應的表存儲記錄。而前台應用則能根據USER_ID%256的規則去找到對應訂單存儲的表,再去存儲該記錄的表中取出數據(余數為0,則查0號表,余數為233,則查233號表)。這樣以來,USER_ID便變成了一個必須的查詢條件,否則將會因為無法定位數據存儲的表而無法對數據進行訪問。
這時,如果要訪問USER_ID是257的訂單記錄,則根據USER_ID%256的規則得到要訪問的表是ORDER_1。這里可能會有個疑問,就是如果257這個用戶特別有錢,下了一億個訂單,ORDER_1表豈不是又數據量過大了。當然這種情況現實是不可能出現的,但是這個疑問可以推出一個觀點,就是說具體的分表策略是要根據實際情況來制定的。
分庫的概念與策略
分表能夠解決單表數據量過大帶來的查詢效率下降的問題,但是卻無法給數據庫的並發處理能力帶來質的提升。面對高並發的讀寫訪問,當數據庫Master服務器無法承載寫操作壓力時,不管如何擴展Slave服務器,都沒有什么意義了。因此必須換一種思路:既然拆分多個表不行了,那就拆分多個數據庫好了,聚集多個單數據庫的寫入能力提高整體的寫入能力,這就是分庫的概念。
與分表策略類似,分庫也可以采用通過一個關鍵字取模的方式來對數據訪問進行路由。比如還是之前的訂單表,假設USER_ID字段的值是258,將原有的單庫分為256個庫,那么應用程序對數據庫的訪問請求將被路由到第二個庫(258%256 = 2)。
分庫分表的概念與策略
大多數時候,數據庫是會同時面臨高並發訪問的壓力和海量數據的存儲問題的。這時候就需要采用分表策略,又要采用分庫策略,以便能既擴展系統的並發處理能力,又提升單表的查詢性能。這種分庫和分表聯合使用的方式,就是分庫分表的概念。
分庫分表的策略比起僅分庫或僅分表的策略要更為復雜,一種分庫分表的路由策略如下:
1.中間變量=USER_ID%(分庫數量*每個庫的表數量)
2.庫=取整數(中間變量/每個庫的表數量)
3.表=中間變量%每個庫的表數量
同樣是訂單表,同樣采用用戶ID作為路由字段,首先使用USER_ID對庫數量*每個庫表的數量取模,得到一個中間變量;然后使用中間變量除以每個庫表的數量,取整,便得到對應的庫;而中間變量對每個庫表的數量取模,即得到對應的表。
假設將原來的單庫單表ORDER表拆分為256個庫,每個庫包含1024個表,那么按照前面所提到的路由策略,對於USER_ID=262145的訪問,路由的計算過程如下:
1.中間變量=262145%(256*1024)=1
2.庫=(1/1024)取整=0
3.表=1%1024=1
因此得出,對於USER_ID=262145的訂單記錄的查詢和修改,將會被路由到第0個庫的第1個ORDER_1表中執行。
"我懂得了那么多道理,卻仍然過不好這一生。"
