數據庫擴容方案


參考:

https://m.w3cschool.cn/architectroad/architectroad-database-smooth-expansion.html

https://www.cnblogs.com/kafeixiaoluo/p/9156855.html

https://developer.aliyun.com/article/578120

 

 

 

 

數據庫秒級平滑擴容架構方案

一、緣起


(1)並發量大,流量大的互聯網架構,一般來說,數據庫上層都有一個服務層服務層記錄了“業務庫名”與“數據庫實例”的映射關系,通過數據庫連接池向數據庫路由sql語句以執行:

單庫服務層
如上圖:服務層配置用戶庫user對應的數據庫實例物理位置為ip(其實是一個內網域名)。

(2)隨着數據量的增大,數據要進行水平切分,分庫后將數據分布到不同的數據庫實例(甚至物理機器)上,以達到降低數據量,增強性能的擴容目的:
數據水平切分
如上圖:用戶庫user分布在兩個實例上,ip0和ip1,服務層通過用戶標識uid取模的方式進行尋庫路由,模2余0的訪問ip0上的user庫,模2余1的訪問ip1上的user庫。

關於數據庫水平切分,垂直切分的更多細節,詳見《一分鍾掌握數據庫垂直拆分》。

(3)互聯網架構需要保證數據庫高可用,常見的一種方式,使用雙主同步+keepalived+虛ip的方式保證數據庫的可用性:
虛ip,高可用
如上圖:兩個相互同步的主庫使用相同的虛ip。
虛ip漂移
如上圖:當主庫掛掉的時候,虛ip自動漂移到另一個主庫,整個過程對調用方透明,通過這種方式保證數據庫的高可用。

關於高可用的更多細節,詳見《究竟啥才是互聯網架構“高可用”》。

(4)綜合上文的(2)和(3),線上實際的架構,既有水平切分,又有高可用保證,所以實際的數據庫架構是這樣的: 
水平切分+高可用

提問:如果數據量持續增大,分2個庫性能扛不住了,該怎么辦呢?

回答:繼續水平拆分,拆成更多的庫,降低單庫數據量,增加庫主庫實例(機器)數量,提高性能。


最終問題拋出:分成x個庫后,隨着數據量的增加,要增加到y個庫,數據庫擴容的過程中,能否平滑,持續對外提供服務,保證服務的可用性,是本文要討論的問題。

二、停服務方案


在討論平滑方案之前,先簡要說明下“x庫拆y庫”停服務的方案:

(1)站點掛一個公告“為了為廣大用戶提供更好的服務,本站點/游戲將在今晚00:00-2:00之間升級,屆時將不能登錄,用戶周知”

(2)停服務

(3)新建y個庫,做好高可用

(4)數據遷移,重新分布,寫一個數據遷移程序,從x個庫里導入到y個庫里,路由規則由%x升級為%y

(5)修改服務配置,原來x行配置升級為y行

(6)重啟服務,連接新庫重新對外提供服務

整個過程中,最耗時的是第四步數據遷移

 

回滾方案

如果數據遷移失敗,或者遷移后測試失敗,則將配置改回x庫,恢復服務,改天再掛公告。


方案優點:簡單

方案缺點

(1)停服務,不高可用

(2)技術同學壓力大,所有工作要在規定時間內做完,根據經驗,壓力越大約容易出錯(這一點很致命)

(3)如果有問題第一時間沒檢查出來,啟動了服務,運行一段時間后再發現有問題,難以回滾,需要回檔,可能會丟失一部分數據

 

適用:

  1. 小型網站;
  2. 大部分游戲;
  3. 對高可用要求不高的服務。


有沒有更平滑的方案呢?

三、秒級、平滑、帥氣方案


水平切分+高可用
再次看一眼擴容前的架構,分兩個庫,假設每個庫1億數據量,如何平滑擴容,增加實例數,降低單庫數據量呢?三個簡單步驟搞定。

(1)修改配置
修改配置

主要修改兩處:

a)數據庫實例所在的機器做雙虛ip,原來%2=0的庫是虛ip0,現在增加一個虛ip00,%2=1的另一個庫同理

b)修改服務的配置(不管是在配置文件里,還是在配置中心),將2個庫的數據庫配置,改為4個庫的數據庫配置,修改的時候要注意舊庫與新庫的映射關系

%2=0的庫,會變為%4=0與%4=2;

%2=1的部分,會變為%4=1與%4=3;

