1. 程式人生 > >如何在高併發分散式系統中生成全域性唯一Id

如何在高併發分散式系統中生成全域性唯一Id

又一個多月沒冒泡了,其實最近學了些東西,但是沒有安排時間整理成博文,後續再奉上。最近還寫了一個發郵件的元件以及效能測試請看 《NET開發郵件傳送功能的全面教程(含郵件元件原始碼)》 ,還弄了個MSSQL引數化語法生成器,會在9月整理出來,有興趣的園友可以關注下我的部落格。

分享原由,最近公司用到,並且在找最合適的方案,希望大家多參與討論和提出新方案。我和我的小夥伴們也討論了這個主題,我受益匪淺啊……

博文示例:

今天分享的主題是:如何在高併發分散式系統中生成全域性唯一Id

但這篇博文實際上是“半分享半討論”的博文:

1)半分享是我將說下我所瞭解到的關於今天主題所涉及的幾種方案。

2)半討論是我希望大家對各個方案都說說自己的見解,更加希望大家能提出更好的方案。(我還另外提問在此:,若你們有見解和新方案就在本博文留言吧,方便我整理更新到博文中,謝謝!)

我瞭解的方案如下……………………………………………………………………

1、使用資料庫自增Id

優勢:編碼簡單,無需考慮記錄唯一標識的問題。

缺陷:

1)在大表做水平分表時,就不能使用自增Id,因為Insert的記錄插入到哪個分表依分表規則判定決定,若是自增Id,各個分表中Id就會重複,在做查詢、刪除時就會有異常。

2)在對錶進行高併發單記錄插入時需要加入事物機制,否則會出現Id重複的問題。

3)在業務上操作父、子表(即關聯表)插入時,需要在插入資料庫之前

獲取max(id)用於標識父表和子表關係,若存在併發獲取max(id)的情況,max(id)會同時被別的執行緒獲取到。

4)等等。

結論:適合小應用,無需分表,沒有高併發效能要求。

2、單獨開一個數據庫,獲取全域性唯一的自增序列號或各表的MaxId

1)使用自增序列號表

專門一個數據庫,生成序列號。開啟事物,每次操作插入時,先將資料插入到序列表並返回自增序列號用於做為唯一Id進行業務資料插入。

注意:需要定期清理序列表的資料以保證獲取序列號的效率;插入序列表記錄時要開啟事物。

使用此方案的問題是:每次的查詢序列號是一個性能損耗;如果這個序列號列暴了,那就杯具了,你不知道哪個表使用了哪個序列,所以就必須換另一種唯一

Id方式如GUID

2)使用MaxId表儲存各表的MaxId

專門一個數據庫,記錄各個表的MaxId值,建一個儲存過程來取Id,邏輯大致為:開啟事物,對於在表中不存在記錄,直接返回一個預設值為1的鍵值,同時插入該條記錄到table_key表中。而對於已存在的記錄,key值直接在原來的key基礎上加1更新到MaxId表中並返回key

使用此方案的問題是:每次的查詢MaxId是一個性能損耗;不過不會像自增序列表那麼容易列暴掉,因為是擺表進行劃分的。

我擷取此文中的sql語法如下:

第一步:建立表
create table table_key
(
       table_name   varchar(50) not null primary key,
       key_value    int         not null
)


第二步:建立儲存過程來取自增ID
create procedure up_get_table_key
(
   @table_name     varchar(50),
   @key_value      int output
)
as
begin
     begin tran
         declare @key  int
         
         --initialize the key with 1
         set @key=1
         --whether the specified table is exist
         if not exists(select table_name from table_key where [email protected]_name)
            begin
              insert into table_key values(@table_name,@key)        --default key vlaue:1
            end
         -- step increase
         else    
            begin
                select @key=key_value from table_key with (nolock) where [email protected]_name
                set @[email protected]+1
                --update the key value by table name
                update table_key set [email protected] where [email protected]_name
            end
        --set ouput value
    set @[email protected]

    --commit tran
    commit tran
        if @@error>0
      rollback tran
end

感謝園友的好建議:

1.@_)建議給table_key中為每個表初始化一條key1的記錄,這樣就不用每次if來判斷了。

2.程式碼層上使用如下事物程式碼會導致併發重複問題.

TransactionOptions option = new TransactionOptions();
option.IsolationLevel = IsolationLevel.ReadUncommitted;
option.Timeout = new TimeSpan(0, 10, 0);
 
using (TransactionScope transaction = new TransactionScope(TransactionScopeOption.RequiresNew, option))
{
        //呼叫儲存過程
}

在諮詢過DBA後,這個儲存過程提高資料庫隔離級別會加大資料庫訪問壓力,導致響應超時問題。所以這個建議我們只能在程式碼編寫宣導上做

3.@土豆烤肉)儲存過程中不使用事物,一旦使用到事物效能就急劇下滑。直接使用UPDATE獲取到的更新鎖,即SQL SERVER會保證UPDATE的順序執行。(已在使用者過千萬的併發系統中使用)

