分庫分表入門介紹

本文收集網上資料,多合一
編撰於2020年4月21日
原文鏈接1
原文鏈接2
原文鏈接3

爲什麼要分庫分表

移動互聯網時代,海量的用戶每天產生海量的數量,比如:

  • 用戶表
  • 訂單表
  • 交易流水錶

以支付寶用戶爲例,8億;微信用戶更是10億。訂單表更誇張,比如美團外賣,每天都是幾千萬的訂單。淘寶的歷史訂單總量應該百億,甚至千億級別,這些海量數據遠不是一張表能Hold住的。事實上MySQL單表可以存儲10億級數據,只是這時候性能比較差,業界公認MySQL單表容量在1KW以下是最佳狀態,因爲這時它的BTREE索引樹高在3~5之間。

既然一張表無法搞定,那麼就想辦法將數據放到多個地方,目前比較普遍的方案有3個:

  1. 讀寫分離
  2. 分庫分表
  3. NoSQL/NewSQL

說明:只分庫,或者只分表,或者分庫分表融合方案都統一認爲是分庫分表方案,因爲分庫,或者分表只是一種特殊的分庫分表而已。NoSQL比較具有代表性的是MongoDB,es。NewSQL比較具有代表性的是TiDB。

讀寫分離,主從複製

在實際應用中的絕大多數情況下讀操作遠大於寫操作。MySQL提供了讀寫分離的機制,所有寫操作必須對應到主庫(Master),讀操作可以在主庫(Master)和從庫(Slave)機器上進行。

主庫與從庫的結構完全一樣,一個主庫可以有多個從庫,甚至在從庫下還可以掛從庫,這種一主多從的方式可以有效地提高數據庫集羣的吞吐量。

在DBA領域一般配置主-主-從或者主-從-從兩種部署模型。

所有寫操作都先在主庫上進行,然後異步更新到從庫上,所以從主庫同步到從庫機器有一定的延遲,當系統很繁忙時,延遲問題會更加嚴重,從庫機器數量的增加也會使這個問題更嚴重。

此外,主庫是集羣的瓶頸,當寫操作過多時會嚴重影響主庫的穩定性,如果主庫掛掉,則整個集羣都將不能正常工作。

根據以上特點,我們總結一些最佳實踐如下。

  • 當讀操作壓力很大時,可以考慮添加從庫機器來分解大量讀操作帶來的壓力,但是當從庫機器達到一定的數量時,就需要考慮分庫來緩解壓力了。
  • 當寫壓力很大時,就必須進行分庫分表操作了。

可能會因爲種種原因,集羣中的數據庫硬件配置等會不一樣,某些性能高,某些性能低,這時可以通過程序控制每臺機器讀寫的比重來達到負載均衡,這需要更加複雜的讀寫分離的路由規則。

Why Not NoSQL/NewSQL?

首先,爲什麼不選擇第三種方案NoSQL/NewSQL,我認爲主要是RDBMS有以下幾個優點:

  1. RDBMS生態完善;
  2. RDBMS絕對穩定;
  3. RDBMS的事務特性;

NoSQL/NewSQL作爲新生兒,在我們把可靠性當做首要考察對象時,它是無法與RDBMS相提並論的。RDBMS發展幾十年,只要有軟件的地方,它都是核心存儲的首選。

目前絕大部分公司的核心數據都是:以RDBMS存儲爲主,NoSQL/NewSQL存儲爲輔!互聯網公司又以MySQL爲主,國企&銀行等不差錢的企業以Oracle/DB2爲主!NoSQL/NewSQL宣傳的無論多牛逼,就現在各大公司對它的定位,都是RDBMS的補充,而不是取而代之!

什麼是RDBMS

RDBMS即關係數據庫管理系統(Relational Database Management System),是將數據組織爲相關的行和列的系統,而管理關係數據庫的計算機軟件就是關係數據庫管理系統,常用的數據庫軟件有Oracle、SQL Server等。

分庫分表概述

在日常的工作中,關係型數據庫本身比較容易成爲系統的瓶頸點,雖然讀寫分離能分散數據庫的讀寫壓力,但並沒有分散存儲壓力,當數據量達到千萬甚至上億時,單臺數據庫服務器的存儲能力會成爲系統的瓶頸,主要體現在以下幾個方面:

  • 數據量太大,讀寫的性能會下降,即使有索引,索引也會變得很大,性能同樣會降下。
  • 數據庫文件會得很大,數據庫備份和恢復需要耗時很長。
  • 數據庫文件越大,極端情況下丟失數據的風險越高。
    因此,當流量越來越大時,且單機容量達到上限時,此時需要考慮對其進行切分,切分的目的就在於減少單機數據庫的負擔,將由多臺數據庫服務器一起來分擔,縮短查詢時間。

