資料庫分庫分表和帶來的唯一ID 分頁查詢問題的解決

2021-09-25 11:09:14 字數 3241 閱讀 2342

需求緣起(用乙個公司的發展作為背景)

1.還是個小公司的時候,註冊使用者就20w,每天活躍使用者1w,每天最大單錶資料量就1000,然後高峰期每秒併發請求最多就10,此時乙個16核32g的伺服器,每秒請求支撐在2000左右,負載合理,沒有太大壓力,基本沒有宕機風險。

2.當註冊使用者達到2000w,每天活躍使用者數100w,每天單錶新增資料量達到50w條,高峰期請求量達到1w。經過一段時間的執行,單標資料量會越來越多,帶來的問題

2.1 資料庫伺服器的io,網路寬頻,cpu負載,記憶體消耗都會達到非常高,伺服器已經不堪重負

2.2 高峰時期,單錶資料量本來就很大,加上資料庫伺服器負載太高,導致效能下降,此時sql的效能就更差了,使用者體驗賊差, 點乙個按鈕要很久才有響應,如果伺服器的配置再低一點的話,資料庫可能直接宕機

3. 實現乙個基本的分庫分表的思路,將一台資料庫伺服器變成5臺資料庫,就能有5個庫,5個表,這樣可以將表中的資料按照id分別通過同乙個對映方法,分布到這5個庫中。此時寫入資料的時候,需要借助資料庫中介軟體,比如shardng-jdbc或者mycat。查詢的時候先通過一步對映到具體的資料庫,再進行查詢。

4. 當使用者量再次增長時,只能繼續分表,比如將一張表拆分成1024張表,這樣在運算元據的時候,需要兩次路由,一次找到在哪個資料庫,一次找到在哪張表。

5. 除了分表,資料庫還可以做主從架構,主伺服器用以寫入,從伺服器用以查詢,根據業務需求具體實現即可。

分庫分表帶來的問題

1. 分庫分表之後乙個必然的問題,如何獲取乙個全域性為乙個id?因為表中的資料是通過id路由對映的,id不能重複。

2. 就算有了全域性唯一的id,那面對分頁查詢的需求,應該怎麼處理呢?

唯一id的生成

下面列舉幾種常見的唯一id生成方案,需要滿足兩大核心需求:1.全域性唯一  2趨勢有序

1. 用資料庫的auto_increment(自增id)來生成,每次通過寫入資料庫一條記錄,利用資料庫id自增的特性獲取唯一,有序的id。

優點:使用資料庫原有的功能,相對簡單;能夠保證唯一;能夠保證遞增性;id之間的步長是固定且可以自定義的

缺點:可用性難以保證,當生成id的那台伺服器宕機,系統就玩不轉了;由於寫入是單點的,所以擴充套件性差,效能上限取決於資料庫的寫效能。

2. 用uuid

優點:簡單方便;全球唯一,在遇見資料遷移、合併或者變更時可以從容應對;

缺點:沒有遞增性;uuid是很長的字串,作為主鍵對儲存空間有一定要求,查詢效率也較低。

3. 使用redis生成id,主要利用redis是單執行緒的,所以也可以用來生成唯一id。當使用的是redis集群的時候,比如集群中有5臺redis,初始化每台redis的值為1,2,3,4,5,設定步長為5,並且確定乙個不隨機的負載均衡策略,能夠保證有序,唯一。

優點:不依賴資料庫,靈活,且效能相對於資料庫有一定提高;使用redis集群策略還能排除單點故障問題;id天然有序

缺點:如果系統中沒有redis,還需要引入新的元件;編碼和配置工作量大

4. 使用twitter的snowflake演算法;其核心思想是乙個64位long型id,使用41bit作為毫秒數,10bit作為機器的id(5個bit是資料中心,5個bit的機器id),12bit作為毫秒內的流水號(意味著每個節點在每毫秒可以產生 4096 個 id),最後還有乙個符號位,永遠是0。具體實現的**可以參看可以根據自身需求進行一定的修改。

優點:不依賴資料庫,靈活方便,效能優於資料庫;id按照時間在單機上是遞增的

缺點:單機上遞增,但是當分布式環境下每台機器的時鐘不可能完全同步,有時並不能做做全域性遞增。

5. 使用zookeeper生成唯一id,主要通過znode資料版本來生成序列號,可以生成32為和64為的資料版本號。很少使用,因為是多步調用api,併發情況下還需要考慮分布式鎖,不是很理想。

