我們為什麼要分庫分表?

2021-03-02 腳本之家

在文章開頭先拋幾個問題:

(1)什麼時候才需要分庫分表呢?我們的評判標準是什麼?

(2)一張表存儲了多少數據的時候,才需要考慮分庫分表?

(3)數據增長速度很快,每天產生多少數據,才需要考慮做分庫分表?

這些問題你都搞清楚了嗎?相信看完這篇文章會有答案。

為什麼要分庫分表?

首先回答一下為什麼要分庫分表,答案很簡單:資料庫出現性能瓶頸。用大白話來說就是資料庫快扛不住了。

資料庫出現性能瓶頸,對外表現有幾個方面:

大量請求阻塞

在高並發場景下,大量請求都需要操作資料庫,導致連接數不夠了,請求處於阻塞狀態。

SQL 操作變慢

如果資料庫中存在一張上億數據量的表,一條 SQL 沒有命中索引會全表掃描,這個查詢耗時會非常久。

存儲出現問題

業務量劇增,單庫數據量越來越大,給存儲造成巨大壓力。

從機器的角度看,性能瓶頸無非就是CPU、內存、磁碟、網絡這些,要解決性能瓶頸最簡單粗暴的辦法就是提升機器性能,但是通過這種方法成本和收益投入比往往又太高了,不划算,所以重點還是要從軟體角度入手。

資料庫相關優化方案

資料庫優化方案很多,主要分為兩大類:軟體層面、硬體層面。

軟體層面包括:SQL 調優、表結構優化、讀寫分離、資料庫集群、分庫分表等;

硬體層面主要是增加機器性能。

SQL 調優

SQL 調優往往是解決資料庫問題的第一步,往往投入少部分精力就能獲得較大的收益。

SQL 調優主要目的是儘可能的讓那些慢 SQL 變快,手段其實也很簡單就是讓 SQL 執行儘量命中索引。

開啟慢 SQL 記錄

如果你使用的是 Mysql,需要在 Mysql 配置文件中配置幾個參數即可。

slow_query_log=on
long_query_time=1
slow_query_log_file=/path/to/log

調優的工具

常常會用到 explain 這個命令來查看 SQL 語句的執行計劃,通過觀察執行結果很容易就知道該 SQL 語句是不是全表掃描、有沒有命中索引。

select id, age, gender from  user where name = '愛笑的架構師';

返回有一列叫「type」,常見取值有:

ALL、index、range、 ref、eq_ref、const、system、NULL(從左到右,性能從差到好)

ALL 代表這條 SQL 語句全表掃描了,需要優化。一般來說需要達到range 級別及以上。

表結構優化

以一個場景舉例說明:

「user」表中有 user_id、nickname 等欄位,「order」表中有order_id、user_id等欄位,如果想拿到用戶暱稱怎麼辦?一般情況是通過 join 關聯表操作,在查詢訂單表時關聯查詢用戶表,從而獲取導用戶暱稱。

但是隨著業務量增加,訂單表和用戶表肯定也是暴增,這時候通過兩個表關聯數據就比較費力了,為了取一個暱稱欄位而不得不關聯查詢幾十上百萬的用戶表,其速度可想而知。

這個時候可以嘗試將 nickname 這個欄位加到 order 表中(order_id、user_id、nickname),這種做法通常叫做資料庫表冗餘欄位。這樣做的好處展示訂單列表時不需要再關聯查詢用戶表了。

冗餘欄位的做法也有一個弊端,如果這個欄位更新會同時涉及到多個表的更新,因此在選擇冗餘欄位時要儘量選擇不經常更新的欄位。

架構優化

當單臺資料庫實例扛不住,我們可以增加實例組成集群對外服務。

當發現讀請求明顯多於寫請求時,我們可以讓主實例負責寫,從實例對外提供讀的能力;

如果讀實例壓力依然很大,可以在資料庫前面加入緩存如 redis,讓請求優先從緩存取數據減少資料庫訪問。

緩存分擔了部分壓力後,資料庫依然是瓶頸,這個時候就可以考慮分庫分表的方案了,後面會詳細介紹。

硬體優化

硬體成本非常高,一般來說不可能遇到資料庫性能瓶頸就去升級硬體。

在前期業務量比較小的時候,升級硬體資料庫性能可以得到較大提升;但是在後期,升級硬體得到的收益就不那麼明顯了。

分庫分表詳解

下面我們以一個商城系統為例逐步講解資料庫是如何一步步演進。

單應用單資料庫

在早期創業階段想做一個商城系統,基本就是一個系統包含多個基礎功能模塊,最後打包成一個 war 包部署,這就是典型的單體架構應用。

商城項目使用單資料庫

