日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

分布式系统概念 | 分布式ID:数据库、号段模式、雪花算法(Snowflake)、Redis实现方案

發布時間:2024/4/11 windows 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 分布式系统概念 | 分布式ID:数据库、号段模式、雪花算法(Snowflake)、Redis实现方案 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 分布式ID
  • 數據庫
    • 自增ID
    • 多主模式
  • 號段模式
  • 雪花算法
  • Redis
  • 總結


分布式ID

ID是數據的唯一標識,傳統的做法是使用數據庫的自增ID,但是隨著業務規模的不斷發展,數據量將越來越大,于是需要進行分庫分表,而分表后,每個表中的ID都會按自己的節奏進行自增,很有可能出現ID沖突的情況。這時就需要一個單獨的機制來負責生成一個全局唯一的ID。這個ID也可以叫做分布式ID

對于一個合格的分布式ID,它應該滿足以下幾項條件

  • 全局唯一
  • 高可用
  • 高性能
  • 趨勢遞增
  • 方便接入

下面就介紹幾種常見的分布式ID生成方案

數據庫

自增ID

我們可以利用數據庫的auto_increment自增ID來生成分布式ID,只需要一個數據庫實例即可完成。

建表如下

CREATE DATABASE `SEQID`;CREATE TABLE SEQID.SEQUENCE_ID (id bigint(20) unsigned NOT NULL auto_increment, stub char(10) NOT NULL default '',PRIMARY KEY (id),UNIQUE KEY stub (stub) ) ENGINE=MyISAM;

我們通過往該表中插入數據,并獲取到自增的主鍵id。為了考慮到并發的安全 ,我們可以通過事務來完成這個操作。

begin; insert into SEQUENCE_ID(value) VALUES ('values'); select last_insert_id(); commit;

我們可以看出,這種依賴單點的方法雖然實現起來簡單,但是這種依賴單點的方法并不可靠,因為Mysql并不能很好的支持高并發,當請求ID量特別大的時候就會因為單點的宕機而影響到整個業務系統。

多主模式

既然單點不可靠,那么我們可以考慮使用集群的方式來解決這個問題。考慮到單個主節點可能會因為宕機而沒有將數據同步到從節點,可能會導致ID重復的情況,因此我們可以考慮使用多主集群

為了防止多個主節點生成重復的ID,我們通常會通過控制初始值步長來避免這個問題。

例如在有兩個主節點的情況下,我們就可以通過這種方式來錯開ID。

-- 節點1 set @@auto_increment_offset = 1; -- 起始值 set @@auto_increment_increment = 2; -- 步長-- 節點2 set @@auto_increment_offset = 2; -- 起始值 set @@auto_increment_increment = 2; -- 步長

但是,這種方法的可拓展性不強,倘若我們要往集群中增加新的主節點時,我們就需要重新去設置步長,并且還需要根據前兩個節點的自增ID的大小來考慮我們新節點的起始值,而這些操作只能人工來進行。如果在修改步長的時候出現了重復的ID,此時就還需要進行停機修改。

號段模式

前面兩種方法的局限性在于其每次獲取ID時都需要直接訪問數據庫,效率較低,如果能夠一次獲取大量的ID,并將其緩存在本地,那樣就可以大大的提升ID獲取的效率,這也是號段模式的核心思想。

號段模式每次從數據庫中批量的獲取一段自增ID,即取出一個范圍的ID交給號段服務維護。例如(1,2000]即代表著2000個ID。我們的業務系統只需要到號段服務中申請ID,不需要每次都去請求數據庫,直到所有的ID都用完后才會去申請下一個號段。

數據庫表修改如下

CREATE TABLE id_generator (id int(10) NOT NULL,max_id bigint(20) NOT NULL COMMENT '當前最大id',step int(20) NOT NULL COMMENT '號段的步長',biz_type int(20) NOT NULL COMMENT '業務類型',version int(20) NOT NULL COMMENT '版本號',PRIMARY KEY (`id`) )

號段服務不再強依賴數據庫,即使數據庫不可用,號段服務也可以繼續工作直到申請的ID全部使用完。但是如果此時號段服務重啟,就會導致剩余的ID丟失。

為了保證號段服務的高可用,我們同樣需要建立一個集群,在請求方從號段服務獲取ID時,就會隨機的選取一個節點來獲取,而這種并發場景下我們同樣需要考慮到并發安全的問題,因此我們上面的表中也提供了一個版本號的字段version,我們可以使用樂觀鎖來進行并發的控制。

update id_generator set current_max_id=#{newMaxId}, version=version+1 where version = #{version}

我們可以使用上面的SQL來獲取新號段,當update更新成功就說明號段獲取成功了。

雪花算法

雪花算法(Snowflake) 是twitter開源的一個分布式ID的生成算法,它的核心思想是:生成一個long類型的ID,一個long大小8字節,一個字節8個比特位,因此它使用64個比特位來確定一個分布式ID。其中41bit代表時間戳,10bit標識一臺機器,剩下12bit則用來標識每個id