切分策略

數據切分分爲兩種方式,縱向切分和水平切分

  1. 縱向切分
      常見有縱向分庫縱向分表兩種。
      
    1). 縱向分庫就是根據業務耦合性,將關聯度低的不同表存儲在不同的數據庫,做法與大系統拆分爲多個小系統類似,按業務分類進行獨立劃分。與“微服務治理”的做法相似,每個微服務使用單獨的一個數據庫。

    2). 垂直分表是基於數據庫中的列進行,某個表字段較多,可以新建一張擴展表,將不經常用或者字段長度較大的字段拆出到擴展表中。在字段很多的情況下,通過大表拆小表,更便於開發與維護,也能避免跨頁問題,MYSQL底層是通過數據頁存儲的,一條記錄佔用空間過大會導致跨頁,造成額外的開銷。另外,數據庫以行爲單位將數據加載到內存中,這樣表中字段長度越短且訪問頻次較高,內存能加載更多的數據,命中率更高,減少磁盤IO,從而提升數據庫的性能。

垂直切分的優點:

  • 解決業務系統層面的耦合,業務清晰
  • 與微服務的治理類似,也能對不同業務的數據進行分級管理,維護,監控,擴展等。
  • 高併發場景下,垂直切分一定程度的提升IO,數據庫連接數,單機硬件資源的瓶頸。

垂直切分的缺點

  • 部分表無法join,只能通過接口聚合方式解決,提升了開發的複雜度。
  • 分佈式事處理複雜
  • 依然存在單表數據量過大的問題。
  1. 水平切分
      當一個應用難以再細粒度的垂直切分或切分後數據量行數依然巨大,存在單庫讀寫,存儲性能瓶頸,這時候需要進行水平切分。
      水平切分爲庫內分表和分庫分表,是根據表內數據內在的邏輯關係,將同一個表按不同的條件分散到多個數據庫或多表中,每個表中只包含一部分數據,從而使得單個表的數據量變小,達到分佈式的效果。
      庫內分表只解決單一表數據量過大的問題,但沒有將表分佈到不同機器的庫上,因些對於減輕mysql的壓力來說幫助不是很大,大家還是競爭同一個物理機的CPU、內存、網絡IO,最好通過分庫分表來解決。

水平切分優點

  • 不存在單庫數據量過大、高併發的性能瓶頸,提升系統穩定性和負載能力。
  • 應用端改造較小,不需要拆分業務模塊。

水平切分缺點

  • 跨分片的事務一致性難以保證
  • 跨庫的join關聯查詢性能較差
  • 數據多次擴展維度和維護量極大。

路由規則

水平切分後同一張表會出現在多個數據庫或表中,每個庫和表的內容不同,對於水平分表後分庫後,如何知道哪條數據在哪個庫裏或表裏,則需要路由算法進行計算,這個算法會引入一定的複雜性。

範圍路由

選取有序的數據列,如時間戳作爲路由的條件,不同分段分散到不同的數據庫表中,以最常見的用戶ID爲例,路由算法可以按照1000000的範圍大小進行分段,1 ~ 9999999放到數據庫1的表中,10000000~199999999放到數據庫2的表中,以此累推。
  範圍路由設計的複雜點主要體現在分段大小的選取上,分段太小會導致切分後子表數量過多增加維護複雜度,分段太大可能會導致單表依然存在性能問題,按一般大老們的經驗,分段大小100W至2000W之間,具體需要根據業務選 取合適的分段大小。

範圍路由的優點

  • 可以隨着數據的增加平滑地擴充新的表或庫,原有的數據不需要動。
  • 單表大小可控
  • 使用分片字段進行範圍查找時,連續分片可快速定位查詢,有效避免分片查詢的問題。
  • 熱點數據成爲性能瓶頸,連續分片可能存在數據熱點,例如按時單字段分片,有些分片存儲最近時間內的數據,可能會被頻繁讀寫,而有些歷史數據則很少被查詢。

hash算法