如上圖,商城系統包括主頁 Portal 模板、用戶模塊、訂單模塊、庫存模塊等,所有的模塊都共有一個資料庫,通常資料庫中有非常多的表。

因為用戶量不大,這樣的架構在早期完全適用,開發者可以拿著 demo到處找(騙)投資人。

一旦拿到投資人的錢,業務就要開始大規模推廣,同時系統架構也要匹配業務的快速發展。

多應用單資料庫

在前期為了搶佔市場,這一套系統不停地迭代更新,代碼量越來越大,架構也變得越來越臃腫,現在隨著系統訪問壓力逐漸增加,系統拆分就勢在必行了。

為了保證業務平滑,系統架構重構也是分了幾個階段進行。

第一個階段將商城系統單體架構按照功能模塊拆分為子服務,比如:Portal 服務、用戶服務、訂單服務、庫存服務等。

多應用單資料庫

如上圖,多個服務共享一個資料庫,這樣做的目的是底層資料庫訪問邏輯可以不用動,將影響降到最低。

多應用多資料庫

隨著業務推廣力度加大,資料庫終於成為了瓶頸,這個時候多個服務共享一個資料庫基本不可行了。我們需要將每個服務相關的表拆出來單獨建立一個資料庫,這其實就是「分庫」了。

單資料庫的能夠支撐的並發量是有限的,拆成多個庫可以使服務間不用競爭,提升服務的性能。

多應用多資料庫

如上圖,從一個大的數據中分出多個小的資料庫,每個服務都對應一個資料庫,這就是系統發展到一定階段必要要做的「分庫」操作。

現在非常火的微服務架構也是一樣的,如果只拆分應用不拆分資料庫,不能解決根本問題,整個系統也很容易達到瓶頸。

分表

說完了分庫,那什麼時候分表呢?

如果系統處於高速發展階段,拿商城系統來說,一天下單量可能幾十萬,那資料庫中的訂單表增長就特別快,增長到一定階段資料庫查詢效率就會出現明顯下降。

因此,當單表數據增量過快,業界流傳是超過500萬的數據量就要考慮分表了。當然500萬隻是一個經驗值,大家可以根據實際情況做出決策。

那如何分表呢?

分表有幾個維度,一是水平切分和垂直切分,二是單庫內分表和多庫內分表。

水平拆分和垂直拆分

就拿用戶表(user)來說,表中有7個欄位:id,name,age,sex,nickname,description,如果 nickname 和 description 不常用,我們可以將其拆分為另外一張表:用戶詳細信息表,這樣就由一張用戶表拆分為了用戶基本信息表+用戶詳細信息表,兩張表結構不一樣相互獨立。但是從這個角度來看垂直拆分並沒有從根本上解決單表數據量過大的問題,因此我們還是需要做一次水平拆分。

拆分表

還有一種拆分方法,比如表中有一萬條數據,我們拆分為兩張表,id 為奇數的:1,3,5,7……放在 user1, id 為偶數的:2,4,6,8……放在 user2中,這樣的拆分辦法就是水平拆分了。

水平拆分的方式也很多,除了上面說的按照 id 拆表,還可以按照時間維度取拆分,比如訂單表,可以按每日、每月等進行拆分。

每月表:可以起一個定時任務將前一天的數據全部遷移到當月表。

歷史表:同樣可以用定時任務把時間超過 30 天的數據遷移到 history表。

總結一下水平拆分和垂直拆分的特點:

單庫內拆分和多庫拆分

拿水平拆分為例,每張表都拆分為了多個子表,多個子表存在於同一資料庫中。比如下面用戶表拆分為用戶1表、用戶2表。

單庫拆分

在一個資料庫中將一張表拆分為幾個子表在一定程度上可以解決單表查詢性能的問題,但是也會遇到一個問題:單資料庫存儲瓶頸。

所以在業界用的更多的還是將子表拆分到多個資料庫中。比如下圖中,用戶表拆分為兩個子表,兩個子表分別存在於不同的資料庫中。

多庫拆分

一句話總結:分表主要是為了減少單張表的大小,解決單表數據量帶來的性能問題。

分庫分錶帶來的複雜性

既然分庫分表這麼好,那我們是不是在項目初期就應該採用這種方案呢?不要激動,冷靜一下,分庫分表的確解決了很多問題,但是也給系統帶來了很多複雜性,下面簡要說一說。

(1)跨庫關聯查詢

在單庫未拆分表之前,我們可以很方便使用 join 操作關聯多張表查詢數據,但是經過分庫分表後兩張表可能都不在一個資料庫中,如何使用 join 呢?

有幾種方案可以解決:

欄位冗餘:把需要關聯的欄位放入主表中,避免 join 操作;數據抽象:通過ETL等將數據匯合聚集,生成新的表;全局表:比如一些基礎表可以在每個資料庫中都放一份;應用層組裝:將基礎數據查出來,通過應用程式計算組裝;