create procedure [dbo].[up_get_table_key]
(
   @table_name     varchar(50),
   @key_value      int output
)
as
begin

	SET NOCOUNT ON;
	DECLARE @maxId INT
	UPDATE table_key
	SET @maxId = key_value,key_value = key_value + 1 
	WHERE [email protected]_name
	SELECT @maxId

end

結論:適用中型應用,此方案解決了分表,關聯表插入記錄的問題。但是無法滿足高併發效能要求。同時也存在單點問題,如果這個資料庫cash掉的話……

我們目前正頭痛這個問題,因為我們的高併發常常出現數據庫訪問超時,瓶頸就在這個MaxId表。我們也有考慮使用分散式快取(egmemcached)快取第一次訪問MaxId表資料,以提高再次訪問速度,並定時用快取資料更新一次MaxId表,但我們擔心的問題是:

a)倘若快取失效或暴掉了,那快取的MaxId沒有更新到資料庫導致資料丟失,必須停掉站點來執行Select max(id)各個表來同步MaxId表。

b)分散式快取不是一儲存下去,其他伺服器上就立馬可以獲取到的,即資料存在不確定性。(其實也是快取的一個誤用,快取應該用來存的是頻繁訪問並且很少改動的內容)

改進方案:

整體思想:建立兩臺以上的資料庫ID生成伺服器,每個伺服器都有一張記錄各表當前IDMaxId表,但是MaxId表中Id的增長步長是伺服器的數量,起始值依次錯開,這樣相當於把ID的生成雜湊到每個伺服器節點上。例如:如果我們設定兩臺資料庫ID生成伺服器,那麼就讓一臺的MaxId表的Id起始值為1(或當前最大Id+1),每次增長步長為2,另一臺的MaxId表的ID起始值為2(或當前最大Id+2),每次步長也為2。這樣就將產生ID的壓力均勻分散到兩臺伺服器上,同時配合應用程式控制,當一個伺服器失效後,系統能自動切換到另一個伺服器上獲取ID,從而解決的單點問題保證了系統的容錯。(Flickr思想)

但是要注意:1、多伺服器就必須面臨負載均衡的問題;2、倘若新增新節點,需要對原有資料重新根據步長計算遷移資料。

相關推薦

如何在併發分散式系統生成全域性唯一Id

又一個多月沒冒泡了,其實最近學了些東西,但是沒有安排時間整理成博文,後續再奉上。最近還寫了一個發郵件的元件以及效能測試請看 《NET開發郵件傳送功能的全面教程(含郵件元件原始碼)》 ,還弄了個MSSQL引數化語法生成器,會在9月整理出來,有興趣的園友可以關注下我的部落格。 分享原由,最近公司用到,並

併發分散式環境獲取全域性唯一ID[分散式資料庫全域性唯一主鍵生成]

需求說明 在過去單機系統中,生成唯一ID比較簡單,可以使用mysql的自增主鍵或者oracle中的sequence, 在現在的大型高併發分散式系統中,以上策略就會有問題了,因為不同的資料庫會部署到不同的機器上,一般都是多主例項,而且再加上高併發的話,就會有重複

分散式系統應用生成全域性唯一ID的演算法(snowflake)----java 實現,單例模式

概述 在分散式系統中,有很多的地方需要生成全域性id的場景,比方說,訂單模組,使用者id等。這種情況下大多數的做法是通過UUID來做處理。首先,UUID是36位的一個字串,相對來說是比較長的,一般我們採用的資料庫會是MySQL,因為大多數的情況下,我們都希望我們的資料是可以

併發分散式系統如何做到唯一Id

又一個多月沒冒泡了,其實最近學了些東西,但是沒有安排時間整理成博文,後續再奉上。最近還寫了一個發郵件的元件以及效能測試請看 《NET開發郵件傳送功能的全面教程(含郵件元件原始碼)》 ,還弄了個MSSQL引數化語法生成器,會在9月整理出來,有興趣的園友可以關注下我的部落格。 分享原由,最近公司用到,並且在找最

分散式儲存生成全域性唯一ID的幾種方案