選取某個列或幾個列的值進行hash運算,然後根據hash的結果分散到不同的數據庫表中,以用ID爲例,假如我們一開始就規劃10個數據庫表,路由算法可以簡單地用id % 10的值來表示數據所屬的數據庫編號,ID爲985的用戶放到編號爲5的子表中。ID爲10086編號放到編號爲6的表中。
  Hash路由設計的複雜點主要體現 在初始表數量的選取上,表數量太多維護比較麻煩,表數量太小又可能導致單表性能存在問題。而用Hash路由後,增加字表數量是非常麻煩的,所有數據都要重新分佈。
  Hash路由的優缺點與範圍路由相反,Hash路由的優點是表分佈比較均勻,缺點是擴容時很麻煩,所有數據均需要重新分佈。

路由配置

配置路由就是路由表,用一張獨立的表來記錄路由信息。同樣以用戶ID爲例,我們新增一張ROUTER表,這個表包含table_Id兩列,根據user_id就可以查詢對應的修改路由表就可以了。
配置路由設計簡單,使用起來非常靈活,尤其是在擴充表的時候,只需要遷移指定的數據,然後修改路由表就可以了。
其缺點就是必須多查詢一次,會影響整體性能,而且路由表本身如果太大,性能會成爲瓶頸點,如果我們再將路由表分庫分表,則又面臨一個死循環。

分庫分錶帶來的問題

join操作

水平分表後,雖然物理上分散在多個表中,如果需要與其它表進行join查詢,需要在業務代碼或者數據庫中間件中進行多次join查詢,然後將結果合併。

COUNT(*)操作

水平分表後,某些場景下需要將這些表當作一個表來處理,那麼count(*)顯得沒有那麼容易 了。

order by 操作

分表後,數據分散到多個表中,排序操作無法在數據庫中完成,只能由業務代碼或數據中間件分別查詢每個子表中的數據,然後彙總進行排序。

整體的切分方式

一般就是垂直切分和水平切分,這是一種結果集描述的切分方式,是物理空間上的切分。 我們從面臨的問題,開始解決,闡述: 首先是用戶請求量太大,我們就堆機器搞定(這不是本文重點)。

然後是單個庫太大,這時我們要看是因爲表多而導致數據多,還是因爲單張表裏面的數據多。 如果是因爲表多而數據多,使用垂直切分,根據業務切分成不同的庫。

如果是因爲單張表的數據量太大,這時要用水平切分,即把表的數據按某種規則切分成多張表,甚至多個庫上的多張表。

切分方式小結

  • 垂直切分:把單一的表拆分成多個表。
  • 水平切分:根據表中數據的邏輯關係,將同一個表中的數據按照某種條件拆分到多臺數據庫(主機)上。

1. 垂直切分

垂直分表如果一張表的字段非常多,那麼很有可能會引起數據的跨頁存儲,這會造成數據庫額外的性能開銷,而垂直分表可以解決這個問題。垂直分表就是將一張表中不常用的字段拆分到另一張表中,從而保證第一章表中的字段較少,避免出現數據庫跨頁存儲的問題,從而提升查詢效率。而另一張表中的數據通過外鍵與第一張表進行關聯,如下圖所示。

也就是“大表拆小表”,基於列字段進行的。一般是表中的字段較多,將不常用的, 數據較大,長度較長(比如text類型字段)的拆分到“擴展表“。 一般是針對那種幾百列的大表,也避免查詢時,數據量太大造成的“跨頁”問題。

垂直拆分優點:

  1. 可以使得行數據變小,一個數據塊 (Block) 就能存放更多的數據,在查詢時就會減少 I/O 次數 (每次查詢時讀取的 Block 就少)。
  2. 可以達到最大化利用 Cache 的目的,具體在垂直拆分的時候可以將不常變的字段放一起,將經常改變的放一起。
  3. 便於實現動靜分離、冷熱分離的數據庫表的設計模式。
  4. 數據維護簡單。

垂直拆分缺點

  • 主鍵出現冗餘,需要管理冗餘列。
  • 會引起表連接 JOIN 操作(增加 CPU 開銷)可以通過在業務服務器上進行 join 來減少數據庫壓力,提高了系統的複雜度。。
  • 依然存在單表數據量過大的問題(需要水平拆分)。
  • 事務處理複雜。

垂直拆分小結:

系統層面的“服務化”拆分操作,能夠解決業務系統層面的耦合和性能瓶頸,有利於系統的擴展維護。而數據庫層面的拆分,道理也是相通的。與服務的“治理”和“降級”機制類似,我們也能對不同業務類型的數據進行“分級”管理、維護、監控、擴展等。

衆所周知,數據庫往往最容易成爲應用系統的瓶頸,而數據庫本身屬於“有狀態”的,相對於Web和應用服務器來講,是比較難實現“橫向擴展”的。數據庫的連接資源比較寶貴且單機處理能力也有限,在高併發場景下,垂直分庫一定程度上能夠突破IO、連接數及單機硬件資源的瓶頸,是大型分佈式系統中優化數據庫架構的重要手段。

然後,很多人並沒有從根本上搞清楚爲什麼要拆分,也沒有掌握拆分的原則和技巧,只是一味的模仿大廠的做法。導致拆分後遇到很多問題(例如:跨庫join,分佈式事務等)。

關於冷熱分離

垂直切分除了用於分解單庫單表的壓力,也用於實現冷熱分離,也就是根據數據的活躍度進行拆分,因爲對擁有不同活躍度的數據的處理方式不同。

冷熱分離是反範式的,因爲按照第三範式,有些字段是需要在一張表裏面的,但是冷熱分離這種策略會把字段分開。

例如,對配置表的某些字段很少進行修改時,將其放到一個查詢性能較高的數據庫硬件上;對配置表的其他字段更新頻繁時,則將其放到另一個更新性能較高的數據庫硬件上。

這裏我們再舉一個例子:在微博系統的設計中,一個微博對象包括文章標題、作者、分類、創建時間等屬性字段,這些字段的變化頻率低,查詢次數多,叫作冷數據。而博客的瀏覽量、回覆數、點贊數等類似的統計信息,或者別的變化頻率比較高的數據,叫作活躍數據或者熱數據。

我們把冷熱數據分開存放,就叫作冷熱分離,在MySQL的數據庫中,冷數據查詢較多,更新較少,適合用MyISAM引擎,而熱數據更新比較頻繁,適合使用InnoDB存儲引擎,這也是垂直拆分的一種。

我們推薦在設計數據庫表結構時,就考慮垂直拆分,根據冷熱分離、動靜分離的原則,再根據使用的存儲引擎的特點,對冷數據可以使用MyISAM,能更好地進行數據查詢;對熱數據可以使用InnoDB,有更快的更新速度,這樣能夠有效提升性能。

其次,對讀多寫少的冷數據可配置更多的從庫來化解大量查詢請求的壓力;對於熱數據,可以使用多個主庫構建分庫分表的結構,請參考下面關於水平切分的內容,後續的三四五章提供了不同的分庫分表的具體實施方案。

注意,對於一些特殊的活躍數據或者熱點數據,也可以考慮使用Memcache、Redis之類的緩存,等累計到一定的量後再更新數據庫,例如,在記錄微博點贊數量的業務中,點贊數量被存儲在緩存中,每增加1000個點贊,才寫一次數據。

2. 水平切分

如果一張表中的記錄數過多(超過1000萬條記錄),那麼會對數據庫的讀寫性能產生較大的影響,雖然此時仍然能夠正確地讀寫,但讀寫的速度已經到了業務無法忍受的地步,此時就需要使用水平分表來解決這個問題。水平分表是將一張含有很多記錄數的表水平切分,拆分成幾張結構相同的表。舉個例子,假設一張訂單表目前存儲了2000萬條訂單的數據,導致數據讀寫效率極低。此時可以採用水平分表的方式,將訂單表拆分成100張結構相同的訂單表,分別叫做 order_1__、order_2……order_100。

然後可以根據訂單所屬用戶的 ID 進行哈希取模後均勻地存儲在這100張表中,從而每張表中只存儲了20萬條訂單記錄,極大提升了訂單的讀寫效率,如下圖所示。

針對數據量巨大的單張表(比如訂單表),按照某種規則(時間,HASH取模等),切分到多張表裏面去。 但是這些表還是在同一個庫中,所以庫級別的數據庫操作還是有IO瓶頸。不建議採用。

水平拆分優點

  • 單庫單表的數據保持在一定的量級,有助於性能的提高。
  • 切分的表的結構相同,應用層改造較少,只需要增加路由規則即可。
  • 提高了系統的穩定性和負載能力。

水平拆分缺點

  • 切分後,數據是分散的,很難利用數據庫的Join操作,跨庫Join性能較差。
  • 分片事務的一致性難以解決。
  • 數據擴容的難度和維護量極大。

