如何生成嚴格遞增的分布式id?


本文字數:2604

預計閱讀時間:15分鐘

01

引言

在現有分布式系統中,面對增長迅速的業務數據,id生成一直是非常重要的一環。而分布式系統的id生成方案需要滿足幾個重要特性:容錯高可用、高性能高并發、全局唯一。

02

技術背景

我們系統中最開始使用的是通過數據庫表生成對應的分布式id,數據庫中存一張sequence表,只有一行數據,記錄下一個id值。每次有新數據生成,都開啟事務加鎖查詢當前值,然后再更新字段值加1。

經過測試,該方案每次生成分布式id的平均耗時為1.46毫秒,而跨機房獲取id的平均耗時為5.32毫秒。

這種讀數據庫方案的弊端就很明顯:

  1. 當qps過高時,數據庫的壓力就會大大增加;

  2. 跨機房讀的耗時也會明顯升高;

  3. 如果使用框架自帶的邏輯,如Hibernate的strategy = GenerationType.TABLE策略,要求sequence表和數據表同數據源,所以當進行分庫后,新庫對id的獲取就會很麻煩;

  4. 有很小的概率會在生成id讀寫數據庫時,導致死鎖,新增數據無法入庫,我們就遇到過一次 - -!!!

當然這種方案有一個最大的好處:產生的id嚴格遞增,對我們業務來說,這個特性非常重要。

03

現有分布式方案分析

分布式id生成這么重要,市面上當然有很多的解決方案。下邊簡單介紹一下幾種常見的方案:

UUID

介紹:UUID是一個由32個十六進制數字組成,中間由橫杠分割(例:372f3ba5-6359-4f1f-9184-a938a4908072),java中可以直接調用UUID.randomUUID()實現,也可以使用特定算法生成。

優點:實現簡單,UUID的生成非常簡單,不需要依賴于任何外部資源;實際應用中基本不會遇到重復的情況。

缺點:UUID長度較長,占用的存儲空間較大,且可讀性差;算法實現復雜,經測試存在效率問題。在數據庫作為主鍵時,可能會影響寫入性能;不是遞增的。

雪花算法

介紹:SnowFlake是 Twitter 開源的分布式 id 生成算法,可以不用依賴任何第三方工具進行自增的數字類型的id生成;雪花算法的核心邏輯是使用一個 64 bit 的 long 型的數字作為全局唯一id。

雪花算法生成的唯一ID均為正數,所以這 64 個 bit 中,其中 1 個 bit 是不用的(第一個 bit 默認都是 0),然后用 41 bit 作為毫秒數,用 10 bit 作為工作機器 id,12 bit 作為序列號。

優點:實現簡單,不依賴其他第三方庫;高效,雪花算法能以極高的速度生成ID,每秒可生成數百萬個,滿足高并發場景的需求。

缺點:時間回撥問題;生成的id長度比較長;機器碼不同,生成的id也不同,跟歷史id相比變化比較大;生成的id趨勢遞增。

百度uid-generator框架

介紹:百度UidGenerator是基于snowflake算法思想實現的,但與原始算法不同的地方在于,UidGenerator支持自定義時間戳、工作機器id(workId)以及序列號等各個組成部分的位數,并且工作機器id采用用戶自定義的生成策略。百度uid-generator有兩種實現方式:DefaultUidGenerator和CachedUidGenerator。從性能和時間回撥問題考慮,一般都是考慮CachedUidGenerator類實現。

優點:性能好,每秒可生成數百萬個id;簡單易用,現成jar包直接接入,包括獲取當前時間戳、數據中心id和機器id。支持多種部署方式,包括單機模式和分布式模式。

缺點:默認接入mybatis框架,否則需要自己重寫dao層;生成的id趨勢遞增。

美團leaf框架

介紹:美團的leaf框架有兩種模式。一種是雪花算法模式,也是基于雪花算法這里不再贅述。另一種模式是號段模式。號段模式:每次從數據庫中取一個號段的id值,號段由step步長決定。然后把號段放在內存中。當號碼使用到一定范圍時,則更新到下一號段。