6. mongodb的objectid,和snowflake演算法類似。4位元組unix時間戳,3位元組機器編碼,2位元組程序編碼,3位元組隨機數

分庫分表下的分頁查詢

假設有一張使用者表,經過分庫分表之後,現在均勻分布在2臺伺服器例項上。業務需要查詢「最近註冊的第3頁使用者」,雖然資料庫有分庫用的全域性的id,但是沒有排序條件time的全域性視野,此時應該怎麼做呢?

1. 全域性視野法:因為不清楚按照時間排序之後的第三頁資料到底是如何分布在資料庫上的,所以必須每個庫都返回3頁資料,所得到的6頁資料在服務層進行記憶體排序,得到全域性視野,再取第3頁資料。

優點:通過服務層修改,擴大資料查詢量,得到全域性視野,業務無損,精確

缺點(顯而易見):每個分庫都需要返回更多的資料,增大網路傳輸量;除了資料庫要按照time排序,服務層也需要二次排序,損耗效能;隨著頁碼的增大,效能極具下降,資料量和排序量都將大增,效能平方級下降。

2. 業務折中

2.2.2這樣服務層還是獲得兩頁資料,再做一次排序,獲取一頁資料。

2.2.3改進了不會因為頁碼增大而導致資料的傳輸量和排序量增大

3. 允許資料精度丟失:需要考慮業務員上是否接受在頁碼較大是返回的資料不是精準的資料。

3.1在資料量較大,且id對映分布足夠隨機的話,應該是滿足等概率分布的情況的,所以取一頁資料,我們在每個資料庫中取前半頁。

3.2當然這樣的到的結果並不是精準的,但是當實際業務可以接受的話, 此時的技術方案的複雜度變大大降低。也不需要服務層記憶體排序了。

4. 二次查詢法:既滿足業務的精確需求,也無需業務折中。現在假設每頁顯示10條資料,要查第三頁,資料分了兩個庫。 正常的語句是 select * from table order by time offset 20 limit 10,取偏移20個之後的10個

4.1首次查詢查詢每個庫的select * from table order by time offset 10 limit 10;得到10條資料。這裡的offset是總offset/分庫數

4.2 服務層得到來自兩個分庫的結果集,得到最小的time,也就是最頂層的time,這個time滿足最少有10條記錄在它前面,然後分別記錄每個庫的最大time

4.3 分別再次查詢最小time->每個庫上一次的最大time的資料,得到每個庫的查詢結果

4.4 在每個集合的最小time都是相同的,所以可以得到該最小time在整個資料庫中的offset,加起來就是這個最小time在全域性庫的offset位置。

4.5 再將第二次查詢的結果集拼起來和得到的最小time的offset,推導出 offset 20 limit 10的一頁記錄。

優點:可以精確得到業務資料,且每次返回的資料量都非常小,不會隨著頁碼增加而資料量增大。

缺點:需要進行兩次資料庫查詢

資料庫分庫分表

1 基本思想之什麼是分庫分表?從字面上簡單理解,就是把原本儲存於乙個庫的資料分塊儲存到多個庫上,把原本儲存於乙個表的資料分塊儲存到多個表上。2 基本思想之為什麼要分庫分表?資料庫中的資料量不一定是可控的,在未進行分庫分表的情況下,隨著時間和業務的發展,庫中的表會越來越多,表中的資料量也會越來越大,相...

資料庫分庫 分表

分庫的優點是 實現簡單,庫與庫之間界限分明,便於維護,缺點是不利於頻繁跨庫操作,單錶資料量大的問題解決不了。分表的優點是 能解決分庫的不足點,但是缺點卻恰恰是分庫的優點,分表實現起來比較複雜,特別是分表規則的劃分,程式的編寫,以及後期的 資料庫拆分移植維護。實際應用中,一般網際網路企業的路線都是先分...

資料庫分庫分表

簡單了解資料庫分庫分表,以及資料庫的分片 什麼是分庫分表 原本儲存於乙個庫的資料分塊儲存到多個庫上,把原本儲存於乙個表的資料分塊儲存在到多個表上 為什麼分庫分表 當一張表的資料達到幾千萬時,你查詢一次所花的時間會變多,如果有聯合查詢的花,我想啃根會死在那。分表的目的就在於此,減少資料庫的負擔,縮短查...