綜上所述,垂直切分和水平切分的共同點如下:

  • 存在分佈式事務的問題。
  • 存在跨節點Join的問題。
  • 存在跨節點合併排序、分頁的問題。
  • 存在多數據源管理的問題。

在瞭解這兩種切分方式的特點後,我們就可以根據自己的業務需求來選擇,通常會同時使用這兩種切分方式,垂直切分更偏向於業務拆分的過程,在技術上我們更關注水平切分的方案。

阿里開發手冊關於Join的問題

來自 阿里巴巴Java開發手冊,關於這句話的討論:https://www.zhihu.com/question/56236190

2. 【強制】超過三個表禁止join。需要join的字段,數據類型必須絕對一致;多表關聯查詢時,保證被關聯的字段需要有索引。
說明:即使雙表join也要注意表索引、SQL性能。

水平切分需要路由

既然數據已經水平切分,那麼數據就保存在不同的表中,如果有新的請求要求訪問數據庫,那麼怎麼從多個表中找到對應的數據呢?

答案就是:路由。

那麼問題來了,什麼是路由?

通過分庫分表規則查找到對應的表和庫的過程叫作路由。簡單的說,路由就是映射表,你想查什麼,告訴它,它會告訴你數據在哪。
例如,分庫分表的規則是user_id % 4,當用戶新註冊了一個賬號時,假設用戶的ID是123,我們就可以通過123 % 4 = 3確定此賬號應該被保存在User3表中。那麼以後當ID爲123的用戶登錄時,我們可通過123 % 4 = 3計算,確定其被記錄在User3中。

水平切分規則

規則有很多,具體情況具體分析,這裏只列舉常見的兩種

  1. 按哈希切分
  2. 按時間切分

1)按照哈希切片

數據水平切分後我們希望是一勞永逸或者是易於水平擴展的,所以推薦採用mod 2^n這種一致性Hash。

以統一訂單庫爲例,我們分庫分表的方案是32*32的,即通過UserId後四位mod 32分到32個庫中,同時再將UserId後四位Div 32 Mod 32將每個庫分爲32個表,共計分爲1024張表。線上部署情況爲8個集羣(主從),每個集羣4個庫。

優點:

切片均勻,對數據壓力分散效果較好

缺點

查詢需要聚合處理,較麻煩

2)按照時間切片

這種切片方式適用於有明顯時間特點的數據,例如,距離現在1個季度的數據訪問頻繁,距離現在兩個季度的數據可能沒有更新,距離現在3個季度的數據沒有查詢需求。

針對這種情況,可以通過按照時間進行切片,針對不同的訪問頻率使用不同檔次的硬件資源來節省成本:假設距離現在1個季度的數據訪問頻率最高,我們就用更好的硬件來運行這個分片;假設距離現在3個季度的數據沒有任何訪問需求,我們就可以將其整體歸檔,以方便DBA操作。

按時間切片的一個最大好處就是方便擴容,因爲它不需要任何的數據遷移。但是,連續分片有個最大的缺點就是熱點問題。按時間切片使得新插入的數據集中在同一個分片上,而往往新插入的數據讀寫頻率較高,因此,讀寫操作都會集中在最新的分片上,從而無法體現數據分片的優勢。

優點:單表大小可控,天然水平擴展。

缺點:無法解決集中寫入瓶頸的問題。

小結

在實際的生產實踐中,按照哈希切片和按照時間切片都是常用的分庫分表方式,並被廣泛使用,有時可以結合使用這兩種方式,例如:對交易數據先按照季度進行切片,然後對於某一季度的數據按照主鍵哈希進行切片。

分庫分表兩大模式

但是這麼多的分庫分表中間件全部可以歸結爲兩大類型:

  • CLIENT模式
  • PROXY模式

CLIENT模式代表有阿里的TDDL,開源社區的sharding-jdbc(sharding-jdbc的3.x版本即sharding-sphere已經支持了proxy模式)。架構如下:

PROXY模式代表有阿里的cobar,民間組織的MyCAT。架構如下:

但是,無論是CLIENT模式,還是PROXY模式。幾個核心的步驟是一樣的:SQL解析,重寫,路由,執行,結果歸併

分庫分表方案產品

應用層依賴類中間件

