數據庫分庫分表存在的問題及解決方案


讀寫分離分散了數據庫讀寫操作的壓力,但是沒有分散存儲壓力,當數據庫的數據量達到千萬甚至上億條的時候,單台數據庫服務器的存儲能力就會達到瓶頸,主要體現在以下幾個方面:

  1. 數據量太大,讀寫性能會下降,即使有索引,索引也會變得很大,性能同樣會下降
  2. 數據文件會變得很大,數據庫備份和恢復需要消耗更長的時間
  3. 數據文件越大,極端情況下丟失數據的風險就會越高

基於上述原因,單個數據庫服務器存儲的數據量不能太大,需要控制在一定的范圍內,為了滿足業務數據存儲的需求,需要將存儲分散到多台數據庫服務器上

常見的分散存儲的方法有分庫和分布兩大類

  • 業務分庫  業務分庫之的是按照業務模塊將數據分散到不同的數據庫服務器,雖然業務分庫能夠分散存儲和訪問的壓力,但是同時也帶來了新的問題,主要存在的問題如下:
  1. join操作問題     業務分庫后,原本在同一個數據庫中的表分散到不同數據庫中,導致無法使用SQL中的join查詢
  2. 事務問題           原本在同一個數據庫中不同的表可以在同一個事物中修改,業務分庫后,表分散到不同的數據庫中,無法通過事務統一修改,雖然數據庫廠商針對此問題提供了一些分布式事務解決方案(例如,MySQL的XA),但是性能實在太低,與高性功能存儲的目標是相違背的
  3. 成本問題          業務分庫同時也帶來了成本的代價,本來1台服務器搞定的事情,現在需要3台,如果考慮備份,那就是2台變成了6台

基於上述原因,對於初創業務,並不建議一開始就這樣拆分,主要有幾個原因:

  1. 初創業務存在很大的不確定性,業務不一定能發展起來,業務開始的時候並沒有真正的存儲和訪問壓力,業務分庫並不能為業務帶來價值
  2. 業務分庫后,表之間的join查詢,數據庫事務無法簡單實現了發
  3. 業務分庫后,因為不同的數據要讀寫不同的數據庫,代碼需要增加根據數據類型映射到不同數據庫的邏輯,增加了工作量,而業務初創期最重要的是快速實現,快速驗證,業務分庫會拖慢業務節奏
  • 分表  將不同的業務數據分散存儲到不同的數據庫服務器,能夠支撐百萬甚至千萬用戶規模的業務,但是如果業務繼續發展,同一個業務的單表數據也會達到單台數據庫服務器的處理瓶頸,此時就需要對單表進行拆分,單表數據拆分有兩種方式:垂直分表和水平分表

分表能夠有效的分散存儲壓力和帶來性能提升,但是和分庫一樣,也會引入各種復雜性,主要存在的問題如下:

  1. 垂直分表  垂直分表適合將表中某些不常用而且占了大量空間的列拆分出去,垂直分表的引入的復雜性主要體現在表操作的數量會增加,例如原來只要一次查詢的就可以獲取,現在要查詢兩次或者多次才能獲得想要的數據
  2. 水平分表  水平分表適合表行數特別大的表,如果單表行數超過5000萬就必須進行分表,這個數字可以作為參考,但是並不是絕對的標准,關鍵還是要看表的訪問性能