這樣修改是為了保證,拆分后依然能夠路由到正確的數據。


(2)reload配置,實例擴容
重啟reload配置,完成擴庫

服務層reload配置,reload可能是這么幾種方式:

a)比較原始的,重啟服務,讀新的配置文件

b)高級一點的,配置中心給服務發信號,重讀配置文件,重新初始化數據庫連接池


不管哪種方式,reload之后,數據庫的實例擴容就完成了,原來是2個數據庫實例提供服務,現在變為4個數據庫實例提供服務,這個過程一般可以在秒級完成。

整個過程可以逐步重啟,對服務的正確性和可用性完全沒有影響

a)即使%2尋庫和%4尋庫同時存在,也不影響數據的正確性,因為此時仍然是雙主數據同步的

b)服務reload之前是不對外提供服務的,冗余的服務能夠保證高可用


完成了實例的擴展,會發現每個數據庫的數據量依然沒有下降,所以第三個步驟還要做一些收尾工作。

(3)收尾工作,數據收縮
收尾工作

有這些一些收尾工作

a)把雙虛ip修改回單虛ip

b)解除舊的雙主同步,讓成對庫的數據不再同步增加

c)增加新的雙主同步,保證高可用

d)刪除掉冗余數據,例如:ip0里%4=2的數據全部干掉,只為%4=0的數據提供服務啦


這樣下來,每個庫的數據量就降為原來的一半,數據收縮完成

 

優點

  1. 擴容期間,服務照常進行,保證高可用
  2. 時間長,項目組壓力沒有這么大,出錯率低
  3. 擴容期間,遇到什么問題,都可以隨時調試,不怕影響線上服務
  4. 每個數據庫少了一半的數據量。

 

缺點

  1. 程序復雜,需要配置雙主、主主雙寫、檢測數據同步等額外技術;
  2. 但后期數據庫成千上萬台的時候,擴容復雜(情況非常少,除非將很多業務數據放在同一個數據庫)。

 

適用:

  1. 大型網站;
  2. 對高可用要求高的服務。

四、總結


2個1億庫到4個5KW庫
該帥氣方案能夠實現n庫擴2n庫的秒級、平滑擴容,增加數據庫服務能力,降低單庫一半的數據量,其核心原理是:成倍擴容,避免數據遷移。【4擴8也同樣適用】

遷移步驟

(1)修改配置

(2)reload配置,實例擴容完成

(3)刪除冗余數據等收尾工作,數據量收縮完成

 

  • 停機擴容:簡單,不高可用,易出錯,擴容后不能回滾,只能回檔,會丟失一部分數據。
  • 平滑擴容:復雜,高可用,出錯調試容易,易回滾,不會造成數據丟失

 

 

 

 

 

 

 

數據庫優化和擴容(圖文詳解)

一、數據庫瓶頸

不管是IO瓶頸,還是CPU瓶頸,最終都會導致數據庫的活躍連接數增加,進而逼近甚至達到數據庫可承載活躍連接數的閾值。在業務Service來看就是,可用數據庫連接少甚至無連接可用。接下來就可以想象了吧(並發量、吞吐量、崩潰)。

IO瓶頸

第一種:磁盤讀IO瓶頸,熱點數據太多,數據庫緩存放不下,每次查詢時會產生大量的IO,降低查詢速度 -> 分庫和垂直分表

第二種:網絡IO瓶頸,請求的數據太多,網絡帶寬不夠 -> 分庫

CPU瓶頸

第一種:SQL問題,如SQL中包含join,group by,order by,非索引字段條件查詢等,增加CPU運算的操作 -> SQL優化,建立合適的索引,在業務Service層進行業務計算

第二種:單表數據量太大,查詢時掃描的行太多,SQL效率低,CPU率先出現瓶頸 -> 水平分表。

二、分庫分表

水平分庫

概念:以字段為依據,按照一定策略(hash、range等),將一個庫中的數據拆分到多個庫中。

結果:

  • 每個庫的結構都一樣;
  • 每個庫的數據都不一樣,沒有交集;
  • 所有庫的並集是全量數據;

場景:系統絕對並發量上來了,分表難以根本上解決問題,並且還沒有明顯的業務歸屬來垂直分庫

分析:庫多了,io和cpu的壓力自然可以成倍緩解。

水平分表

概念:以字段為依據,按照一定策略(hash、range等),將一個表中的數據拆分到多個表中。

