国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

分布式ID解決方案對比

這篇具有很好參考價值的文章主要介紹了分布式ID解決方案對比。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

在復雜的分布式系統(tǒng)中,往往需要對大量的數據進行唯一標識,比如在對一個訂單表進行了分庫分表操作,這時候數據庫的自增ID顯然不能作為某個訂單的唯一標識。除此之外還有其他分布式場景對分布式ID的一些要求:

  • 趨勢遞增:?由于多數RDBMS使用B-tree的數據結構來存儲索引數據,在主鍵的選擇上面我們應該盡量使用有序的主鍵保證寫入性能。

  • 單調遞增:?保證下一個ID一定大于上一個ID,例如排序需求。

  • 信息安全:?如果ID是連續(xù)的,惡意用戶的扒取工作就非常容易做了;如果是訂單號就更危險了,可以直接知道我們的單量。所以在一些應用場景下,會需要ID無規(guī)則、不規(guī)則。

就不同的場景及要求,市面誕生了很多分布式ID解決方案。本文針對多個分布式ID解決方案進行介紹,包括其優(yōu)缺點、使用場景及代碼示例。

一、UUID?

UUID(Universally Unique Identifier)是基于當前時間、計數器(counter)和硬件標識(通常為無線網卡的MAC地址)等數據計算生成的。包含32個16進制數字,以連字號分為五段,形式為8-4-4-4-12的36個字符,可以生成全球唯一的編碼并且性能高效。

JDK提供了UUID生成工具,代碼如下:

import java.util.UUID;

public class Test {
    public static void main(String[] args) {
        System.out.println(UUID.randomUUID());
        // 輸出:b0378f6a-eeb7-4779-bffe-2a9f3bc76380
    }
}

UUID完全可以滿足分布式唯一標識,但是在實際應用過程中一般不采用,有如下幾個原因:

  • 存儲成本高:?UUID太長,16字節(jié)128位,通常以36長度的字符串表示,很多場景不適用。

  • 信息不安全:?基于MAC地址生成的UUID算法會暴露MAC地址,曾經梅麗莎病毒的制造者就是根據UUID尋找的。

  • 不符合MySQL主鍵要求:?MySQL官方有明確的建議主鍵要盡量越短越好,因為太長對MySQL索引不利:如果作為數據庫主鍵,在InnoDB引擎下,UUID的無序性可能會引起數據位置頻繁變動,嚴重影響性能。

二、數據庫自增ID

利用Mysql的特性ID自增,可以達到數據唯一標識,但是分庫分表后只能保證一個表中的ID的唯一,而不能保證整體的ID唯一。為了避免這種情況,我們有以下兩種方式解決該問題。

2.1 主鍵表

通過單獨創(chuàng)建主鍵表維護唯一標識,作為ID的輸出源可以保證整體ID的唯一。舉個例子:

創(chuàng)建一個主鍵表

CREATE TABLE `unique_id`  (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `biz` char(1) NOT NULL,
  PRIMARY KEY (`id`),
 UNIQUE KEY `biz` (`biz`)
) ENGINE = InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET =utf8;

業(yè)務通過更新操作來獲取ID信息,然后添加到某個分表中。

BEGIN;

REPLACE INTO unique_id (biz) values ('o') ;
SELECT LAST_INSERT_ID();

COMMIT;

分布式ID解決方案對比

?2.2 ID自增步長設置

我們可以設置Mysql主鍵自增步長,讓分布在不同實例的表數據ID做到不重復,保證整體的唯一。

如下,可以設置Mysql實例1步長為1,實例1步長為2。

分布式ID解決方案對比

查看主鍵自增的屬性

show variables like '%increment%'

?分布式ID解決方案對比

?顯然,這種方式在并發(fā)量比較高的情況下,如何保證擴展性其實會是一個問題。

三、號段模式

號段模式是當下分布式ID生成器的主流實現方式之一。其原理如下:

  • 號段模式每次從數據庫取出一個號段范圍,加載到服務內存中。業(yè)務獲取時ID直接在這個范圍遞增取值即可。

  • 等這批號段ID用完,再次向數據庫申請新號段,對max_id字段做一次update操作,新的號段范圍是(max_id?,max_id +step]。

  • 由于多業(yè)務端可能同時操作,所以采用版本號version樂觀鎖方式更新。

分布式ID解決方案對比

?例如 (1,1000] 代表1000個ID,具體的業(yè)務服務將本號段生成1~1000的自增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 '業(yè)務類型',
  version int(20) NOT NULL COMMENT '版本號,是一個樂觀鎖,每次都更新version,保證并發(fā)時數據的正確性',
  PRIMARY KEY (`id`)
) 

