掃二維碼與項目經(jīng)理溝通
我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流
作者:cscw 2021-06-28 14:45:07
開發(fā)
架構(gòu)
分布式 每一次HTTP請求,數(shù)據(jù)庫的事務(wù)的執(zhí)行,我們追蹤代碼執(zhí)行的過程中,需要一個唯一值和這些業(yè)務(wù)操作相關(guān)聯(lián),對于單機的系統(tǒng),可以用數(shù)據(jù)庫的自增ID或者時間戳加一個在本機遞增值,即可實現(xiàn)唯一值。

創(chuàng)新互聯(lián)公司成都網(wǎng)站建設(shè)按需制作網(wǎng)站,是成都網(wǎng)站建設(shè)公司,為成都崗?fù)?/a>提供網(wǎng)站建設(shè)服務(wù),有成熟的網(wǎng)站定制合作流程,提供網(wǎng)站定制設(shè)計服務(wù):原型圖制作、網(wǎng)站創(chuàng)意設(shè)計、前端HTML5制作、后臺程序開發(fā)等。成都網(wǎng)站設(shè)計熱線:028-86922220
本文轉(zhuǎn)載自微信公眾號「潛行前行」,作者cscw。轉(zhuǎn)載本文請聯(lián)系潛行前行公眾號。
每一次HTTP請求,數(shù)據(jù)庫的事務(wù)的執(zhí)行,我們追蹤代碼執(zhí)行的過程中,需要一個唯一值和這些業(yè)務(wù)操作相關(guān)聯(lián),對于單機的系統(tǒng),可以用數(shù)據(jù)庫的自增ID或者時間戳加一個在本機遞增值,即可實現(xiàn)唯一值。但在分布式,又該如何實現(xiàn)唯一性的ID
實現(xiàn)簡單,ID單調(diào)自增,數(shù)值類型查詢速度快,但是單點DB存在宕機風(fēng)險,無法扛住高并發(fā)場景
- CREATE TABLE FLIGHT_ORDER (
- id int(11) unsigned NOT NULL auto_increment, #自增ID
- PRIMARY KEY (id),
- ) ENGINE=innodb;
集群下如何保證數(shù)據(jù)庫ID的唯一性
缺點:不利于后續(xù)擴容,如果后續(xù)需要擴容還需要人工介入修改 起始值和增長步長
假如系統(tǒng)有億萬的數(shù)據(jù),依靠數(shù)據(jù)庫的自增ID在分表分庫之后,需要人工修改每臺數(shù)據(jù)庫實例,擴容性差,維護性不好
基于Redis INCR 命令生成分布式全局唯一ID
- @Autowired
- private StringRedisTemplate stringRedisTemplate;
- private static final String ID_KEY = "id_good_order";
- public Long incrementId() {
- return stringRedisTemplate.opsForValue().increment(ID_KEY);
- }
HINCRBY 命令
- //KEY_NAME 是 hash結(jié)構(gòu)對應(yīng)的Key,FIELD_NAME 是hash結(jié)構(gòu)的字段,INCR_BY_NUMBER是增量值
- redis 127.0.0.1:6379> HINCRBY KEY_NAME FIELD_NAME INCR_BY_NUMBER
宕機序列號恢復(fù)問題
org.apache.curator curator-framework 4.2.0 org.apache.curator curator-recipes 4.2.0
- RetryPolicy retryPolicy = new ExponentialBackoffRetry(500, 3);
- CuratorFramework client = CuratorFrameworkFactory.builder()
- .connectString("localhost:2181")
- .connectionTimeoutMs(5000)
- .sessionTimeoutMs(5000)
- .retryPolicy(retryPolicy)
- .build();
- client.start();
- String sequenceName = "root/sequence/distributedId";
- DistributedAtomicLong distAtomicLong = new DistributedAtomicLong(client, sequenceName, retryPolicy);
- //使用DistributedAtomicLong生成自增序列
- public Long sequence() throws Exception {
- AtomicValue
sequence = this.distAtomicLong.increment(); - if (sequence.succeeded()) {
- return sequence.postValue();
- } else {
- return null;
- }
- }
UUID的優(yōu)缺點
- String uuid = UUID.randomUUID().toString().replaceAll("-","");
Snowflake ID組成結(jié)構(gòu):正數(shù)位(占1比特)+ 時間戳(占41比特)+ 機器ID(占5比特)+ 數(shù)據(jù)中心(占5比特)+ 自增值(占12比特),總共64比特組成的一個Long類型。
1:第一個bit位(1bit):Java中l(wèi)ong的最高位是符號位代表正負,正數(shù)是0,負數(shù)是1,一般生成ID都為正數(shù),所以默認為0。
2:時間戳部分(41bit):毫秒級的時間,不建議存當(dāng)前時間戳,而是用(當(dāng)前時間戳 - 固定開始時間戳)的差值,可以使產(chǎn)生的ID從更小的值開始
3:工作機器id(10bit):也被叫做workId,這個可以靈活配置,機房或者機器號組合都可以。
4:序列號部分(12bit),自增值支持同一毫秒內(nèi)同一個節(jié)點可以生成4096個ID
- //Twitter的SnowFlake算法,使用SnowFlake算法生成一個整數(shù)
- public class SnowFlakeShortUrl {
- //起始的時間戳
- static long START_TIMESTAMP = 1624698370256L;
- //每一部分占用的位數(shù)
- static long SEQUENCE_BIT = 12; //序列號占用的位數(shù)
- static long MACHINE_BIT = 5; //機器標識占用的位數(shù)
- static long DATA_CENTER_BIT = 5; //數(shù)據(jù)中心占用的位數(shù)
- //每一部分的最大值
- static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT);
- static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT);
- static long MAX_DATA_CENTER_NUM = -1L ^ (-1L << DATA_CENTER_BIT);
- //每一部分向左的位移
- static long MACHINE_LEFT = SEQUENCE_BIT;
- static long DATA_CENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT;
- static long TIMESTAMP_LEFT = DATA_CENTER_LEFT + DATA_CENTER_BIT;
- //dataCenterId + machineId 等于10bit工作機器ID
- private long dataCenterId; //數(shù)據(jù)中心
- private long machineId; //機器標識
- private volatile long sequence = 0L; //序列號
- private volatile long lastTimeStamp = -1L; //上一次時間戳
- private volatile long l currTimeStamp = -1L; //當(dāng)前時間戳
- private long getNextMill() {
- long mill = System.currentTimeMillis();
- while (mill <= lastTimeStamp) mill = System.currentTimeMillis();
- return mill;
- }
- //根據(jù)指定的數(shù)據(jù)中心ID和機器標志ID生成指定的序列號
- public SnowFlakeShortUrl(long dataCenterId, long machineId) {
- Assert.isTrue(dataCenterId >=0 && dataCenterId <= MAX_DATA_CENTER_NUM,"dataCenterId is illegal!");
- Assert.isTrue(machineId >= 0 || machineId <= MAX_MACHINE_NUM,"machineId is illegal!");
- this.dataCenterId = dataCenterId;
- this.machineId = machineId;
- }
- //生成下一個ID
- public synchronized long nextId() {
- currTimeStamp = System.currentTimeMillis();
- Assert.isTrue(currTimeStamp >= lastTimeStamp,"Clock moved backwards");
- if (currTimeStamp == lastTimeStamp) {
- //相同毫秒內(nèi),序列號自增
- sequence = (sequence + 1) & MAX_SEQUENCE;
- if (sequence == 0L) { //同一毫秒的序列數(shù)已經(jīng)達到最大,獲取下一個毫秒
- currTimeStamp = getNextMill();
- }
- } else {
- sequence = 0L; //不同毫秒內(nèi),序列號置為0
- }
- lastTimeStamp = currTimeStamp;
- return (currTimeStamp - START_TIMESTAMP) << TIMESTAMP_LEFT //時間戳部分
- | dataCenterId << DATA_CENTER_LEFT //數(shù)據(jù)中心部分
- | machineId << MACHINE_LEFT //機器標識部分
- | sequence; //序列號部分
- }
- public static void main(String[] args) {
- SnowFlakeShortUrl snowFlake = new SnowFlakeShortUrl(10, 4);
- for (int i = 0; i < (1 << 12); i++) {
- //10進制
- System.out.println(snowFlake.nextId());
- }
- }
- }
Reference
[1]github地址:https://github.com/cscsss/learnHome
[2]常見分布式全局唯一ID生成策略及算法的對比:
https://blog.csdn.net/u010398771/article/details/79765836
[3]基于 Redis 的序列號服務(wù)(分布式id)的設(shè)計:
https://blog.csdn.net/carryxu123456/article/details/82630029
[4]9種 分布式ID生成方案,讓你一次學(xué)個夠:
https://segmentfault.com/a/1190000022717820

我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流