基於redis的分散式ID生成器
專案地址
https://github.com/hengyunabc/redis-id-generator
基於redis的分散式ID生成器。
準備
首先,要知道redis的EVAL,EVALSHA命令:
http://redis.readthedocs.org/en/latest/script/eval.html
http://redis.readthedocs.org/en/latest/script/evalsha.html
原理
利用redis的lua指令碼執行功能,在每個節點上通過lua指令碼生成唯一ID。
生成的ID是64位的:
- 使用41 bit來存放時間,精確到毫秒,可以使用41年。
- 使用12 bit來存放邏輯分片ID,最大分片ID是4095
- 使用10 bit來存放自增長ID,意味著每個節點,每毫秒最多可以生成1024個ID
比如GTM時間 Fri Mar 13 10:00:00 CST 2015
,它的距1970年的毫秒數是 1426212000000
,假定分片ID是53,自增長序列是4,則生成的ID是:
5981966696448054276 = 1426212000000 << 22 + 53 << 10 + 4
redis提供了TIME命令,可以取得redis伺服器上的秒數和微秒數。因些lua指令碼返回的是一個四元組。
second, microSecond, partition, seq
客戶端要自己處理,生成最終ID。
((second * 1000 + microSecond / 1000) << (12 + 10)) + (shardId << 10) + seq;
叢集實現原理
假定叢集裡有3個節點,則節點1返回的seq是:
0, 3, 6, 9, 12 ...
節點2返回的seq是
1, 4, 7, 10, 13 ...
節點3返回的seq是
2, 5, 8, 11, 14 ...
這樣每個節點返回的資料都是唯一的。
單個節點部署
下載redis-script-node1.lua,並把它load到redis上。
cd redis-directory/
wget https://raw.githubusercontent.com/hengyunabc/redis-id-generator/master/redis-script-node1.lua
./redis-cli script load "$(cat redis-script-node1.lua)"
獲取lua指令碼的sha1值,可能是:
fce3758b2e0af6cbf8fea4d42b379cd0dc374418
在程式碼裡,通過EVALSHA命令,傳遞這個sha1值,就可以得到生成的ID。
比如,通過命令列執行:
./redis-cli EVALSHA fce3758b2e0af6cbf8fea4d42b379cd0dc374418 2 test 123456789
結果可能是:
1) (integer) 1426238286
2) (integer) 130532
3) (integer) 277
4) (integer) 4
叢集部署
假定叢集是3個節點,則分別對三個節點執行:
./redis-cli -host node1 -p 6379 script load "$(cat redis-script-node1.lua)"
./redis-cli -host node2 -p 7379 script load "$(cat redis-script-node2.lua)"
./redis-cli -host node3 -p 8379 script load "$(cat redis-script-node3.lua)"
效能
redis預設配置。
單節點,單執行緒:
time:0:00:00.959
speed:10427.52867570386
單節點,20執行緒:
time:0:00:06.710
speed:29806.259314456034
結論:
– 單節點,qps約3w
– 可以線性擴充套件,3個結點足以滿足絕大部分的應用
java客戶端封裝
在redis-id-generator-java目錄下,有example和benchmark程式碼。
在呼叫時,要傳入兩個引數
– tag,即為哪一類服務生成ID
– shardId,即分片由哪個ID生成,比如一個使用者的訂單,則分片ID應該由userId來生成
public class Example {
public static void main(String[] args) {
String tab = "order";
long userId = 123456789;
IdGenerator idGenerator = IdGenerator.builder()
.addHost("127.0.0.1", 6379, "fce3758b2e0af6cbf8fea4d42b379cd0dc374418")
// .addHost("127.0.0.1", 7379, "1abc55928f37176cb934fc7a65069bf32282d817")
// .addHost("127.0.0.1", 8379, "b056d20feb3f89483b10c81027440cbf6920f74f")
.build();
long id = idGenerator.next(tab, userId);
System.out.println("id:" + id);
List<Long> result = IdGenerator.parseId(id);
System.out.println("miliSeconds:" + result.get(0) + ", partition:"
+ result.get(1) + ", seq:" + result.get(2));
}
}
多語言客戶端
只要支援redis evalsha命令就可以了。
其它
之前寫的一個blog:分片(Sharding)的全域性ID生成
相關文章
- 基於 Redis 的分散式鎖Redis分散式
- 基於redis的分散式鎖Redis分散式
- 基於 Redis 分散式鎖Redis分散式
- 基於redis做分散式鎖Redis分散式
- [翻譯]基於redis的分散式鎖Redis分散式
- 基於Redis分散式BitMap的應用Redis分散式
- 基於redis的分散式鎖實現Redis分散式
- 基於redis實現分散式鎖Redis分散式
- 一種redis命令搞定基於redis的分散式鎖Redis分散式
- 基於 Redis 的分散式鎖到底安全嗎?Redis分散式
- 基於 Redis 實現簡單的分散式鎖Redis分散式
- 基於Redis的分散式鎖的簡單實現Redis分散式
- 基於redis分散式鎖實現“秒殺”Redis分散式
- 基於Redis實現一個分散式鎖Redis分散式
- 基於 Redis 實現分散式應用限流Redis分散式
- 分散式唯一 ID 生成器分散式
- 基於Redis分散式鎖的正確開啟方式Redis分散式
- [轉載]基於Redis的分散式鎖到底安全嗎?Redis分散式
- java 實現開箱即用基於 redis 的分散式鎖JavaRedis分散式
- 基於redis和zookeeper的分散式鎖實現方式Redis分散式
- 分散式唯一 ID 生成器 - IDGen分散式
- 【分散式架構】(10)---基於Redis元件的特性,實現一個分散式限流分散式架構Redis元件
- Golang 基於單節點 Redis 實現的分散式鎖GolangRedis分散式
- 分散式鎖與實現(一)基於Redis實現!分散式Redis
- 基於AOP和Redis實現的簡易版分散式鎖Redis分散式
- 基於asyncio和redis的Python分散式任務佇列RedisPython分散式佇列
- 基於資料庫、redis和zookeeper實現的分散式鎖資料庫Redis分散式
- 分散式ID生成器的解決方案總結分散式
- redis實現分散式id方案Redis分散式
- 基於註解的方式使用spring-integration-redis分散式鎖SpringRedis分散式
- 手把手教你實現一個基於Redis的分散式鎖Redis分散式
- 分散式鎖中-基於 Redis 的實現需避坑 - Jedis 篇分散式Redis
- 靈感來襲,基於Redis的分散式延遲佇列(續)Redis分散式佇列
- 【進階篇】基於 Redis 實現分散式鎖的全過程Redis分散式
- NodeJS 基於redis的分散式鎖的實現(Redlock演算法)NodeJSRedis分散式演算法
- 教你一招:基於Redis實現一個分散式鎖Redis分散式
- 基於Redis實現分散式鎖,Redisson使用及原始碼分析Redis分散式原始碼
- [分散式][Redis]Redis分散式框架搭建與整合分散式Redis框架