這種分布式ID生成方式不強依賴于數據庫,不會頻繁的訪問數據庫,對數據庫的壓力小很多。但同樣也會存在一些缺點比如:服務器重啟,單點故障會造成ID不連續(xù)。

四、Redis INCR

基于全局唯一ID的特性,我們可以通過Redis的INCR命令來生成全局唯一ID。

分布式ID解決方案對比

?Redis分布式ID的簡單案例:

/**
 *  Redis 分布式ID生成器
 */
@Component
public class RedisDistributedId {

    @Autowired
    private StringRedisTemplate redisTemplate;

    private static final long BEGIN_TIMESTAMP = 1659312000l;

    /**
     * 生成分布式ID
     * 符號位    時間戳[31位]  自增序號【32位】
     * @param item
     * @return
     */
    public long nextId(String item){
        // 1.生成時間戳
        LocalDateTime now = LocalDateTime.now();
        // 格林威治時間差
        long nowSecond = now.toEpochSecond(ZoneOffset.UTC);
        // 我們需要獲取的 時間戳 信息
        long timestamp = nowSecond - BEGIN_TIMESTAMP;
        // 2.生成序號 --》 從Redis中獲取
        // 當前當前的日期
        String date = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));
        // 獲取對應的自增的序號
        Long increment = redisTemplate.opsForValue().increment("id:" + item + ":" + date);
        return timestamp << 32 | increment;
    }

}
同樣使用Redis也有對應的缺點:ID 生成的持久化問題,如果Redis宕機了怎么進行恢復?

五、雪花算法

Snowflake,雪花算法是有Twitter開源的分布式ID生成算法,以劃分命名空間的方式將64bit位分割成了多個部分,每個部分都有具體的不同含義,在Java中64Bit位的整數是Long類型,所以在Java中Snowflake算法生成的ID就是long來存儲的。具體如下:

分布式ID解決方案對比

  • 第一部分:?占用1bit,第一位為符號位,不適用

  • 第二部分:?41位的時間戳,41bit位可以表示241個數,每個數代表的是毫秒,那么雪花算法的時間年限是(241)/(1000×60×60×24×365)=69

  • 第三部分:?10bit表示是機器數,即?2^ 10 = 1024臺機器,通常不會部署這么多機器

  • 第四部分:?12bit位是自增序列,可以表示2^12=4096個數,一秒內可以生成4096個ID,理論上snowflake方案的QPS約為409.6w/s

雪花算法案例代碼:

public class SnowflakeIdWorker {

    // ==============================Fields===========================================
    /**
     * 開始時間截 (2020-11-03,一旦確定不可更改,否則時間被回調,或者改變,可能會造成id重復或沖突)
     */
    private final long twepoch = 1604374294980L;

    /**
     * 機器id所占的位數
     */
    private final long workerIdBits = 5L;

    /**
     * 數據標識id所占的位數
     */
    private final long datacenterIdBits = 5L;

    /**
     * 支持的最大機器id,結果是31 (這個移位算法可以很快的計算出幾位二進制數所能表示的最大十進制數)
     */
    private final long maxWorkerId = -1L ^ (-1L << workerIdBits);