1.自定義生成規則 eg: 3位伺服器編碼+15位年月日時分秒毫秒+3位表編碼+4位隨機碼 (這樣就完全單機完成編碼任務)---共25位 3位伺服器編碼+15位年月日時分秒毫秒+3位表編碼+4流水碼 (這樣流水碼就需要結合資料庫和快

go分散式生成全域性唯一ID

因為snowFlake目的是解決分散式下生成唯一id 所以ID中是包含叢集和節點編號在內的 const ( numberBits uint8 = 12 // 表示每個叢集下的每個節點,1毫秒內可生成的id序號的二進位制位 對應上圖中的最後一段 workerBits uint8 = 10

併發分散式系統架構——Nginx

Nginx介紹 Nginx是一款輕量級的Web 伺服器/反向代理伺服器及電子郵件(IMAP/POP3)代理伺服器,並在一個BSD-like 協議下發行。其特點是佔有記憶體少,併發能力強,事實上nginx的併發能力確實在同類型的網頁伺服器中表現較好,中國大

雪花演算法(snowflake) :分散式環境,生成全域性唯一的訂單號

準備 apache.commons.lang3包 <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3&l

Spring Boot教你一種方法生成全域性唯一ID

一、概述 流水號生成器(全域性唯一 ID生成器)是服務化系統的基礎設施,其在保障系統的正確執行和高可用方面發揮著重要作用。而關於流水號生成演算法首屈一指的當屬 Snowflake雪花演算法,然而 Snowflake本身很難在現實專案中直接使用,因此實際應用時需要一種可落地的方案。 UidGe

生成全域性唯一ID的3個思路

標識(ID / Identifier)是無處不在的,生成標識的主體是人,那麼它就是一個命名過程,如果是計算機,那麼它就是一個生成過程。如何保證分散式系統下,並行生成標識的唯一與標識的名稱空間有著密不可分的關係。在世界裡,「潛意識下的名稱空間裡,相對的唯一標識」是普遍存在的

生成全域性唯一ID的3個思路,來自一個資深架構師的總結

標識(ID / Identifier)是無處不在的,生成標識的主體是人,那麼它就是一個命名過程,如果是計算機,那麼它就是一個生成過程。如何保證分散式系統下,並行生成標識的唯一與標識的名稱空間有著密不可分的關係。在世界裡,「潛意識下的名稱空間裡,相對的唯一標識」是

golang 實現Twitter snowFlake演算法 高效生成全域性唯一ID

最近在著手準備一個H5遊戲  因為這是我第一次接觸遊戲這個類目  即使量不大也想好好的做它一番  在設計表結構的時候想到了表全域性唯一id這個問題  既然是遊戲  那麼一定是多人線上點點點(運營理想狀態 哈哈哈)  一開始想使用mongoDB的objectId來作為全域性唯一

Snowflake生成全域性唯一ID的改進

開發十年,就只剩下這套架構體系了! >>>   

分散式全域性唯一ID生成策略

為什麼分散式系統需要用到ID生成系統 在複雜分散式系統中,往往需要對大量的資料和訊息進行唯一標識。如在美團點評的金融、支付、餐飲、酒店、貓眼電影等產品的系統中,資料日漸增長,對資料庫的分庫分表後需要有一個唯一ID來標識一條資料或訊息,資料庫的自增ID顯然不能滿足需求;特別一點的如訂單、騎

Java架構-在一個成熟的分散式系統 如何下手做可用?

對於企業來說,隨著規模越來越大,整個系統中存在越來越多的子系統,每個子系統又被多個其他子系統依賴或者依賴於其他子系統。大部分系統在走到這一步的過程中,大概率會發生這樣的場景:作為某個子系統的負責人或者 OnCall 人員,休息的時候都不安穩,心裡老是忐忑著系統會不會掛。導致週末不敢長時間

基於SOA的併發可用分散式系統架構和元件詳解

基於SOA的分散式高可用架構和微服務架構,是時下如日中天的網際網路企業級系統開發架構選擇方案。在核心思想上,兩者都主張對系統的橫向細分和擴充套件,按不同的業務功能模組來對系統進行分割並且使用一定的手段實現服務之間的通訊,並且基於彈性雲服務搭建高可用的分散式解決方案。 但它們之間的區別可能比相似的地方要多,特別

併發下使用Redis生成唯一id

最近使用spirngcloud來搭建分散式專案,遇到插入重複問題,決定用redis生成唯一ID來解決。 /** * 獲取唯一Id * @param key *

不懂這些併發分散式架構、分散式系統的資料一致性解決方案,你如何能找到高新網際網路工作呢?強勢解析eBay BASE模式、去哪兒及蘑菇街分散式架構

網際網路行業是大勢所趨,從招聘工資水平即可看出,那麼如何提升自我技能,滿足網際網路行業技能要求?需要以目標為導向,進行技能提升,本文主要針對高併發分散式系統設計、架構(資料一致性)做了分析,祝各位早日走上屬於自己的"成金之路"。 目錄:問題分析概念解讀Most Simple原理解讀eBey、去哪兒、蘑菇街分

分散式系統負載均衡演算法在可用場景下的分析

在分散式系統中,負載均衡是非常重要的環節,通過負載均衡將請求派發到網路中的一個或多個節點上進行處理。通常來說,負載均衡分為硬體負載均衡及軟體負載均衡。硬體負載均衡,顧名思義,在伺服器節點之間安裝專門的硬體進行負載均衡的工作,F5便為其中的佼佼者。軟體負載均衡則是通過在伺服器上安裝的特定的負載均衡軟體或是自

常見分散式全域性唯一ID生成策略及演算法的對比

全域性唯一的 ID 幾乎是所有系統都會遇到的剛需。這個 id 在搜尋, 儲存資料, 加快檢索速度 等等很多方面都有著重要的意義。工業上有多種策略來獲取這個全域性唯一的id,針對常見的幾種場景,我在這裡進行簡單的總結和對比。 簡單分析一下需求 [1] 所謂全域性唯一的