結果:

  • 每個表的結構都一樣;
  • 每個表的數據都不一樣,沒有交集;
  • 所有表的並集是全量數據;

場景:系統絕對並發量並沒有上來,只是單表的數據量太多,影響了SQL效率,加重了CPU負擔,以至於成為瓶頸

分析:表的數據量少了,單次SQL執行效率高,自然減輕了CPU的負擔。

垂直分庫

概念:以表為依據,按照業務歸屬不同,將不同的表拆分到不同的庫中。

結果:

  • 每個庫的結構都不一樣;
  • 每個庫的數據也不一樣,沒有交集;
  • 所有庫的並集是全量數據;

場景:系統絕對並發量上來了,並且可以抽象出單獨的業務模塊

分析:到這一步,基本上就可以服務化了。例如,隨着業務的發展一些公用的配置表、字典表等越來越多,這時可以將這些表拆到單獨的庫中,甚至可以服務化。再有,隨着業務的發展孵化出了一套業務模式,這時可以將相關的表拆到單獨的庫中,甚至可以服務化。

垂直分表

概念:以字段為依據,按照字段的活躍性,將表中字段拆到不同的表(主表和擴展表)中。

結果:

  • 每個表的結構都不一樣;
  • 每個表的數據也不一樣,一般來說,每個表的字段至少有一列交集,一般是主鍵,用於關聯數據;
  • 所有表的並集是全量數據;

場景:系統絕對並發量並沒有上來,表的記錄並不多,但是字段多,並且熱點數據和非熱點數據在一起,單行數據所需的存儲空間較大。以至於數據庫緩存的數據行減少,查詢時會去讀磁盤數據產生大量的隨機讀IO,產生IO瓶頸

分析:可以用列表頁和詳情頁來幫助理解。垂直分表的拆分原則是將熱點數據(可能會冗余經常一起查詢的數據)放在一起作為主表,非熱點數據放在一起作為擴展表。這樣更多的熱點數據就能被緩存下來,進而減少了隨機讀IO。拆了之后,要想獲得全部數據就需要關聯兩個表來取數據。但記住,千萬別用join,因為join不僅會增加CPU負擔並且會講兩個表耦合在一起(必須在一個數據庫實例上)。關聯數據,應該在業務Service層做文章,分別獲取主表和擴展表數據然后用關聯字段關聯得到全部數據。

分庫分表工具

sharding-sphere:jar,前身是sharding-jdbc;

TDDL:jar,Taobao Distribute Data Layer;

Mycat:中間件。

注:工具的利弊,請自行調研,官網和社區優先。

分庫分表步驟

根據容量(當前容量和增長量)評估分庫或分表個數 -> 選key(均勻)-> 分表規則(hash或range等)-> 執行(一般雙寫)-> 擴容問題(盡量減少數據的移動)。

分庫分表問題

非partition key的查詢問題

基於水平分庫分表,拆分策略為常用的hash法。

端上除了partition key只有一個非partition key作為條件查詢

映射法

基因法

注:寫入時,基因法生成user_id,如圖。關於xbit基因,例如要分8張表,23=8,故x取3,即3bit基因。根據user_id查詢時可直接取模路由到對應的分庫或分表。根據user_name查詢時,先通過user_name_code生成函數生成user_name_code再對其取模路由到對應的分庫或分表。id生成常用snowflake算法。

端上除了partition key不止一個非partition key作為條件查詢

映射法

冗余法

注:按照order_id或buyer_id查詢時路由到db_o_buyer庫中,按照seller_id查詢時路由到db_o_seller庫中。感覺有點本末倒置!有其他好的辦法嗎?改變技術棧呢?

后台除了partition key還有各種非partition key組合條件查詢

NoSQL法

冗余法

非partition key跨庫跨表分頁查詢問題

基於水平分庫分表,拆分策略為常用的hash法。

注:用NoSQL法解決(ES等)。

擴容問題

基於水平分庫分表,拆分策略為常用的hash法

水平擴容庫(升級從庫法)

注:擴容是成倍的

水平擴容表(雙寫遷移法)

第一步:(同步雙寫)修改應用配置和代碼,加上雙寫,部署;

第二步:(同步雙寫)將老庫中的老數據復制到新庫中;

第三步:(同步雙寫)以老庫為准校對新庫中的老數據;

第四步:(同步雙寫)修改應用配置和代碼,去掉雙寫,部署;

注:雙寫是通用方案。

