自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

框架篇:分布式全局唯一ID

開發(fā) 架構(gòu) 分布式
每一次HTTP請求,數(shù)據(jù)庫的事務(wù)的執(zhí)行,我們追蹤代碼執(zhí)行的過程中,需要一個唯一值和這些業(yè)務(wù)操作相關(guān)聯(lián),對于單機的系統(tǒng),可以用數(shù)據(jù)庫的自增ID或者時間戳加一個在本機遞增值,即可實現(xiàn)唯一值。

[[407823]]

本文轉(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

  • 分布式ID的特性
  • 數(shù)據(jù)庫自增的ID
  • Redis分布式ID
  • Zookeeper分布式ID
  • 全局唯一UUID的優(yōu)缺點
  • Twitter的雪花算法生成分布式ID

分布式ID的特性

  • 全局唯一性,必須性
  • 冪等性,如果是根據(jù)某些信息生成,則需要保障冪等性
  • 注意安全性,ID里隱藏一些信息,不能被猜出來,也不能被猜出來 ID 如何生成
  • 趨勢遞增性,在查詢比較時,可以判斷業(yè)務(wù)操作的時間順序

數(shù)據(jù)庫自增的ID

實現(xiàn)簡單,ID單調(diào)自增,數(shù)值類型查詢速度快,但是單點DB存在宕機風(fēng)險,無法扛住高并發(fā)場景

  1. CREATE TABLE FLIGHT_ORDER ( 
  2.     id int(11) unsigned NOT NULL auto_increment, #自增ID 
  3.     PRIMARY KEY (id), 
  4. ) ENGINE=innodb; 

集群下如何保證數(shù)據(jù)庫ID的唯一性

  • 當(dāng)隨著業(yè)務(wù)發(fā)展,服務(wù)拓展到多臺的大集群時,為了解決單點數(shù)據(jù)庫的壓力,數(shù)據(jù)庫也會相應(yīng)的變成一個集群,那如何保證集群下數(shù)據(jù)庫ID的唯一性
  • 每一臺數(shù)據(jù)庫實例都設(shè)置一個起始值和增長步長

缺點:不利于后續(xù)擴容,如果后續(xù)需要擴容還需要人工介入修改 起始值和增長步長

Redis 分布式ID

假如系統(tǒng)有億萬的數(shù)據(jù),依靠數(shù)據(jù)庫的自增ID在分表分庫之后,需要人工修改每臺數(shù)據(jù)庫實例,擴容性差,維護(hù)性不好

基于Redis INCR 命令生成分布式全局唯一ID

  • 服務(wù)向redis獲取Id,ID則和數(shù)據(jù)庫解耦,可以解決ID和分表分庫的問題,而且redis比數(shù)據(jù)庫性能更快,可以支撐集群服務(wù)并發(fā)獲取ID的需求
  • redis的INCR命令具備了 INCR AND GET 的原子操作;redis是單進(jìn)程單線程架構(gòu),INCR 命令不會出現(xiàn) ID 重復(fù)
  1. @Autowired 
  2. private StringRedisTemplate stringRedisTemplate; 
  3. private static final String ID_KEY = "id_good_order"
  4. public Long incrementId() { 
  5.     return stringRedisTemplate.opsForValue().increment(ID_KEY); 

HINCRBY 命令

  • 實際上,為了存儲序列號的更多相關(guān)信息,可以使用了 Redis 的 Hash 數(shù)據(jù)結(jié)構(gòu),Redis 同樣為 Hash 提供 HINCRBY 命令來實現(xiàn) “INCR AND GET” 原子操作
  1. //KEY_NAME 是 hash結(jié)構(gòu)對應(yīng)的Key,FIELD_NAME 是hash結(jié)構(gòu)的字段,INCR_BY_NUMBER是增量值 
  2. redis 127.0.0.1:6379> HINCRBY KEY_NAME FIELD_NAME INCR_BY_NUMBER  

宕機序列號恢復(fù)問題

  • redis是內(nèi)存數(shù)據(jù)庫,在沒有開啟RDB或AOF持久化的情況下,一旦宕機ID數(shù)據(jù)將會有丟失。即便開啟了RDB持久化,由于最近一次快照時間和最新一條 HINCRBY 命令的時間有可能存在時間差,宕機后通過RDB快照恢復(fù)數(shù)據(jù)集會發(fā)生ID取值重復(fù)的情況
  • redis宕機序列號恢復(fù)方案
    • 利用關(guān)系型數(shù)據(jù)庫來記錄一個短時內(nèi) 最大可取序列號 MAX_ID,從redis獲取ID時只能取小于 MAX_ID 的序列號
    • 為了計算最大值,需要一個定時任務(wù)定期計算ID消費速度RATE,存于redis。當(dāng)客戶端取得 CUR_ID、RATE 和 MAX_ID,則根據(jù) ID 消費速度 RATE 計算 CUR_ID 是否逼近MAX_ID,如果是則更新數(shù)據(jù)庫的MAX_ID

Zookeeper 分布式ID

  • 利用zookeeper的持久性有序節(jié)點,可以實現(xiàn)自增的分布式ID,而且zookeeper是個高可用的集群服務(wù),提交成功的消息具有持久性,因此不怕機器宕機問題,或者單機問題
  1. <dependency> 
  2.     <groupId>org.apache.curator</groupId> 
  3.     <artifactId>curator-framework</artifactId> 
  4.     <version>4.2.0</version> 
  5. </dependency> 
  6. <dependency> 
  7.     <groupId>org.apache.curator</groupId> 
  8.     <artifactId>curator-recipes</artifactId> 
  9.     <version>4.2.0</version> 
  10. </dependency> 

 

  • 示例
  1. RetryPolicy retryPolicy = new ExponentialBackoffRetry(500, 3); 
  2. CuratorFramework client = CuratorFrameworkFactory.builder() 
  3.       .connectString("localhost:2181"
  4.       .connectionTimeoutMs(5000) 
  5.       .sessionTimeoutMs(5000) 
  6.       .retryPolicy(retryPolicy) 
  7.       .build(); 
  8. client.start();   
  9. String sequenceName = "root/sequence/distributedId"
  10. DistributedAtomicLong  distAtomicLong = new DistributedAtomicLong(client, sequenceName, retryPolicy); 
  11. //使用DistributedAtomicLong生成自增序列 
  12. public Long sequence() throws Exception { 
  13.     AtomicValue<Long> sequence = this.distAtomicLong.increment(); 
  14.     if (sequence.succeeded()) { 
  15.         return sequence.postValue(); 
  16.     } else { 
  17.         return null
  18.     } 

UUID的優(yōu)缺點

  • 基于數(shù)據(jù)庫,redis,zookeeper的分布式ID都高度依賴一個外部服務(wù),對于某些場景,假如不存在這些外部服務(wù)又該怎么生成分布式的ID
  • JDK里自帶一個唯一性的ID的生成器,具有全球唯一性,這就是UUID,不過它是串無意義的字符串,存儲性能差,查詢也很耗時,對于訂單系統(tǒng),不適合作為唯一ID,常見優(yōu)化方案為「轉(zhuǎn)化為兩個uint64整數(shù)存儲」或者 「折半存儲」(折半后不能保證唯一性)
  • 但對于日志系統(tǒng),或只是為了作為數(shù)據(jù)里可以唯一識別序列號的關(guān)聯(lián)屬性時,可以用UUID
  1. String uuid = UUID.randomUUID().toString().replaceAll("-",""); 

Twitter 的雪花算法生成分布式ID

  • 和UUID一樣,雪花算法并不依賴外部服務(wù)
  • 雪花算法是 Twitter 公司內(nèi)部分布式項目采用的ID生成算法,廣受國內(nèi)公司好評。不依賴第三方服務(wù),效率高

Snowflake ID組成結(jié)構(gòu):正數(shù)位(占1比特)+ 時間戳(占41比特)+ 機器ID(占5比特)+ 數(shù)據(jù)中心(占5比特)+ 自增值(占12比特),總共64比特組成的一個Long類型。

1:第一個bit位(1bit):Java中l(wèi)ong的最高位是符號位代表正負(fù),正數(shù)是0,負(fù)數(shù)是1,一般生成ID都為正數(shù),所以默認(rèn)為0。

2:時間戳部分(41bit):毫秒級的時間,不建議存當(dāng)前時間戳,而是用(當(dāng)前時間戳 - 固定開始時間戳)的差值,可以使產(chǎn)生的ID從更小的值開始

3:工作機器id(10bit):也被叫做workId,這個可以靈活配置,機房或者機器號組合都可以。

4:序列號部分(12bit),自增值支持同一毫秒內(nèi)同一個節(jié)點可以生成4096個ID

  1. //Twitter的SnowFlake算法,使用SnowFlake算法生成一個整數(shù) 
  2. public class SnowFlakeShortUrl { 
  3.     //起始的時間戳 
  4.     static long START_TIMESTAMP = 1624698370256L; 
  5.     //每一部分占用的位數(shù) 
  6.     static long SEQUENCE_BIT = 12;   //序列號占用的位數(shù) 
  7.     static long MACHINE_BIT = 5;     //機器標(biāo)識占用的位數(shù) 
  8.     static long DATA_CENTER_BIT = 5; //數(shù)據(jù)中心占用的位數(shù) 
  9.     //每一部分的最大值 
  10.     static long MAX_SEQUENCE = -1L ^ (-1L << SEQUENCE_BIT); 
  11.     static long MAX_MACHINE_NUM = -1L ^ (-1L << MACHINE_BIT); 
  12.     static long MAX_DATA_CENTER_NUM = -1L ^ (-1L << DATA_CENTER_BIT); 
  13.     //每一部分向左的位移 
  14.     static long MACHINE_LEFT = SEQUENCE_BIT; 
  15.     static long DATA_CENTER_LEFT = SEQUENCE_BIT + MACHINE_BIT; 
  16.     static long TIMESTAMP_LEFT = DATA_CENTER_LEFT + DATA_CENTER_BIT; 
  17.     //dataCenterId + machineId 等于10bit工作機器ID 
  18.     private long dataCenterId;  //數(shù)據(jù)中心 
  19.     private long machineId;     //機器標(biāo)識 
  20.     private volatile long sequence = 0L; //序列號 
  21.     private volatile long lastTimeStamp = -1L;  //上一次時間戳 
  22.     private volatile long l currTimeStamp = -1L; //當(dāng)前時間戳 
  23.      
  24.     private long getNextMill() { 
  25.         long mill = System.currentTimeMillis(); 
  26.         while (mill <= lastTimeStamp) mill = System.currentTimeMillis(); 
  27.         return mill; 
  28.     } 
  29.     //根據(jù)指定的數(shù)據(jù)中心ID和機器標(biāo)志ID生成指定的序列號 
  30.     public SnowFlakeShortUrl(long dataCenterId, long machineId) { 
  31.         Assert.isTrue(dataCenterId >=0 && dataCenterId <= MAX_DATA_CENTER_NUM,"dataCenterId is illegal!"); 
  32.         Assert.isTrue(machineId >= 0 || machineId <= MAX_MACHINE_NUM,"machineId is illegal!"); 
  33.         this.dataCenterId = dataCenterId; 
  34.         this.machineId = machineId; 
  35.     } 
  36.     //生成下一個ID 
  37.     public synchronized long nextId() { 
  38.         currTimeStamp = System.currentTimeMillis(); 
  39.         Assert.isTrue(currTimeStamp >= lastTimeStamp,"Clock moved backwards"); 
  40.         if (currTimeStamp == lastTimeStamp) { 
  41.             //相同毫秒內(nèi),序列號自增 
  42.             sequence = (sequence + 1) & MAX_SEQUENCE; 
  43.             if (sequence == 0L) { //同一毫秒的序列數(shù)已經(jīng)達(dá)到最大,獲取下一個毫秒 
  44.                 currTimeStamp = getNextMill(); 
  45.             } 
  46.         } else {  
  47.             sequence = 0L; //不同毫秒內(nèi),序列號置為0 
  48.         } 
  49.         lastTimeStamp = currTimeStamp; 
  50.         return (currTimeStamp - START_TIMESTAMP) << TIMESTAMP_LEFT //時間戳部分 
  51.                 | dataCenterId << DATA_CENTER_LEFT       //數(shù)據(jù)中心部分 
  52.                 | machineId << MACHINE_LEFT             //機器標(biāo)識部分 
  53.                 | sequence;                             //序列號部分 
  54.     } 
  55.      
  56.     public static void main(String[] args) { 
  57.         SnowFlakeShortUrl snowFlake = new SnowFlakeShortUrl(10, 4); 
  58.         for (int i = 0; i < (1 << 12); i++) { 
  59.             //10進(jìn)制 
  60.             System.out.println(snowFlake.nextId()); 
  61.         } 
  62.     } 

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

 

責(zé)任編輯:武曉燕 來源: 潛行前行
相關(guān)推薦

2021-06-05 07:33:09

ID分布式架構(gòu)

2022-02-23 07:09:30

分布式ID雪花算法

2023-12-13 09:35:52

算法分布式

2021-07-07 07:14:48

分布式ID分布式系統(tǒng)

2021-11-08 19:25:37

Go生成系統(tǒng)

2021-06-02 22:16:56

框架CAPBASE

2021-07-02 06:54:43

分布式環(huán)境ID

2017-04-12 09:29:02

HiveMapReduceSpark

2020-07-21 11:35:21

開發(fā)技能代碼

2024-02-02 10:57:12

Java分布式算法

2023-09-03 22:14:23

分布式ID

2019-09-03 09:22:08

數(shù)據(jù)庫Redis算法

2022-09-28 07:58:06

MongoDB分布式ID

2021-06-06 12:45:41

分布式CAPBASE

2024-10-31 13:51:58

2017-07-01 16:02:39

分布式ID生成器

2023-01-12 17:46:37

分庫分表id如何生成

2021-08-04 10:38:51

分布式 ID策略

2024-03-28 10:01:38

2024-03-13 08:23:08

分布式系統(tǒng)隨機
點贊
收藏

51CTO技術(shù)棧公眾號