優點:每次取一個號段的id,大大減少了對數據庫的讀寫,減輕了數據庫壓力;不同的機器存在不同的號段,放在內存中,速度快效率高,只需要考慮本機的線程安全問題。

缺點:如果有多臺機器提供服務,那么每臺機器生成的號段不同,只能保證趨勢遞增;如果有一臺服務器,對于業務請求量巨大時,單臺服務器可能會扛不住壓力,服務器宕機就會使獲取id服務不可用。

當然除了上述方案,還有其他的分布式id生成方案:比如zookeeper的順序節點,滴滴的Tinyid框架,這里就不一一列舉。

04

嚴格遞增的分布式id生成方案

上述中的那些方案,可以看到除了使用zk的順序節點,其他都是只能保證趨勢遞增,并不能保證嚴格遞增(后請求的數據id,一定比先請求的數據id大)。對于我們業務來說,嚴格遞增id非常有必要,而使用zk又需要維護一套高可用的zk集群。所以學習前人們的解決方案之后,誕生了我們自己的分布式id生成方案。

方案介紹

采用的是?數據庫號段模式?加?緩存?加?監聽?的方案,有兩種id生成模式:

  1. 使用緩存生成;

  2. 使用數據庫表生成。

具體工具可以自行選擇。這里使用的是mysql + redis + nacos。

mysql中創建一張sequence表,主要字段:bizId: 業務表示,區分不同業務的id;maxId:目前號段最大的Id值;step:步長,每個號段包含的id個數。

redis中也需要存儲三個key:currentId:當前已經使用到的id值;maxId:當前號段的最大id值,step:步長。

nacos開關的作用:控制是否id生成模式,打開:使用redis生成模式,關閉:使用數據庫表生成模式;同時監聽nacos開關,控制生成模式自動切換。

實現流程:

項目啟動時如果nacos開關打開,檢測redis中是否存在當前業務id相關的key,如果沒有則讀取數據庫加載到redis中(注意先更新到下一號段)

當有新的寫入請求時:

  1. 首先判斷 redis心跳檢測正常 且 nacos開關打開,則是緩存生成模式:

    1. 直接讀取redis中對應currentId,通過incr()方法獲取到下一個id值;

    2. 此時檢查id時否合理,合理閾值可以自行設置。如果不合理則調用 數據庫同步redis流程(先把數據庫更新到下一號段,然后再更新redis中的值:currentId = 原maxId + step*0.1, 新maxId = 原maxId + step);

    3. 生成id后,異步線程判斷:當前id是否已經使用了當前號段的百分之30,如果超過則更新獲取下一號段。

  2. 否則:使用數據庫生成模式,直接讀取數據庫sequence表把maxId字段作為當前id使用,maxId字段先加鎖查詢,然后再更新加1。

當有特殊情況時:

  1. redis集群不可用,通過心跳任務檢測出狀態不對,則直接調用api關閉開關。關閉后就是使用數據庫模式生成id,雖然耗時有所增加,但增加量不多且可以保證業務流程不阻塞;

  2. 處理好redis問題后,修改naocs開關,程序監聽到開關打開事件,則從數據庫模式改為緩存模式生成:先更新下一號段避免id重復,然后把新更新的值寫入redis中,下次請求就繼續開始使用redis生成id。

總結:

這個方案使用redis來生成Id,主要是因為redis作為強大的中間件基本所有項目都會用到,隨處可見,不用再引入新的第三方依賴;其次redis的自增自帶原子性,生成的id是嚴格遞增。

并且redis可以很好的抗住高QPS請求,經測試id的獲取絕大部分小于等于1毫秒。

為了防止redis集群抽瘋不可用,準備了數據庫生成方案:直接讀取數據庫中的sequence表,查詢并更新maxId字段加1。這樣可以保證業務的正常運行,耗時平均漲幾毫秒,屬于可接受范圍。在使用數據模式生成期間,就可以著手處理redis集群的問題,處理完后通過監聽開關打開事件,再重新切換到緩存生成模式,繼續生成嚴格遞增的分布式id。(為了防止重復,先更新數據庫到下一號段,把新值更新到redis中)