snowflake結構圖
  • 第1個bit位為符號位,因為生成的id通常為正數所以固定為0
  • 2~42位為時間戳部分,其精確至毫秒。同時為了更加合理的利用,其并不會存儲當前的時間,而是使用時間戳的差值(當前時間 - 固定的開始時間),這樣就可以保證ID從更小的起點開始生成。
  • 43~52位為工作機器的id,這里的計算會更加靈活,可以根據機房數量、機器數量來自行均衡,保證能夠利用到更多的機器。
  • 53~64位為序列編號,在同一毫秒中的同一臺機器上我們可以生成4096個ID

考慮到不同的業務場景以及各個公司的特性,大多數公司并不會去直接使用snowflake,而是會對其進行改造,讓其更貼合自身的使用場景。如百度的uid-generator、美團的Leaf、滴滴的TinyId等。

下面是github上開源的一個java實現的snowflake

/*** twitter的snowflake算法 -- java實現* github鏈接:https://github.com/beyondfengyu/SnowFlake* @author beyond* @date 2016/11/26*/ public class SnowFlake {/*** 起始的時間戳*/private final static long START_STMP = 1480166465631L;/*** 每一部分占用的位數*/private final static long SEQUENCE_BIT = 12; //序列號占用的位數private final static long MACHINE_BIT = 5; //機器標識占用的位數private final static long DATACENTER_BIT = 5;//數據中心占用的位數/*** 每一部分的最大值*/private final static long MAX_DATACENTER_NUM = -1L ^ (-1L << DATACENTER_BIT);private final static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);private final static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);/*** 每一部分向左的位移*/private final static long MACHINE_LEFT = SEQUENCE_BIT;private final static long DATACENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;private final static long TIMESTMP_LEFT = DATACENTER_LEFT + DATACENTER_BIT;private long datacenterId; //數據中心private long machineId; //機器標識private long sequence = 0L; //序列號private long lastStmp = -1L;//上一次時間戳public SnowFlake(long datacenterId, long machineId) {if (datacenterId > MAX_DATACENTER_NUM || datacenterId < 0) {throw new IllegalArgumentException("datacenterId can't be greater than MAX_DATACENTER_NUM or less than 0");}if (machineId > MAX_MACHINE_NUM || machineId < 0) {throw new IllegalArgumentException("machineId can't be greater than MAX_MACHINE_NUM or less than 0");}this.datacenterId = datacenterId;this.machineId = machineId;}/*** 產生下一個ID** @return*/public synchronized long nextId() {long currStmp = getNewstmp();if (currStmp < lastStmp) {throw new RuntimeException("Clock moved backwards. Refusing to generate id");}if (currStmp == lastStmp) {//相同毫秒內,序列號自增sequence = (sequence + 1) & MAX_SEQUENCE;//同一毫秒的序列數已經達到最大if (sequence == 0L) {currStmp = getNextMill();}} else {//不同毫秒內,序列號置為0sequence = 0L;}lastStmp = currStmp;return (currStmp - START_STMP) << TIMESTMP_LEFT //時間戳部分| datacenterId << DATACENTER_LEFT //數據中心部分| machineId << MACHINE_LEFT //機器標識部分| sequence; //序列號部分}private long getNextMill() {long mill = getNewstmp();while (mill <= lastStmp) {mill = getNewstmp();}return mill;}private long getNewstmp() {return System.currentTimeMillis();}public static void main(String[] args) {SnowFlake snowFlake = new SnowFlake(2, 3);for (int i = 0; i < (1 << 12); i++) {System.out.println(snowFlake.nextId());}} }


Redis

我們可以利用Redis中的incr命令來原子的獲取自增ID

127.0.0.1:6379> set seq_id 1 // 初始化自增ID OK 127.0.0.1:6379> incr seq_id // 自增并返回結果 (integer) 2

使用Redis實現起來特別簡單且高效,但是我們還需要考慮到持久化時帶來的一些問題

  • RDB持久化:由于其是定期保存一次數據庫的快照,為了保證效率他也存在著一定的時間間隔。倘若在我們剛保存一次快照后,連續獲取了幾次ID,而此時還沒來得及做下一次持久化就宕機了。當我們通過持久化重啟Redis后,這段時間生成的ID就會被重復使用。
  • AOF持久化:AOF相當于是邏輯日志,其會通過保存我們執行過的命令來進行持久化。它并不像RDB出現一段時間的數據丟失而導致的ID重復的情況,但是在它恢復的過程中需要重新執行保存的命令,因此隨著ID數量的增多,它重啟恢復數據的時間也會越來越慢。

總結

優點缺點
數據庫自增ID實現簡單,ID單調自增,數值類型查詢效率高單點問題,在高并發時可能會有宕機的風險
數據庫多主集群解決了單點問題,一定程度上提高了穩定性可拓展性不強,隨著業務規模的不斷擴大, 集群也會隨之增加,但是新主節點的加入較為麻煩,需要人工操作
號段模式不強依賴數據庫,提高了效率當為了保證高可用而使用多主集群時,仍然需要去修改起始值和步長
雪花算法1.可以根據業務特性自由分配比特位,較為靈活。2.不依賴第三方系統,獨立部署強依賴機器時鐘,如果出現時鐘回撥則會導致系統不可用
Redis實現起來簡單且高效持久化恢復存在問題,如RDB重復ID,AOF速度慢

總結

以上是生活随笔為你收集整理的分布式系统概念 | 分布式ID:数据库、号段模式、雪花算法(Snowflake)、Redis实现方案的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。