基于Redis实现全局唯一Id

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 基于Redis 的INCR 命令生成分布式全局唯一id。基于redis实现全局唯一ID,性能还是非常高的,并且耗时非常短的。

一、简介
使用数据库自增ID就存在一些问题:
1、受单表自增数量的限制;
原因:mysql单表的容量不宜超过500W条,数据量过大之后,就需要进行拆库拆表,但拆分表之后,它们从逻辑上讲仍然是同一张表,所以他们的id是不能一样的(不同表,若使用自增ID,是可能一样的),所以随着我们的业务数据越来越大,我们需要保证id的唯一性。
2、id的规律性太明显。
原因:自增id具有太明显的规则,用户或者说商业对手很容易猜测出来一些敏感信息,例如:在一天时间内,我们卖出了多少单,这明显不合适。
二、全局唯一ID生成策略
640.png
一般要满足下列特性:
640 (1).png
为了增加ID的安全性,可以不直接使用Redis自增的数值,而是拼接一些其它信息:
640 (2).png
组成说明:

1、符号位:1bit,永远为0;

2、时间戳(31 Bit):31bit,以秒为单位,可以使用69年;

3、序列号:32bit,秒内的计数器,支持每秒产生2^32个不同ID。
三、基于Redis实现全局唯一Id案例
原理:基于Redis 的INCR 命令生成分布式全局唯一id。INCR 命令主要有以下2个特征:
1、具备了“INCR AND GET”的原子操作,即:增加并返回结果的原子操作。这个原子性很方便我们实现获取ID。
2、Redis是单进程单线程架构,INCR命令不会出现id重复。
3.1、构建RedisIdUtils类

/**
 * 功能描述:使用redis生成全局唯一ID
 * @Author: yyalin
 * @CreateDate: 2023/8/13 11:35
 */
@Component
public class RedisIdUtils {
    //预生成开始时间戳
    private static final long BEGIN_TIMESTAMP = 1640995200L;
    //序列号的位数
    private static final int COUNT_BITS = 32;
    //redis提供的字符串
    private StringRedisTemplate stringRedisTemplate;
    //有参构造函数
    public RedisIdUtils(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }
    /**
     * 功能描述:根据keyPrefix前缀生成对应业务的全局唯一ID
     * @MethodName: nextId
     * @MethodParam: [keyPrefix:使用前缀来区分不同的业务]
     * @Return: long
     * @Author: yyalin
     * @CreateDate: 2023/8/13 12:20
     */
    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.自增长 icr:order:2023:08:13
        long count = stringRedisTemplate.opsForValue().increment("icr:" + keyPrefix + ":" + date);
        // 3.拼接并返回  timestamp << COUNT_BITS :向左移动32位
        //原本时间戳在低位上,通过向左移动32位,变位到高位存储,低32位都是0,然后与自增序列按位操作
        //形成低32位为序列号。
        return timestamp << COUNT_BITS | count;
    }
}

3.2、构建多线程测试类

**
 * @Description: TODO:测试RedisIdUtils
 * @Author: yyalin
 * @CreateDate: 2023/8/13 12:27
 * @Version: V1.0
 */
@Service
@Slf4j
public class TestRedisIdUtils {
    @Autowired
    private RedisIdUtils redisIdUtils;
    //使用自定义的线程池
    private ExecutorService executorService = Executors.newFixedThreadPool(500);
    /**
     * 功能描述:使用多线程测试生成40000条数据耗时
     * @MethodName: testIdWorker
     * @MethodParam: [nums]
     * @Return: void
     * @Author: yyalin
     * @CreateDate: 2023/8/13 12:36
     */
    public void testIdWorker(int nums) throws InterruptedException {
        //同步协调在多线程的等待于唤醒问题 分线程全部走完之后,主线程再走
        CountDownLatch latch = new CountDownLatch(nums);
        Runnable task = () -> {
            for (int i = 0; i < 100; i++) {
                long id = redisIdUtils.nextId("order");
                System.out.println("id = " + id);
            }
            latch.countDown();
        };
        long begin = System.currentTimeMillis();
        for (int i = 0; i < nums; i++) {
            executorService.submit(task);
        }
        //阻塞方法 让main线程阻塞
        latch.await();
        long end = System.currentTimeMillis();
        log.info("生成"+nums*100+"条id共计耗时(毫秒) = " + (end - begin));
    }
}