附流程圖:

05

結論

這個方案主要是通過redis的自增來高效生成嚴格遞增的id,可以用其他中間件代替。這個方案重要的是不只依賴于redis,還要對redis不可用的情況進行兜底檢測,形成一個自動切換的閉環。

經測試該方案性能,相比于之前直接查詢更新數據庫sequence表方案,同機房獲取id性能提升接近10倍,跨機房獲取id性能提升接近7倍。

同時該方案也解決了之前遇到過的數據庫sequence表死鎖,導致業務數據無法新增入庫的問題。

當然如果業務需求并不要求id嚴格遞增,那么上邊介紹的優秀的框架都可以使用。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/95651.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/95651.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/95651.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【LeetCode】二叉樹相關算法題

目錄1、二叉樹介紹【1】核心概念【2】關鍵特性2、算法題【1】二叉樹的前序遍歷【2】二叉樹的后序遍歷1、二叉樹介紹 【1】核心概念 結構含義節點結構二叉樹由節點組成, 每個節點包含一個數據元素和最多兩個子節點:左子節點和右子節點根節點樹的頂部節點…

Vulnhub Deathnote靶機復現攻略

一、靶機安裝 下載地址:https://download.vulnhub.com/deathnote/Deathnote.ova 下載好后使用VB打開,配置如下 二、主機發現 使用相同連接方式的kali進行后續操作(172.16.2.7)根據mac地址進行確認。 nmap -sn 172.16.2.1/24 三、端口掃描 端口開放了…

DevEco Studio 6.0.0 元服務頁面跳轉失敗

背景,我使用最新的編輯器DevEco Studio 6.0.0,編寫一個元服務,發現使用跳轉頁面的時候失敗了!然后查看官方文檔,兩種方式都測試了,發現都不行。 方法1:Navigation路由跳轉無效,見官方…

docker重啟或系統重啟后harbor自動啟動

docker重啟或系統重啟后harbor自動啟動docker重啟或系統重啟后harbor自動啟動方法 1:在 docker-compose.yml 中配置重啟策略(推薦)方法 2:創建 Systemd 服務(更可靠)方法 3:使用 Docker 的 Rest…

OpenZeppelin Contracts 架構分層分析

OpenZeppelin Contracts 是一個面向以太坊(及兼容 EVM 的區塊鏈)生態系統的??模塊化、安全性優先、標準兼容的智能合約庫??。其內部代碼按照功能職責與抽象層級,可系統性地劃分為多個邏輯層次。理解這些層次及其依賴關系,對于…

Java-JVM的內存模型

一.JVM內存模型JVM內存模型可以從進程生命周期和線程生命周期1.線程生命周期每個線程都會有自己各自一份數據,不會存在線程安全問題1.程序計數器指示當前線程執行的字節碼指令的行號,以便線程執行時可以回到正確的位置2.虛擬機棧線程私有的,與…

Highcharts Dashboards | 打造企業級數據儀表板:從圖表到數據駕駛艙

企業日常決策、產品運營、業務監控,越來越依賴數據驅動。而儀表板(Dashboard)作為匯總展示多維度信息的“數據駕駛艙”,已成為企業可視化的核心場景之一。如果你正在尋找一款能夠快速、靈活、安全構建儀表板的前端圖表工具&#x…

基于Java的Markdown轉Word工具(標題、段落、表格、Echarts圖等)

項目源于我們開發的一款基于大模型的報告生成工具。由于需要將 Markdown 格式的內容導出為 Word 文檔,而市面上缺乏合適的現成工具,所以決定自己開發一個Markdown轉Word的工具。 🩷源碼地址:daydayup-zyn/md2doc-plus &#x1f…

Unity:PlayerPrefs筆記

寫在前面:寫本系列(自用)的目的是回顧已經學過的知識、記錄新學習的知識或是記錄心得理解,方便自己以后快速復習,減少遺忘。一、PlayerPrefs的基本方法1、存儲相關PlayerPrefs的數據存儲類似于鍵值對存儲,一個鍵對應一個值。Unity…

SQL tutorials