水平分表相比垂直分表,會引入更多的復雜性,主要表現在以下幾個方面:

  • 路由   水平分表后,某條數據具體屬於哪個切分后的表,需要增加路由算法進行計算,這個算法會引入一定的復雜性,常見的路由算法有如下幾種:
  1. 范圍路由   選擇有序的數據列作為路由條件,不同分段分散到不同的數據庫表中,以常見的用戶ID為例,路由算法可以按照10000的范圍大小進行分段 1-9999放到數據庫1中的表,10000-19999的數據放到數據庫2中的表,依次類推,范圍路由算法的復雜性主要體現在分段大小的選取上,分段太小會導致切分后的子表數據量過多,增加維護復雜度;分段太大可能會導致單表依然存在性能問題,一般建議分段大學在100萬到200萬之間,具體要根據業務選擇合適的大小分段,路由算法的優點就是可以隨着數據的增加可以平滑的擴充新的表,原有的數據不需要懂,范圍路由的一個比較隱含的缺點就是分布不均勻 
  2. Hahs路由算法  選擇某個列(或者某幾個列組合也可以)的進行Hash運算,然后根據Hash結果分散到不同的數據庫表中,同樣根據用戶ID為例,假如一開始就規划10個數據庫表,路由算法可以簡單的用user_id%10的值來表示數據所屬的數據庫表編號,ID為985的用戶放到編號為5的子表中,ID為10086的用戶放到編號為6的子表中;Hash 路由算法設計的復雜點主要體現在初始表數量的選取上,表數量太多維護比較麻煩,表數據量太少又可能導致單表性能問題,而用了Hash路由后,增加表的數量非常麻煩,所有數據都要重新分布,Hash路由算法的優缺點和范圍路由基本相反,Hash路由算法的優點是表分布比較均勻,缺點是擴充新的表很麻煩,所有數據需要重新分布
  3. 配置路由  配置路由就是路由表,用一張獨立的表來記錄路由信息,同樣根據用戶ID為例,我們新增一張user_router表,這個表包含user_id和table_id兩列,根據user_id就可以查詢對應的table_id,配置路由設計簡單,使用起來非常靈活,尤其是在擴充表的時候,只需要遷移指定書,然后修改路由表就可以。配置路由的缺點就是必須多查詢一次,會影響整體的性能;而且路由表本身如果太大,性能同樣可能成為瓶頸,如果我們再次將路由表分庫分表,則面臨一個死循環式的路由算法選擇問題

分表操作和分庫操作一樣,同樣會存在一些問題,主要體現在如下幾個方面:

  1. join操作           水平分表后,數據分散到多個表中,如果需要與其他表進行join 查詢,需要在業務代碼或者數據庫中間件中進行多次join查詢,然后將結果合並
  2. count()操作     水平分表后,雖然物理上數據分散到多個表中,但是某些業務邏輯上還是會將這些表當作一個表進行處理,例如,獲取記錄總數用於分頁或展示,水平分表之前用一個count()就能完成的操作,在分表之后就沒有那么簡單了,常見的處理方式有如下兩種:
  • count()相加   具體做法就是在業務代碼或者數據庫中間件中對每個表進行count()操作,然后將結果相加,這種方式實現簡單,缺點就是性能比較低
  • 記錄數表       具體做法就是新建一張表,例如表名為:記錄數表,包含table_name,row_count兩個字段,每次插入或刪除子表數據成功后,都更新記錄數表,這種方式獲取表記錄數的性能要大大優於count()相加方式,因為只需要一次簡單的查詢就可以獲得數據,缺點是復雜度增加不少,對子表的操作要同步操作記錄數表,如果一個業務邏輯遺漏了,數據就會不一致;而且針對記錄數表的操作和針對子表的操作無法放在同一個事物中進行處理,異常的情況會出現操作子表成功了而操作記錄數表示不,同樣導致數據不一致,同時,記錄數表的方式也增加了數據庫的寫壓力,因為每次針對子表的insert 和 delete操作需要update記錄數表,所以對於一些不要去記錄數實時保持精確的業務,也可以通過后台定時更新記錄數表,定時更新實際上就是count()相加和記錄數表的結合,定時通過count()相加計算表的記錄數,然后更新記錄數表中的數據

    3 order by 操作     水平分表后,數據分散到多個子表中,排序操作無法在數據庫中完成,只能由業務代碼或數據庫中間件分表查詢美國子表中的數據,然后匯總進行排序


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM