莫张周刘王 发表于 2025-4-19 15:00:57

Redis之全局唯一ID

全局ID天生器

https://i-blog.csdnimg.cn/direct/867274b523bc4554983ef3da7959d884.png#pic_center


一、全局ID天生器的界说

界说



[*]全局ID天生器是一种在分布式体系中天生唯一标识符(ID)的机制,确保天生的ID在整个体系范围内(大概跨多个服务、数据库或数据中心)具有唯一性。它是解决分布式环境下数据唯一性、数据关联和事务协调等标题的核心技术之一。
核心作用



[*]​唯一性:确保每个天生的ID在全局范围内不重复。
[*]可辨认性:通过ID快速定位资源或关联业务(如订单、用户、消息等),不能被看出太明显的规律。
[*]​扩展性:支持体系规模增长(如分库分表、多节点部署)时仍能高效天生ID,高效应对海量数据。
二、全局ID天生器需满意的特征

全局ID天生器,是分布式体系环境下天生全局唯一ID的工具,一样平常需要满意下列特征:
1. 唯一性(Uniqueness)​



[*]​核心要求:天生的ID在全局范围内绝对唯一。
[*]意义:制止数据冲突(如主键重复、消息覆盖)。
[*]实现方式:

[*]算法设计(如Snowflake通过时间戳+机器ID+序列号包管唯一)。
[*]中心化协调(如数据库自增ID依赖数据库的唯一约束)。

2. 高性能(High Performance)​



[*]​要求:天生ID的速度快,延迟低,支持高并发场景。
[*]意义:制止成为体系瓶颈(如秒杀场景下每秒天生数十万ID)。
[*]实现方式:

[*]当地天生(如Snowflake在内存中天生,无需网络请求)。
[*]批量预分配(如数据库号段模式一次性获取多个ID)。

3. 可扩展性(Scalability)​



[*]要求:支持体系横向扩展(如新增节点)时无需重构ID天生逻辑。
[*]意义:顺应业务增长,制止单点瓶颈。
[*]实现方式:

[*]分布式算法(如Snowflake答应动态增加机器ID)。
[*]去中心化设计(如UUID无需中心节点)。

4. 有序性(Orderliness)​



[*]​要求:天生的ID按时间递增或可排序。
[*]意义:便于数据库索引优化、分页查询和业务排序(如按时间排序订单)。
[*]​实现方式:

[*]时间戳高位(如Snowflake将时间戳放在ID的高位)。
[*]数据库自增ID自然有序,但分布式下需分片步长计谋。

5. 可靠性(Reliability)​



[*]要求:天生过程容错,制止单点故障。
[*]意义:保障体系高可用(如机器宕机不影响ID天生)。
[*]实现方式:

[*]去中心化算法(如Snowflake无单点依赖)。
[*]冗余设计(如Leaf通过ZooKeeper管理机器ID,支持故障转移)。

6. 安全性(Security)​



[*]​要求:防止ID被推测或遍历(如制止袒露业务量或用户隐私)。
[*]意义:防止恶意攻击(如通过ID遍历批量查询数据)。
[*]实现方式:

[*]加入随机性(如UUIDv4的随机部分)。
[*]加密或哈希处理(如美团的Leaf-Segment加密号段)。

7. 兼容性(Compatibility)​



[*]要求:ID格式适配现有技术栈(如数据库范例、网络传输)。
[*]意义:低落集成成本(如制止利用超长ID导致存储浪费)。
[*]实现方式:

[*]数值型ID(如Snowflake的64位Long范例,兼容MySQL BIGINT)。
[*]字符串ID(如UUID的128位字符串,适配NoSQL数据库)。

三、全局唯一ID天生计谋:

1. UUID



[*]原理:天生128位随机字符串(如 550e8400-e29b-41d4-a716-446655440000)。
[*]优点:简单、无需中心化协调。
[*]缺点:无序、长度长、存储和索引效率低。
[*]实用场景:非高频查询场景,如日志跟踪、临时标识。
[*]优化:利用UUIDv4(随机天生)制止隐私标题。
2. 数据库自增ID



[*]​原理:利用数据库自增主键天生唯一ID。
[*]优点:简单、自然有序。
[*]​缺点:单点瓶颈、横向扩展困难。
[*]​优化:

[*]分库分表:为每个分片设置差别步长(如库1步长=2,起始值=1;库2步长=2,起始值=2)。
[*]批量获取:一次性申请多个ID(如 INSERT … SELECT MAX(id)+N)。

3. Snowflake算法(Twitter开源)​



[*]ID结构​(64位):| 符号位(1) | 时间戳(41) | 机器ID(10) | 序列号(12) |

[*]优点:有序、高性能、去中心化。
[*]​缺点:依赖体系时钟(时钟回拨需处理)。
[*]实现:public class Snowflake {

    private final long machineId;   // 机器ID(0~1023)
    private long lastTimestamp = -1L;
    private long sequence = 0L;

    public synchronized long nextId() {
      long timestamp = System.currentTimeMillis();
      if (timestamp < lastTimestamp) throw new RuntimeException("时钟回拨");
      if (timestamp == lastTimestamp) {
            sequence = (sequence + 1) & 0xFFF; // 序列号自增,溢出则等待下一毫秒
            if (sequence == 0) timestamp = waitNextMillis();
      } else {
            sequence = 0;
      }
      lastTimestamp = timestamp;
      return ((timestamp - TWEPOCH) << 22) | (machineId << 12) | sequence;
    }
}

4. Redis自增



[*] 原理:利用Redis的原子操作 INCR 或 INCRBY 天生自增ID。利用Java的Long范例存储,为了增加ID的安全性,我们可以不直接只用Redis自增的数值,而是拼接一些其他信息:
ID的组成部分(Java Long类型存储,占用8个字节,也就是64个比特位):

          0- 0000000 00000000 00000000 00000000 - 00000000 00000000 00000000 00000000
          ↑                  ↑                                     ↑
      符号位 |<------- 时间戳(31 bit)-------->||<-------- 序列号(32 bit)-------->|


[*]符号位: 1 bit,永远为0(表示正数)
[*]时间戳:31 bit,以秒为单位,以2000年1月1日00时作为参照系,用当前时间 - 参照时间 = 时间戳,2^31 秒 约等于69年。
[*]序列号:31 bit,Redis自增的值,该方式理论上支持每秒产生 2^32 个差别ID。

[*] ​优点:高性能、简单。
[*] 缺点:依赖Redis可用性,需处理长期化标题。
[*] 优化:集群模式 + 多Key分片(如 order_id:shard_1)。
[*] 实现:
@Component
public class RedisIdGenerator {

    /**
   * 开始时间戳 2000年1月1日零时零分零秒
   */
    private static final long BEGIN_TIMESTAMP = 946684800L;

    /**
   * 序列号位数
   */
    private static final int COUNT = 32;

    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    public RedisIdGenerator(StringRedisTemplate stringRedisTemplate) {
      this.stringRedisTemplate = stringRedisTemplate;
    }

    public long nextId(String keyPrefix) {

      // 1.生成时间戳
      LocalDateTime now = LocalDateTime.now();
      long nowSecond = now.toEpochSecond(ZoneOffset.UTC);
      long timestamp = nowSecond - BEGIN_TIMESTAMP;

      // 2.生成序列号
      String date = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd")); // 使用冒号分割,方便统计
      Long increment = stringRedisTemplate.opsForValue().increment("icr:" + keyPrefix + ":" + date); // 加入日期,防止超出2^32上限(Redis自增上限为2^64)

      long incrementUnbox = 0L;
      if (increment != null) {
            incrementUnbox = increment;
      }

      // 3.拼接并返回
      return timestamp << COUNT | incrementUnbox; // 左移 32位,拼接时间戳和序列号
    }

    public static void main(String[] args) {
      // 2000年1月1日零时零分零秒 的秒数
      LocalDateTime time = LocalDateTime.of(2000, 1, 1, 0, 0, 0);
      long second = time.toEpochSecond(ZoneOffset.UTC);
      System.out.println("second = " + second); // second = 946684800
    }
}

5. 数据库号段模式



[*]原理:从数据库批量获取号段(如一次分配1000个ID),缓存在当地利用。
[*]​表设计:CREATE TABLE id_segment (
biz_tag VARCHAR(32) PRIMARY KEY,-- 业务标识
max_id BIGINT NOT NULL,         -- 当前最大ID
step INT NOT NULL               -- 每次步长
);

[*]优点:减少数据库压力、可扩展性强。
[*]缺点:需维护号段表,处理并发冲突。
6. Leaf(美团开源)​



[*]​混合模式:

[*]号段模式:类似数据库号段,依赖数据库。
[*]Snowflake模式:依赖ZooKeeper分配机器ID。

[*]优点:高可用、机动切换模式。
[*]实现:通过ZooKeeper管理机器ID,支持号段预分配。
四、计谋对比

计谋唯一性有序性性能可靠性典范场景UUID✅❌高✅日志跟踪、临时标识​数据库自增ID✅✅低❌(单点)中小规模分库分表Snowflake✅✅极高✅(去中心化)高并发订单、消息队列Redis INCR✅✅高❌(依赖Redis)短期唯一ID(如会话ID)​Leaf(美团)​✅✅高✅(混合模式)大规模分布式体系 五、选型建议

场景保举方案关键思量高性能、有序IDSnowflake处理时钟回拨(NTP同步、非常等候)简单、无序UUID v4得当临时标识依赖RedisRedis INCR需保障Redis高可用数据库友好、可控数据库号段模式得当中小规模分布式体系企业级复杂场景Leaf联合号段和Snowflake上风 实际选型建议

[*]​高并发有序ID:优先选择Snowflake或其变种(如美团的Leaf-Segment)。
[*]​简单临时标识:利用UUID v4(如用户临时Token)。
[*]​数据库友好场景:数据库号段模式(如分库分表的预分配ID)。
[*]​强一致性需求:联合Redis或ZooKeeper的分布式锁天生ID。
常见标题处理

[*]时钟回拨:

[*]方案:记录上次时间戳,发现回拨时抛出非常或等候时钟追上。
[*]优化:利用NTP同步服务器时间,或扩展Snowflake增加时间戳位数。

[*]机器ID分配:

[*]方案:ZooKeeper/设置中心动态分配,或按数据中心+机器ID编码。

根据业务规模、一致性要求及运维成本选择合适的计谋,必要时可组合利用多种方案。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: Redis之全局唯一ID