自学内容网 自学内容网

第十四章 Redis之全局唯一ID(分布式集群)

目录

一、概念

‌二、全局唯一ID的生成方法‌

三、Redis生成全局ID

3.1. 生成策略

3.2. 代码


一、概念

全局唯一ID是指在分布式系统中,每个实体都有一个唯一的标识符,确保在不同的节点或服务之间能够唯一标识一个实体。这种唯一性对于数据的一致性和系统的可靠性至关重要。例如,在电商系统中,每个订单、用户和商品都需要一个全局唯一的ID来区分和管理。

全局ID生成器,是一种在分布式系统下用来生成全局唯一ID的工具,一般要满足下列特性:

        

‌二、全局唯一ID的生成方法

1. UUID‌:UUID是一种128位长的数字,它保证了在全球范围内的唯一性。UUID通过算法生成,理论上重复的概率极低。在‌Java中,可以通过java.util.UUID类来生成UUID。

2‌. Redis自增‌:Redis作为一个高性能的键值存储系统,可以通过INCR命令来生成唯一的ID。这种方法适用于需要高并发生成ID的场景。

3. 雪花算法(Snowflake)‌:雪花算法由‌Twitter开发,能够在分布式系统中生成唯一的ID。它结合了时间戳、工作机器ID和序列号来保证ID的唯一性和有序性。

‌4. 数据库自增‌:一些数据库系统提供了自增字段功能,例如MySQL的AUTO_INCREMENT,这种方法简单易用,但在分布式系统中需要额外的同步机制来保证ID的唯一性。

应用场景和优缺点

UUID‌:优点是生成简单,无需中心化管理;缺点是长度较长,且生成性能较低,不适合大量生成ID的场景,且生成的ID中包含字符,不适合作为订单ID等场景。

Redis自增‌:优点是高性能,适合高并发场景;缺点是依赖于Redis服务器的稳定性和网络延迟。

雪花算法‌:优点是高性能和高并发,能够生成有序的ID;缺点是需要维护好时间同步和机器ID的分配。

数据库自增‌:优点是简单易用;缺点是在分布式系统中需要额外的同步机制,且单点压力较大。

三、Redis生成全局ID

3.1. 生成策略

我们为了增加ID的安全性,可以不直接使用Redis自增的数值,而是拼接一些其它信息:

ID的组成部分:

符号位:1bit,永远为0

时间戳:31bit,以秒为单位,可以使用69年

序列号:32bit,秒内的计数器,支持每秒产生2^32个不同ID 

该生成策略是实际项目中运用比较普遍的方案,保证了足够的安全性,另外通过时间戳作为组合还有额外的好处:

1. 能够限制这个生成ID的上限,即一天下单的量的最大值为2^32,那么对于像淘宝这种双十一当天的秒杀场景,也能够完全满足。

2. 通过时间戳作为组合,我们想要查询某一天或者某个范围天数内的订单,也更为方便。

因此,我们通过对Redis自增的方式按上述方式进行优化后,再对Redis进行分布式集群部署来生成全局唯一ID,除了能满足要求的五大特性外,对于我们日常的诸如订单之类业务的查询需求,也能充分满足。

3.2. 代码

package com.hmdp.utils;

import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;

@Component
public class RedisIdWorker {
    /**
     * 开始时间戳
     */
    private static final long BEGIN_TIMESTAMP = 1640995200L;
    /**
     * 序列号的位数
     */
    private static final int COUNT_BITS = 32;

    private StringRedisTemplate stringRedisTemplate;

    public RedisIdWorker(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.生成序列号
        // 2.1.获取当前日期,精确到天
        String date = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));
        // 2.2.自增长
        long count = stringRedisTemplate.opsForValue().increment("icr:" + keyPrefix + ":" + date);

        // 3.拼接并返回
        return timestamp << COUNT_BITS | count;
    }
}

转换成2进制的效果如下: 


原文地址:https://blog.csdn.net/qushaming/article/details/142735140

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!