(2)分布式事務

單資料庫可以用本地事務搞定,使用多資料庫就只能通過分布式事務解決了。

常用解決方案有:基於可靠消息(MQ)的解決方案、兩階段事務提交、柔性事務等。

(3)排序、分頁、函數計算問題

在使用 SQL 時 order by, limit 等關鍵字需要特殊處理,一般來說採用分片的思路:

先在每個分片上執行相應的函數,然後將各個分片的結果集進行匯總和再次計算,最終得到結果。

(4)分布式 ID

如果使用 Mysql 資料庫在單庫單表可以使用 id 自增作為主鍵,分庫分表了之後就不行了,會出現id 重複。

常用的分布式 ID 解決方案有:

這些方案後面會寫文章專門介紹,這裡不再展開。

(5)多數據源

分庫分表之後可能會面臨從多個資料庫或多個子表中獲取數據,一般的解決思路有:客戶端適配和代理層適配。

業界常用的中間件有:

shardingsphere(前身 sharding-jdbc)總結

如果出現資料庫問題不要著急分庫分表,先看一下使用常規手段是否能夠解決。

分庫分表會給系統帶來巨大的複雜性,不是萬不得已建議不要提前使用。作為系統架構師可以讓系統靈活性和可擴展性強,但是不要過度設計和超前設計。在這一點上,架構師一定要有前瞻性,提前做好預判。大家學會了嗎?