    /**
     * 支持的最大數據標識id,結果是31
     */
    private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);

    /**
     * 序列在id中占的位數
     */
    private final long sequenceBits = 12L;

    /**
     * 機器ID向左移12位
     */
    private final long workerIdShift = sequenceBits;

    /**
     * 數據標識id向左移17位(12+5)
     */
    private final long datacenterIdShift = sequenceBits + workerIdBits;

    /**
     * 時間截向左移22位(5+5+12)
     */
    private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

    /**
     * 生成序列的掩碼,這里為4095 (0b111111111111=0xfff=4095)
     */
    private final long sequenceMask = -1L ^ (-1L << sequenceBits);

    /**
     * 工作機器ID(0~31)
     */
    private long workerId;

    /**
     * 數據中心ID(0~31)
     */
    private long datacenterId;

    /**
     * 毫秒內序列(0~4095)
     */
    private long sequence = 0L;

    /**
     * 上次生成ID的時間截
     */
    private long lastTimestamp = -1L;

    //==============================Constructors=====================================

    /**
     * 構造函數
     *
     */
    public SnowflakeIdWorker() {
        this.workerId = 0L;
        this.datacenterId = 0L;
    }

    /**
     * 構造函數
     *
     * @param workerId     工作ID (0~31)
     * @param datacenterId 數據中心ID (0~31)
     */
    public SnowflakeIdWorker(long workerId, long datacenterId) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
    }

    // ==============================Methods==========================================

    /**
     * 獲得下一個ID (該方法是線程安全的)
     *
     * @return SnowflakeId
     */
    public synchronized long nextId() {
        long timestamp = timeGen();

        //如果當前時間小于上一次ID生成的時間戳,說明系統(tǒng)時鐘回退過這個時候應當拋出異常
        if (timestamp < lastTimestamp) {
            throw new RuntimeException(
                    String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }

        //如果是同一時間生成的,則進行毫秒內序列
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            //毫秒內序列溢出
            if (sequence == 0) {
                //阻塞到下一個毫秒,獲得新的時間戳
                timestamp = tilNextMillis(lastTimestamp);
            }
        }
        //時間戳改變,毫秒內序列重置
        else {
            sequence = 0L;
        }

        //上次生成ID的時間截
        lastTimestamp = timestamp;

        //移位并通過或運算拼到一起組成64位的ID
        return ((timestamp - twepoch) << timestampLeftShift) //
                | (datacenterId << datacenterIdShift) //
                | (workerId << workerIdShift) //
                | sequence;
    }

    /**
     * 阻塞到下一個毫秒,直到獲得新的時間戳
     *
     * @param lastTimestamp 上次生成ID的時間截
     * @return 當前時間戳
     */
    protected long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    /**
     * 返回以毫秒為單位的當前時間
     *
     * @return 當前時間(毫秒)
     */
    protected long timeGen() {
        return System.currentTimeMillis();
    }

    /**
     * 隨機id生成,使用雪花算法
     *
     * @return
     */
    public static String getSnowId() {
        SnowflakeIdWorker sf = new SnowflakeIdWorker();
        String id = String.valueOf(sf.nextId());
        return id;
    }

    //=========================================Test=========================================

    /**
     * 測試
     */
    public static void main(String[] args) {
        SnowflakeIdWorker idWorker = new SnowflakeIdWorker(0, 0);
        for (int i = 0; i < 1000; i++) {
            long id = idWorker.nextId();
            System.out.println(id);
        }
    }
}
雪花算法強依賴機器時鐘,如果機器上時鐘回撥,會導致發(fā)號重復。通常通過記錄最后使用時間處理該問題。

分布式ID解決方案對比

六、美團-Leaf

開源項目鏈接:https://github.com/Meituan-Dianping/Leaf

Leaf同時支持號段模式和snowflake算法模式,可以切換使用。

snowflake模式依賴于ZooKeeper,不同于原始snowflake算法也主要是在workId的生成上,Leaf中workId是基于ZooKeeper的順序Id來生成的,每個應用在使用Leaf-snowflake時,啟動時都會都在Zookeeper中生成一個順序Id,相當于一臺機器對應一個順序節(jié)點,也就是一個workId。

號段模式是對直接用數據庫自增ID充當分布式ID的一種優(yōu)化,減少對數據庫的頻率操作。相當于從數據庫批量的獲取自增ID,每次從數據庫取出一個號段范圍,例如 (1,1000] 代表1000個ID,業(yè)務服務將號段在本地生成1~1000的自增ID并加載到內存。

七、百度-Uidgenerator

  • 開源項目鏈接:https://github.com/baidu/uid-generator
  • 中文文檔地址:https://github.com/baidu/uid-generator/blob/master/README.zh_cn.md

?UidGenerator是百度開源的Java語言實現,基于Snowflake算法的唯一ID生成器。它是分布式的,并克服了雪花算法的并發(fā)限制。單個實例的QPS能超過6000000。需要的環(huán)境:JDK8+,MySQL(用于分配WorkerId)。

百度的Uidgenerator對結構做了部分的調整,具體如下:

分布式ID解決方案對比

?時間部分只有28位,這就意味著UidGenerator默認只能承受8.5年(2^28-1/86400/365),不過UidGenerator可以適當調整delta seconds、worker node id和sequence占用位數。

八、滴滴-TinyID

開源項目鏈接:https://github.com/didi/tinyid

Tinyid是在美團(Leaf)的leaf-segment算法基礎上升級而來,不僅支持了數據庫多主節(jié)點模式,還提供了tinyid-client客戶端的接入方式,使用起來更加方便。

但和美團(Leaf)不同的是,Tinyid只支持號段一種模式不支持雪花模式。Tinyid提供了兩種調用方式,一種基于Tinyid-server提供的http方式,另一種Tinyid-client客戶端方式。

九、對比總結

分布式ID解決方案對比文章來源地址http://www.zghlxwxcb.cn/news/detail-444592.html

到了這里,關于分布式ID解決方案對比的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!