這類分庫分表中間件的特點就是和應用強耦合,需要應用顯示依賴相應的jar包(以Java爲例),比如知名的TDDL、噹噹開源的sharding-jdbc、蘑菇街的TSharding、攜程開源的Ctrip-DAL、支付寶開源但比較低調的zdal等。

此類中間件的基本思路就是重新實現JDBC的API,通過重新實現DataSource、PrepareStatement等操作數據庫的接口,讓應用層在基本(注意:這裏用了基本)不改變業務代碼的情況下透明地實現分庫分表的能力。中間件給上層應用提供熟悉的JDBC API,內部通過SQL解析、SQL重寫、SQL路由等一系列的準備工作獲取真正可執行的SQL,然後底層再按照傳統的方法(比如數據庫連接池)獲取物理連接來執行SQL,最後把數據結果合併處理成ResultSet返回給應用層。

此類中間件的優點很明顯,就是無需額外部署,只要和應用綁定一起發佈即可,但是缺點也很明顯,就是不能跨語言,比如Java寫的sharding-jdbc顯然不能用在C#項目中,所以攜程的dal也要重新寫一套C#的客戶端。

中間層代理類中間件

這類分庫分表中間件的核心原理是在應用和數據庫的連接之間搭起一個代理層,上層應用以標準的MySQL協議來連接代理層,然後代理層負責轉發請求到底層的MySQL物理實例,這種方式對應用只有一個要求,就是隻要用MySQL協議來通信即可,所以用MySQL Workbench這種純的客戶端都可以直接連接你的分佈式數據庫,自然也天然支持所有的編程語言。比較有代表性的產品有開創性質的Amoeba、阿里開源的Cobar、社區發展比較好的Mycat 等。

在技術實現上除了和應用層依賴類中間件基本相似外,代理類的分庫分表產品必須實現標準的MySQL協議,某種意義上講數據庫代理層轉發的就是MySQL協議請求,就像Nginx轉發的是Http協議請求。

上述無論哪種類型的產品,除了實現分庫分表這一主要功能外,都會額外實現一些其他很有實用價值的功能,比如讀寫分離、負載均衡等。

生成全局唯一ID

在很多中小項目中,我們往往直接使用數據庫自增特性來生成主鍵ID,這樣確實比較簡單。而在分庫分表的環境中,數據分佈在不同的分片上,不能再借助數據庫自增長特性直接生成,否則會造成不同分片上的數據表主鍵會重複。簡單介紹下使用和了解過的幾種ID生成算法。

方法有很多,本文總結以下方法

  1. 利用數據庫自增ID
  2. UUID
  3. 利用數據庫集羣並設置相應的步長(Flickr方案)
  4. 類Snowflake方案
  5. Redis生成ID

利用數據庫自增ID

以MySQL舉例,利用給字段設置auto_increment_increment和auto_increment_offset來保證ID自增,每次業務使用下列SQL讀寫MySQL得到ID號。

begin;
REPLACE INTO Tickets64 (stub) VALUES ('a');
SELECT LAST_INSERT_ID();
commit;

這種方案的優缺點如下:

優點:

  • 非常簡單,利用現有數據庫系統的功能實現,成本小,有DBA專業維護。
  • ID號單調自增,可以實現一些對ID有特殊要求的業務。

缺點:

  • 強依賴DB,當DB異常時整個系統不可用,屬於致命問題。配置主從複製可以儘可能的增加可用性,但是數據一致性在特殊情況下難以保證。主從切換時的不一致可能會導致重複發號。
  • ID發號性能瓶頸限制在單臺MySQL的讀寫性能。

UUID

UUID(Universally Unique Identifier)的標準型式包含32個16進制數字,以連字號分爲五段,形式爲8-4-4-4-12的36個字符,示例:550e8400-e29b-41d4-a716-446655440000,到目前爲止業界一共有5種方式生成UUID,詳情見IETF發佈的UUID規範 A Universally Unique IDentifier (UUID) URN Namespace

優點:

  • 性能非常高:本地生成,沒有網絡消耗。

缺點:

  • 不易於存儲:UUID太長,16字節128位,通常以36長度的字符串表示,很多場景不適用。
  • 信息不安全:基於MAC地址生成UUID的算法可能會造成MAC地址泄露,這個漏洞曾被用於尋找梅麗莎病毒的製作者位置。
  • ID作爲主鍵時在特定的環境會存在一些問題,比如做DB主鍵的場景下,UUID就非常不適用:

① MySQL官方有明確的建議主鍵要儘量越短越好[4],36個字符長度的UUID不符合要求。

All indexes other than the clustered index are known as secondary indexes. In InnoDB, each record in a secondary index contains the primary key columns for the row, as well as the columns specified for the secondary index. InnoDB uses this primary key value to search for the row in the clustered index. If the primary key is long, the secondary indexes use more space, so it is advantageous to have a short primary key.

② 對MySQL索引不利:如果作爲數據庫主鍵,在InnoDB引擎下,UUID的無序性可能會引起數據位置頻繁變動,嚴重影響性能。

利用數據庫集羣並設置相應的步長(Flickr方案)

flickr使用的一種主鍵生成測策略。
flickr這一方案的整體思想是:建立兩臺以上的數據庫ID生成服務器,每個服務器都有一張記錄各表當前ID的Sequence表,但是Sequence中ID增長的步長是服務器的數量,起始值依次錯開,這樣相當於把ID的生成散列到了每個服務器節點上。例如:如果我們設置兩臺數據庫ID生成服務器,那麼就讓一臺的Sequence表的ID起始值爲1,每次增長步長爲2,另一臺的Sequence表的ID起始值爲2,每次增長步長也爲2,那麼結果就是奇數的ID都將從第一臺服務器上生成,偶數的ID都從第二臺服務器上生成,這樣就將生成ID的壓力均勻分散到兩臺服務器上,同時配合應用程序的控制,當一個服務器失效後,系統能自動切換到另一個服務器上獲取ID,從而保證了系統的容錯。

優點:高可用、ID較簡潔。
缺點:需要單獨的數據庫集羣。

類Snowflake方案

這種方案大致來說是一種以劃分命名空間(UUID也算,由於比較常見,所以單獨分析)來生成ID的一種算法,這種方案把64-bit分別劃分成多段,分開來標示機器、時間等,比如在snowflake中的64-bit分別表示如下圖(圖片來自網絡)所示:

41-bit的時間可以表示(1L<<41)/(1000L*3600*24*365)=69年的時間,10-bit機器可以分別表示1024臺機器。如果我們對IDC劃分有需求,還可以將10-bit分5-bit給IDC,分5-bit給工作機器。這樣就可以表示32個IDC,每個IDC下可以有32臺機器,可以根據自身需求定義。12個自增序列號可以表示2^12個ID,理論上snowflake方案的QPS約爲409.6w/s,這種分配方式可以保證在任何一個IDC的任何一臺機器在任意毫秒內生成的ID都是不同的。

這種方式的優缺點是:

優點:

  • 毫秒數在高位,自增序列在低位,整個ID都是趨勢遞增的。
  • 不依賴數據庫等第三方系統,以服務的方式部署,穩定性更高,生成ID的性能也是非常高的。
  • 可以根據自身業務特性分配bit位,非常靈活。

缺點:

  • 強依賴機器時鐘,如果機器上時鐘回撥,會導致發號重複或者服務會處於不可用狀態。

應用舉例Mongdb objectID

MongoDB官方文檔 ObjectID可以算作是和snowflake類似方法,通過“時間+機器碼+pid+inc”共12個字節,通過4+3+2+3的方式最終標識成一個24長度的十六進制字符。

Redis生成ID

當使用數據庫來生成ID性能不夠要求的時候,我們可以嘗試使用Redis來生成ID。這主要依賴於Redis是單線程的,所以也可以用於生成全局唯一的ID。可以用Redis的原子操作 INCR和INCRBY來實現。

關於全局ID生成詳細可看

https://juejin.im/entry/57fe37eeda2f60004fb45723

https://tech.meituan.com/MT_Leaf.html

參考

https://dbaplus.cn/news-141-2017-1.html

http://www.cnblogs.com/405845829qq/p/7552736.html

https://mp.weixin.qq.com/s/DahF7Epx6MG95ZbxrMka2Q

https://gitbook.cn/books/5ab8a5ca6fa0783769f10ac1/index.html

https://tech.meituan.com/dianping_order_db_sharding.html

https://blog.csdn.net/KingCat666/article/details/78324678

http://www.ywnds.com/?p=7239

https://juejin.im/post/5bf778ef5188251b8a26ed8b

https://www.hollischuang.com/archives/681

https://zh.wikipedia.org/wiki/%E4%BA%8C%E9%98%B6%E6%AE%B5%E6%8F%90%E4%BA%A4

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章