分庫分表總結

  • 分庫分表,首先得知道瓶頸在哪里,然后才能合理地拆分(分庫還是分表?水平還是垂直?分幾個?)。且不可為了分庫分表而拆分。
  • 選key很重要,既要考慮到拆分均勻,也要考慮到非partition key的查詢。
  • 只要能滿足需求,拆分規則越簡單越好。

--轉載自:不用找了,大廠在用的分庫分表方案,都在這里! - 瀟湘夜雨的文章 - 知乎 https://zhuanlan.zhihu.com/p/143072827不用找了,大廠在用的分庫分表方案,都在這里! - 瀟湘夜雨的文章 - 知乎 https://zhuanlan.zhihu.com/p/143072827https://zhuanlan.zhihu.com/p/143072827

不用找了,大廠在用的分庫分表方案,都在這里! - 瀟湘夜雨的文章 - 知乎 https://zhuanlan.zhihu.com/p/143072827

不用找了,大廠在用的分庫分表方案,都在這里! - 瀟湘夜雨的文章 - 知乎 https://zhuanlan.zhihu.com/p/143072827

 
 

 

 

 

 

MySQL數據庫水平擴容方案

一、         背景

隨着雲技術的不斷發展,存儲資源和計算資源的成熟,成本不斷下降,使得企業開發部署提供服務更加的便捷。得益於此,對於高速發展的中小型企業,可以通過不斷地堆砌機器,增加應用集群來應對不斷增長的流量。

但是隨着企業的不斷發展,其中一個瓶頸並不能簡單地通過堆砌機器來解決,這便是越來越龐大的數據庫帶來的性能天花。而應對這個天花板的有效手段之一,就是通過對數據庫的分庫分表,使得單表的數據量低於500W

今天我們討論一個更長遠一點的問題。就是當企業進入了高速增長的賽道后,即便是對數據分庫分表后,無論是數據庫的容量,還是單庫單表的數據量也總會到達天花板,此時該如何擴展我們的數據庫性能。

 

二、 基礎知識

      1、分庫分表

分庫分表這個概念十分好理解,就是原來存儲在一個數據表內的數據,通過某種規則平均的分散在多個數據庫的多張同樣結構的數據表中。

我們假定以用戶表(t_user)來舉例子,假設當前這個表的數據量已經到達了2kw,相對500w這個臨界值來說足足超過了4倍。那么我們如何通過分庫分表來調整該表?

正如上面所說到的,我們可以對數據表其中一個或多個字段,通過某個可計算的平均的每次計算結果絕對一致的算法來分散存儲我們的數據。可以很容易的想到很多種算法,比如對id進行mod運算,對創建時間按月分成多個表等等。

936532075e5bc4519f6d841d9778c8e8ace30464

圖1.分庫分表示意圖

 

如上圖所展示的,表通過函數fx的計算后倍分散到不同數據庫的不同表中。這里可以簡單代入一種分庫算法——id取膜

上圖中一共分了三個數據庫,每個庫中又分了兩張數據表。那么id % 3 == 0的數據都會集中到分庫1中,分庫1中的數據id % 2 == 0 的數據,又會存儲在第一個分表中,其他如此類推。

 

三、         水平擴展

首先來思考一下,水平擴展有什么技術難度?

4c4c4da51b432e8b623dad389da61f44920e224e

圖2.水平擴容示意圖

 

 

第一個顯而易見的問題就是規則的變化和數據的遷移。

如果控制分庫分表的規則是通過應用程序內完成的,規則的變化意味着必須重新發布使用新規則的應用集群。而數據遷移帶來的麻煩則更加嚴重,在數據沒有完成遷移之前,需要編寫專門的腳本來處理數據的導出導入,不同的業務不同的表關系,都會使得這個腳本變得極其的復雜,而且還要同時兼顧增量數據同步,時間點,數據一致性等問題,稍有不慎,便會對用戶的數據造成影響。

 

思考,是否有一種分庫規則,在擴展分庫的時候不需要進行規則的變化和數據的遷移呢

 

 

答案當然是有的,就是將分庫的規則修改為按段分庫。如下圖所示,如果我們分三個庫,每個庫中有兩張分表(分表規則還是按取膜運算),那么一共可以存儲3kw的數據,其中分庫規則為id的值在[1,1kw]的會被存儲到0庫上,在[1kw+1, 2kw]范圍的會被存儲到1庫,在[2kw+1, 3kw]范圍的會被存儲到2庫。當我們的數據量突破3kw時,我們只需要增加一個分庫,用於存儲[3kw+1, 4kw]范圍的數據即可,完全不需要對前三個分庫的數據做處理。同理,也可以基於時間的分庫方式。