3.3、测试结果
640 (3).png

QQ截图20230814181210.png

总结:从测试结果不难看出,基于redis实现全局唯一ID,性能还是非常高的,并且耗时非常短的。

更多优秀文章,请关注个人微信公众号或搜索“程序猿小杨”查阅。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
5月前
|
缓存 负载均衡 NoSQL
Redis系列学习文章分享---第十四篇(Redis多级缓存--封装Http请求+向tomcat发送http请求+根据商品id对tomcat集群负载均衡)
Redis系列学习文章分享---第十四篇(Redis多级缓存--封装Http请求+向tomcat发送http请求+根据商品id对tomcat集群负载均衡)
82 1
|
5月前
|
NoSQL Redis
Redis系列学习文章分享---第五篇(Redis实战篇--优惠券秒杀,全局唯一id 添加优惠券 实现秒杀下单 库存超卖问题分析 乐观锁解决超卖 实现一人一单功能 集群下的线程并发安全问题)
Redis系列学习文章分享---第五篇(Redis实战篇--优惠券秒杀,全局唯一id 添加优惠券 实现秒杀下单 库存超卖问题分析 乐观锁解决超卖 实现一人一单功能 集群下的线程并发安全问题)
127 0
|
6月前
|
存储 数据采集 NoSQL
Redis入门到通过之Redis实现全局唯一id
Redis入门到通过之Redis实现全局唯一id
125 0
|
6月前
|
NoSQL 算法 Java
【Redis】4、全局唯一 ID生成、单机(非分布式)情况下的秒杀和一人一单
【Redis】4、全局唯一 ID生成、单机(非分布式)情况下的秒杀和一人一单
219 0
|
6月前
|
存储 监控 负载均衡
深入Redis Streams:解密神秘的消息ID
深入Redis Streams:解密神秘的消息ID
139 0
|
消息中间件 存储 NoSQL
阿里云国际站代理商:使用Redis实现一个分布式的全局ID怎么操作?
阿里云国际站代理商:使用Redis实现一个分布式的全局ID怎么操作?Redis(Remote Dictionary Server)是一款基于内存的高性能键值对(Key-Value)存储系统。它支持多种数据结构,如字符串、整数、浮点数等,具有高速读写、持久化、分布式等特点。在很多场景下,Redis可以作为数据库、缓存、消息队列等多种应用的数据存储方案。本文将介绍如何使用Redis实现一个分布式的全局ID生成器。
|
存储 数据采集 NoSQL
Redis 实现全局唯一id
Redis 实现全局唯一id
|
NoSQL 算法 Java
高并发下分布式ID各个的解决方案以及redis集群分布式ID代码实现
高并发下分布式ID各个的解决方案以及redis集群分布式ID代码实现
高并发下分布式ID各个的解决方案以及redis集群分布式ID代码实现
|
NoSQL 安全 Java
分布式唯一ID系列(4)——Redis集群实现的分布式ID适合做分布式ID吗
首先把我用Redis实现分布式Id的最终项目地址列出来: https://github.com/maqiankun/distributed-id-redis-generator 关于Redis集群生成分布式ID,这里要先了解redis使用lua脚本的时候的EVAL,EVALSHA命令: https://www.
2887 0
|
NoSQL Redis
分布式Id - redis方式
本篇分享内容是关于生成分布式Id的其中之一方案,除了redis方案之外还有如:数据库,雪花算法,mogodb(object_id也是数据库)等方案,对于redis来说是我们常用并接触比较多的,因此主要谈谈结合redis生成分布式id方案。
5368 0