相關焦點

  • 分庫分表之後,id 主鍵如何處理?
    問:分庫分表之後,id 主鍵如何處理?
  • [精選] MySQL分庫分表後用PHP如何來完美操作
    根據主題的tid最後一位來決定附件要保存在哪個分表。phpclass Config{      public $dsn;      public $user;      public $password;      public $dbname; //分庫分表後得到的資料庫名    public $table; //分庫分表後得到的表名    private static $config;//mysql
  • Spring Boot 採用Sharding-JDBC 實現Mybaits的分庫分表功能
    背景在開發大數據量的應用時為了減少單表數據量經常會使用到分庫分表功能,以前對分庫分表功能都是自己在代碼上單獨對需要分庫分表的實體進行特殊邏輯處理
  • 億級大表分庫分表實戰總結(萬字乾貨,實戰復盤)
    2.第一階段:業務重構(可選)對於微服務劃分比較合理的分庫分表行為,一般只需要關注存儲架構的變化,或者只需要在個別應用上進行業務改造即可,一般不需要著重考慮「業務重構」 這一階段,因此,這一階段屬於「可選」。本次項目的第一大難點,在於業務重構。
  • 分庫分表的4個面試連環炮問題!不會就慘了
    一、面試題為什麼要分庫分表(設計高並發系統的時候,資料庫層面該如何設計)?用過哪些分庫分表中間件?不同的分庫分表中間件都有什麼優點和缺點?你們具體是如何對資料庫如何進行垂直拆分或水平拆分的?三、面試題剖析1、為什麼要分庫分表?(設計高並發系統的時候,資料庫層面該如何設計?)說白了,分庫分表是兩回事兒,大家可別搞混了,可能是光分庫不分表,也可能是光分表不分庫,都有可能。我先給大家拋出來一個場景。
  • 如何徹底解決煩人的 MySQL 分庫分表問題?寫一個更好的資料庫!
    從學術的角度上看起來,並不是提出了什麼驚天地泣鬼神的神奇算法,我們選擇的 Shared-nothing 的架構其實在當時的業界也不是什麼新鮮的事情了,但真正令我激動的是:我們要造的是一個真正能作為整個系統的 Single Source of Truth 的基礎軟體。這句話怎麼理解呢?我在後邊會好好聊聊。
  • 數據量大了一定要分表,分庫分表Sharding-JDBC入門與項目實戰
    最近項目中不少表的數據量越來越大,並且導致了一些資料庫的性能問題。因此想藉助一些分庫分表的中間件,實現自動化分庫分表實現。調研下來,發現Sharding-JDBC目前成熟度最高並且應用最廣的Java分庫分表的客戶端組件。
  • MySQL:網際網路公司常用分庫分表方案匯總
    作者:尜尜人物cnblogs.com/littlecharacter/p/9342129.html本文目錄一、資料庫瓶頸二、分庫分表三、分庫分表工具四、分庫分表步驟五、分庫分表問題非partition key的查詢問題非partition key跨庫跨表分頁查詢問題
  • 分庫分表【Sharding-JDBC】入門與項目實戰
    作者:六點半起床https://juejin.im/post/5edc9f3ff265da76bc760726最近項目中不少表的數據量越來越大,並且導致了一些資料庫的性能問題。因此想藉助一些分庫分表的中間件,實現自動化分庫分表實現。調研下來,發現Sharding-JDBC目前成熟度最高並且應用最廣的Java分庫分表的客戶端組件。
  • 超全的資料庫建表、SQL、索引規範
    回復 1024 有特別禮包來源:juejin.im/post/6871969929365553165上一篇:為什麼我不建議你用阿里巴巴【強制】(2)每張表必須設置一個主鍵ID,且這個主鍵ID使用自增主鍵(在滿足需要的情況下儘量短),除非在分庫分表環境下。解讀:由於InnoDB組織數據的方式決定了需要有一個主鍵,而且若是這個主鍵ID是單調遞增的可以有效提高插入的性能,避免過多的頁分裂、減少表碎片提高空間的使用率。而在分庫分表環境下,則需要統一來分配各個表中的主鍵值,從而避免整個邏輯表中主鍵重複。
  • 分庫分表常見概念解讀+Sharding-JDBC實戰
    什麼是分庫分表其實 分庫 和 分表 是兩個概念,只不過通常分庫與分表的操作會同時進行,以至於我們習慣性的將它們合在一起叫做分庫分表。分庫分表是為了解決由於庫、表數據量過大,而導致資料庫性能持續下降的問題。
  • 一個億級分庫分表項目的實戰全過程解析
    分庫分表的文章網上非常多,但是大多內容比較零散,以講解知識點為主,沒有完整地說明一個大表的切分、新架構設計、上線的完整過程。因此,我結合去年做的一個大型分庫分表項目,來復盤一下完整的分庫分表從架構設計到發布上線的實戰總結。為什麼需要做分庫分表?這個相信大家多少都有所了解。
  • 超全的資料庫建表/SQL/索引規範,建議貼在工位上!
    「【強制】(2)每張表必須設置一個主鍵ID,且這個主鍵ID使用自增主鍵(在滿足需要的情況下儘量短),除非在分庫分表環境下」解讀:由於InnoDB組織數據的方式決定了需要有一個主鍵,而且若是這個主鍵ID是單調遞增的可以有效提高插入的性能,避免過多的頁分裂
  • 為什麼我們國家的標準視力表要用E?
    為什麼我們國家的標準視力表要用E?時間:2020-06-01 09:36   來源:優遊網   責任編輯:沫朵 川北在線核心提示:原標題:提問! 為什麼我們國家的標準視力表要用E? 為什麼我們國家的標準視力表要用E?檢測視力更有效、eyes的首字母。
  • Sharding-jdbc的實戰入門之水平分表(一)
    經常碰到一些小夥伴的問題,就是我們到達什麼量級才會分庫分表?分庫分表經驗值mysql單表經驗一般MySQL單表1000W左右的數據是可以不需要考慮分表的。當然,除了考慮當前的數據量和性能情況時,我們需要提前考慮系統半年到一年左右的業務增長情況。
  • 分庫分表?如何做到永不遷移數據和避免熱點?
    二、分庫分表方案分庫分表方案中有常用的方案,hash取模和range範圍方案;分庫分表方案最主要就是路由算法,把路由的key按照指定的算法進行路由存放。下邊來介紹一下兩個方案的特點。遇到這個情況,我們小夥伴想到的方案就是做數據遷移,把之前的4000萬數據,重新做一個hash方案,放到新的規劃分表中。也就是我們要做數據遷移。這個是很痛苦的事情。有些小公司可以接受晚上停機遷移,但大公司是不允許停機做數據遷移的。
  • 一文快速入門分庫分表中間件 Sharding-JDBC(必修課)
    分庫分表實戰系列的開篇文章,我們在前文中回顧了一下分庫分表的基礎知識,對分庫分表的拆分方式有了一定的了解。ShardingSphere 的前身就是 Sharding-JDBC,所以它是整個框架中最為經典、成熟的組件,我們先從 Sharding-JDBC 框架入手學習分庫分表。2.
  • sqltoy-orm-4.17.5 發布,支持 QueryExecutor 中定義分庫分表
    開源地址:更新內容1、 支持QueryExecutor、EntityQuery中使用分庫分表策略配置 (原本只支持xml中定義)//分庫dbSharding(String strategy, String... paramNames)//分表tableSharding(String strategy, String[] tables, String... paramNames)
  • 我們為什麼要學習spss?
    領導當時給了我兩張表,是兩撥人不同的購買信息。每一張表都有幾千人,這其中又存在幾百名購買者姓名和手機號是同一個人的情況。這些信息重複的購買者,在一個表中購買了a類課程,在一個表中購買了b類課程。領導要求我把兩張表合併,同一個購買者的購買數據需要加總,變成一行顯示。現在,請先不要往下看,想想如果是你,你會怎麼做?