本文來自互聯(lián)網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉載,請注明出處: 如若內容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • 分布式鎖解決方案

    分布式鎖解決方案

    由于分布式或者集群部署項目時,在某些業(yè)務場景下需保證資源的原子性、一致性和互斥性。 如果把房子比作資源,通俗的來講,我無論在那個城市生活,這個房子我先租的,再沒有退房的前提下,別人都不能用 目前最流行的解決方案 redisson 分布式鎖 zookeeper 分布式鎖 mav

    2024年02月12日
    瀏覽(19)
  • 分布式websocket解決方案

    分布式websocket解決方案

    websocket基礎請自行學習,本文章是解決在分布式環(huán)境下websocket通訊問題。 在單體環(huán)境下,所有web客戶端都是連接到某一個微服務上,這樣消息都是到達統(tǒng)一服務端,并且也是由一個服務端進行響應,所以不會出現問題。 但是在分布式環(huán)境下,我們很容易發(fā)現,客戶端連接的

    2024年02月13日
    瀏覽(25)
  • 【SpirngCloud】分布式事務解決方案

    【SpirngCloud】分布式事務解決方案

    1.1 CAP 理論 1998年,加州大學的計算機科學家 Eric Brewer 提出,分布式系統(tǒng)有三個指標: Consistency(一致性):用戶訪問分布式系統(tǒng)中的任意節(jié)點,得到的數據必須一致 Availability(可用性):用戶訪問集群中的任意健康節(jié)點,必須能得到響應,而不是超時或拒絕 Partition tolerance(分區(qū)容

    2024年02月15日
    瀏覽(23)
  • Redis 分布式鎖解決方案

    Redis 分布式鎖解決方案

    我們日常在電商網站購物時經常會遇到一些高并發(fā)的場景,例如電商 App 上經常出現的秒殺活動、限量優(yōu)惠券搶購,還有我們去哪兒網的火車票搶票系統(tǒng)等,這些場景有一個共同特點就是訪問量激增,雖然在系統(tǒng)設計時會通過限流、異步、排隊等方式優(yōu)化,但整體的并發(fā)還是

    2023年04月22日
    瀏覽(27)
  • ChatGPT:分布式事務解決方案

    隨著互聯(lián)網的發(fā)展和技術的不斷更新,越來越多的應用程序開始采用分布式架構。然而,由于數據和處理邏輯的分散性和異構性,分布式環(huán)境下的事務處理面臨著許多挑戰(zhàn)。這時候就需要采用分布式事務來確保系統(tǒng)的一致性和可靠性。 分布式事務是指在分布式系統(tǒng)中,涉及多

    2023年04月12日
    瀏覽(23)
  • 聊聊分布式解決方案Saga模式

    聊聊分布式解決方案Saga模式

    Saga模式使用一系列本地事務來提供事務管理,而一個本地事務對應一個Saga參與者,在Saga流程里面每一個本地事務只操作本地數據庫,然后通過消息或事件來觸發(fā)下一個本地事務,如果其中一個本地事務失敗了,Saga就會執(zhí)行一系列補償事務來實現回滾操作。(補償事務簡單來

    2024年02月06日
    瀏覽(25)
  • 無限容量分布式文件存儲解決方案

    無限容量分布式文件存儲解決方案

    常見分布式文件系統(tǒng)比較 常見的分布式文件系統(tǒng)有GFS、HDFS 、Ceph 、GridFS 、TFS、FastDFS等。各自適用于不同的領域。 類 Google FS 都支持文件冗余備份,例如 Google FS、TFS 的備份數是 3。一個文件存儲到哪幾個存儲結點,通常采用動態(tài)分配的方式。采用這種方式,一個文件存儲到

    2024年02月11日
    瀏覽(16)
  • 淺談分布式事務及解決方案

    在講述分布式事務的概念之前,我們先來回顧下事務相關的一些概念。 就是一個程序執(zhí)行單元,里面的操作要么全部執(zhí)行成功,要么全部執(zhí)行失敗,不允許只成功一半另外一半執(zhí)行失敗的事情發(fā)生。例如一段事務代碼做了兩次數據庫更新操作,那么這兩次數據庫操作要么全部

    2024年02月08日
    瀏覽(22)
  • 分布式鎖-Redis紅鎖解決方案

    分布式鎖-Redis紅鎖解決方案

    分布式鎖(多服務共享鎖) 在分布式的部署環(huán)境下,通過鎖機制來讓多客戶端互斥的對共享資源進行訪問控制分布式系統(tǒng)不同進程共同訪問共享資源的一種鎖的實現。如果不同的系統(tǒng)或同一個系統(tǒng)的不同主機之間共享了某個臨界資源,往往需要互斥來防止彼此干擾,以保證一

    2024年02月06日
    瀏覽(14)
  • 分布式集群下WebSocket Session共享解決方案

    接上一篇 SpringBoot集成WebSocket進行消息主動推送 分布式集群下WebSocket Session共享解決方案 在實現中需要解決的類變量有兩個 其中online可以用Redis實現存儲 Session無法采用Redis進行存儲, 因為不能對Session進行序列化 由于session無法實現序列化,不能存儲到redis這些中間存儲里面,

    2024年02月12日
    瀏覽(24)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包