7ff4b5cc7d3a2d746085b56972de733f0f180203

圖3.避免數據遷移和規則更新的分庫示例圖

 

這種分庫的規則優勢可以說是非常的明顯了,但是這種分庫規則會帶來什么樣的劣勢呢?

可以想象,我們為什么要做分庫分表?就是單庫的性能已經不能滿足我們日常的業務需求了,需要將單個數據庫的性能壓力分攤到多個數據庫上。而上述這種分庫方式,勢必會導致insert/update壓力都集中到一個數據庫實例上,並不能很好得分攤性能壓力。

 

 

那么現在第三個問題來了,是否還有什么分庫規划方式,既能避免數據遷移的成本,又能解決單庫性能熱點問題的方案呢?

答案肯定也是有的,下面我們來介紹一下阿里雲TDDL團隊給出的幾種水平擴展模式。

8d0fedaa22bdc1961bc964aacad0aa7f7494806f

圖4.水平擴展模式1

 

第一種水平擴展的模式如上圖,在我們只有一個分庫的時候,可以通過設定4個分表,示例中使用簡單的id取膜分表方式。當單庫的容量已經達到上限,我們可以通過增加一個數據庫實例,把分表2,3整體遷移到新的實例上。這樣做的好處是,只需把整表遷移到新的庫中即可,無需考慮單條數據因為規則的變化而重新計算需要遷移到那個庫。當兩個庫也不夠用是,以此類推,增加兩個分庫,分別吧table1和table3遷移到新的分庫即可。

上述方案有一個缺點,就是在從一個庫到4個庫的過程中,單表的數據量一直在增長。當單表的數據量超過一定范圍時,可能會帶來性能問題。另外當開始預留的分表個數用盡,到了4物理庫每庫1個表的階段,再進行擴容的話,不可避免的要再次從表上下手。

 

 

為了解決模式1的問題,我們接下來看看模式是如何處理的:

23bcfcc3e5b1d4e0ffb6a6f764658a319faacf99

圖5.水平擴展模式2

 

模式2與模式1有類似之處,在擴展階段,還是選擇整表遷移的方式,為了簡化說明,此處使用兩個分表來做說明。

如上圖5,擴展了分庫,把table1整表遷移到新庫中后,如果此時單邊已經快接近500w,我們可以在每個分庫中再創建一個分表,用於存放超過500w部分的數據。此時分庫分表的規則就變為:

通過id % 2確定分庫,然后通過id段[1+0.5kw, 1kw]的數據分表存放在table_0_1和table_1_1中。這樣既滿足的降低單表500w水平線值,也解決了熱點數據庫的問題

如果隨着時間的流逝,我們的數據庫容量需要再次升級,也只需要重新購買兩個實例,把table_0_1和table_1_1分別遷移到新的實例上即可,同理也可以通過為每個分庫建立新分表來解決500w問題

 

 

以上都是倍數增長的擴容方案,對於中小型的企業來說,數據庫資源的開銷很是很大的。用2實例到4實例的費用就增長了一倍,而從4實例到8實例又增長了一倍。那么非倍數擴容的方案是如何的呢?

 

其實原理是相通的,譬如我們從2實例擴容到3實例時,此時我們table_0和table_1很大可能已經飽和了(單表達到500w),我們可以新購一個實例,用於存放這兩個“歷史數據”表,另外兩個則按照模式2進行擴展,這樣單庫熱點問題還是平均到兩個庫上。當然,我們也可以通過給每個庫增加一個分表,來達到每個分庫都承擔1/3的壓力。只不過這種模式,對於分庫分表的規則就提高和很大的復雜度。

9c406958e88efcfc9f3a576da4d11fd1783ecc0c

圖6.水平擴展模式3

 

四、         結語

一個好的設計往往可以為后期的升級維護帶來便利,數據庫的水平擴容是一個很大的技術難點,但是通過優化我們的分庫分表策,還是可以在一定程度上減輕工作量。這個准則無論是放到代碼編寫,產品設計或是生活的方方面面都一樣適用,所以當我們遇到一個難以實現的設計時,也需要反思這種設計是否合理,是否會有更優的方案?

 


免責聲明!

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



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