SQL Literature SQL運行在資料庫管理系統(Database Management System),如MySQL,Postgre SQL,Microsoft SQL Server, Oracle,etc。 SQL練習平臺:https://sqliteviz.com/ EXAMPLE SQL…

MySQL快速恢復數據的N種方案完全教程

目錄 1. 理解MySQL數據恢復的核心邏輯 1.1 數據丟失的常見場景 1.2 MySQL的“救命稻草”:關鍵文件和機制 2. 方案一:利用全量備份+binlog實現點對點恢復 2.1 準備工作 2.2 恢復步驟 2.3 實戰案例 3. 方案二:利用InnoDB的崩潰恢復機制 3.1 崩潰恢復的原理 3.2 恢復步…

雙屏加固筆記本電腦C156-2:堅固與高效的完美融合

在當今數字化時代,筆記本電腦已成為人們工作和生活中不可或缺的工具。然而,對于一些特殊行業和惡劣環境下的應用場景,普通筆記本電腦往往難以滿足需求。此時,具備堅固耐用、高性能等特點的加固筆記本電腦應運而生。魯成偉業的雙屏…

Jenkins 環境部署

下載相關軟件:Jenkins 的安裝和設置 相關工具: Git : Git - Downloads java 17: Java Archive Downloads - Java SE 17.0.12 and earlier python : Download Python | Python.org jenkins、jenkins.war : Jenkins 的安裝和設置 將所有軟件安裝后&am…

如何高效解決 Java 內存泄漏問題方法論

目錄 一、系統化的診斷與優化方法論 二、獲取內存快照:內存泄漏的第一步 (一)自動生成 Heap Dump (二)手動生成 Heap Dump 三、導入分析工具:MAT 和 JProfiler (一)MAT (Memory Analyzer Tool) (二)JProfiler (三)自身企業工具 四、深入分析:逐步排查內存…

HarmonyOS Camera Kit 全解析:從基礎拍攝到跨設備協同的實戰指南

在移動應用開發中,相機功能往往是提升用戶體驗的關鍵模塊,但傳統相機開發面臨權限管理復雜、設備兼容性差、功能實現繁瑣等痛點。HarmonyOS 作為面向全場景的分布式操作系統,其 Camera Kit(相機服務)通過統一的 API 接…

運用詞向量模型分辨評論

代碼實現:import jieba import pandas as pd hp pd.read_table(優質評價.txt,encodinggbk) cp pd.read_table(差評1.txt,encodinggbk) cp_segments [] contents cp.content.values.tolist() for content in contents:results jieba.lcut(content)if len(result…

基于Apache Flink的實時數據處理架構設計與高可用性實戰經驗分享

基于Apache Flink的實時數據處理架構設計與高可用性實戰經驗分享 一、業務場景描述 在現代電商平臺中,實時用戶行為數據(點擊、瀏覽、購物車操作等)對業務決策、個性化推薦和風控都至關重要。我們需要搭建一個高吞吐、低延遲且具備高可用性的…

第二十四天:虛函數與純虛函數

虛函數(Virtual Function) 定義:在基類中使用 virtual 關鍵字聲明的成員函數,允許在派生類中被重新定義(覆蓋,override)。其目的是實現多態性,即通過基類指針或引用調用函數時&#…

uniapp微信小程序-登錄頁面驗證碼的實現(springboot+vue前后端分離)EasyCaptcha驗證碼 超詳細

一、項目技術棧登錄頁面暫時涉及到的技術棧如下:前端 Vue2 Element UI Axios,后端 Spring Boot 2 MyBatis MySQL Redis EasyCaptcha JWT Maven后端使用IntelliJ IDEA 2024.3.5 前端使用 HBuilder X 和 微信開發者工具二、實現功能及效果圖過期管理驗證碼有…

【Java】HashMap的詳細介紹

目錄 一.HashMap 1.基本概念 2.底層數據結構: 3.HashCode和equals方法 為什么重寫HashCode方法? 為什么重新equals方法? 4.put操作 1.初始化和數組檢查 2.計算索引并檢查桶是否為空 3.桶不為null,處理哈希沖突 4.判斷鏈…