1. 程式人生 > 其它 >分散式ID解決方案

分散式ID解決方案

技術標籤:分散式策略分散式一致性協議分散式資料庫

為什麼需要分散式ID(分散式叢集環境下的全域性唯一ID)

在大資料量情況下,我們經常會使用到分表儲存資料,如將表A的資料平均分到表A1、A2、A3中,此時如果表A1和A2或者A3中存在相同的ID,就會導致資料的不唯一,會有多條資料

那麼此時就需要分表之後ID不能重複,不能使用主鍵遞增。下面列出幾種常見的解決方案:

1.UUID

UUID 是指Universally Unique Identifier,翻譯為中文是通用唯一識別碼 產生重複 UUID 並造成錯誤的情況非常低,是故大可不必考慮此問題。 Java中得到一個UUID,可以使用java.util包提供的方法

public class MyTest {
 public static void main(String[] args) {
 System.out.println(java.util.UUID.randomUUID().toString());
 }
}

2. 獨立資料庫的自增ID

比如A表分表為A1表和A2表,那麼肯定不能讓A1表和A2表的ID自增,那麼ID怎麼獲取呢?我們可以單獨的建立一個Mysql資料庫,在這個資料庫中建立一張表,這張表的ID設定為自增,其他地方需要全域性唯一ID的時候,就模擬向這個Mysql資料庫的這張表中模擬插入一條記錄,此時ID會自增,然後我們可以通過Mysql的select last_insert_id() 獲取到剛剛這張表中自增生成的ID.

比如,我們建立一個數據庫例項global_id_generator,在其中建立了一個數據表,表結構如下:

DROP TABLE IF EXISTS `DISTRIBUTE_ID`;
CREATE TABLE `DISTRIBUTE_ID` (
 `id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '主鍵',
 `createtime` datetime DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

當分散式叢集環境中哪個應用需要獲取一個全域性唯一的分散式ID的時候,就可以使代程式碼連線這個資料庫例項,執行如下sql語句即可。

insert into DISTRIBUTE_ID(createtime) values(NOW());
select LAST_INSERT_ID();

注意:

1)這裡的createtime欄位無實際意義,是為了隨便插入一條資料以至於能夠自增id。
2)使用獨立的Mysql例項生成分散式id,雖然可用,但是效能和可靠性都不夠好,因為你需要程式碼連線到資料庫才能獲取到id,效能無法保障,另外mysql資料庫例項掛掉了,那麼就無法獲取分散式id了。
3)有一些開發者又針對上述的情況將用於生成分散式id的mysql資料庫設計成了一個叢集架構, 那麼其實這種方式現在基本不用,因為過於麻煩了。

3.SnowFlake 雪花演算法

雪花演算法是Twitter推出的一個用於生成分散式ID的策略。

雪花演算法是一個演算法,基於這個演算法可以生成ID,生成的ID是一個long型,那麼在java中一個long型是8個位元組,算下來是64bit,如下是使用雪花演算法生成的一個ID的二進位制形式示意:

說明:

1)符號位:固定為0,二進位制表示最高位是符號位,0代表正數,1代表負數。
2)時問戳:41個二進位制數用來記錄時間戳,表示某一個毫秒(毫秒級)。
3)機器:代表當前演算法執行機器的id。
4)序列號:12位.用來記錄某個機器同一個毫秒內產生的不同序列號,代表同一個機器同一個毫秒可以產生的ID序號。

另外,一些網際網路公司也基於上述的方案封裝了一些分散式ID生成器,比如滴滴的tinyid(基於資料庫實現)、百度的uidgenerator(基於SnowFlake)和美團的leaf(基於資料庫和SnowFlake)等,他們在。

4. 藉助Redis的Incr命令獲取全域性唯一ID(推薦)

Redis Incr 命令將 key 中儲存的數字值增一。如果 key 不存在,那麼 key 的值會先被初始化為 0 ,然後再執行INCR 操作。

<key,value>
<id,>
.incr(id) 1 2 3 4

首先需要在本地安裝redis,教程詳細訪問:https://www.runoob.com/redis/redis-install.html

安裝完成後啟動redis服務:在src目錄下執行./redis-server ../redis.conf 啟動redis服務

Java程式碼中使用Jedis客戶端呼叫Reids的incr命令獲得一個全域性的id

引入Jedis客戶端Jar

<dependency>
 <groupId>redis.clients</groupId>
 <artifactId>jedis</artifactId>
 <version>2.9.0</version>
</dependency>

Java程式碼

Jedis jedis = new Jedis("127.0.0.1",6379);
try {
 long id = jedis.incr("id");
 System.out.println("從redis中獲取的分散式id為:" + id);
} finally {
 if (null != jedis) {
 jedis.close();
 }
}