使用 Redis 生成分布式唯一ID

在分布式系统中,生成唯一ID是一个常见的需求。传统的数据库自增ID无法满足分布式系统的需求,因为多个节点可能同时生成ID,容易导致冲突。本文将介绍一种使用 Redis 实现分布式唯一ID的方法,并通过代码示例进行讲解。

一、背景介绍

1.1 唯一ID的需求

在电商系统中,每个订单、用户等都需要一个唯一的标识符。这些标识符不仅需要全局唯一,还要能够快速生成。因此,我们需要一个高效、可靠的方法来生成唯一ID。

1.2 传统方法的缺陷

  • 数据库自增ID:容易成为性能瓶颈,且不适用于分布式系统。
  • UUID:虽然能保证唯一性,但不适合用作数据库主键,因为它是16进制返回的是字符串并且长度较长且不连续,影响索引性能。

二、Redis 实现分布式唯一ID

2.1 方案设计

我们可以结合时间戳和自增序列来生成唯一ID。具体方案如下:

  1. 时间戳:使用当前时间戳,保证生成的ID大致有序。
  2. 自增序列:使用 Redis 的自增(INCR)命令,保证在同一秒内生成的ID是唯一的。

最终生成的ID格式如下:

XML 复制代码
ID = 时间戳 << 序列号位数 | 序列号

2.2 代码实现

下面是具体的代码实现:

java 复制代码
@Component
public class RedisIdWorker {
    private static final long BEGIN_TIMESTAMP = 1704067200L;
    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;
    }
}

在这段代码中:

  1. 生成时间戳 :通过 LocalDateTime.now().toEpochSecond(ZoneOffset.UTC) 获取当前时间戳,并减去一个起始时间戳(BEGIN_TIMESTAMP),得到相对时间戳。
  2. 生成自增序列 :通过 Redis 的 increment 方法实现每日的自增序列,保证在同一天内的ID是唯一的。
  3. 拼接ID:通过左移和按位或操作,将时间戳和自增序列拼接成最终的ID。

2.3 测试代码

为了验证我们的ID生成器是否能高效、正确地生成ID,我们可以编写以下测试代码:

java 复制代码
@Resource
private RedisIdWorker redisIdWorker;

private ExecutorService es = Executors.newFixedThreadPool(500);

@Test
void testIdWorker() throws InterruptedException {
    CountDownLatch latch = new CountDownLatch(300);
    Runnable task = () -> {
        for (int i = 0; i < 100; i++) {
            long id = redisIdWorker.nextId("order");
            System.out.println("id = " + id);
        }
        latch.countDown();
    };
    long begin = System.currentTimeMillis();
    for (int i = 0; i < 300; i++) {
        es.submit(task);
    }
    latch.await();
    long end = System.currentTimeMillis();
    System.out.println("耗时:" + (end - begin) + "ms");
}

在这段代码中,我们使用线程池并发生成ID,并统计生成时间,验证生成ID的效率。

三、总结

通过使用 Redis,我们可以高效地生成分布式唯一ID。该方法结合了时间戳和 Redis 的自增特性,保证了ID的有序性和唯一性。同时,Redis 的高性能也保证了ID生成的效率。

相关推荐
m0_60916049几秒前
mysql如何避免大批量数据修改锁全表_使用分批提交技术
jvm·数据库·python
玛卡巴卡ldf几秒前
【LeetCode 手撕算法】(动态规划)爬楼梯、杨辉三角、打家劫舍、完全平方数、零钱兑换、单词拆分、最长递增子序列、乘积最大子数组、分割等和子集
java·数据结构·算法·leetcode·动态规划·力扣
Languorous.1 分钟前
MySQL 零基础安装教程(Windows11/10,图文分步,新手零失败)
数据库·mysql·adb
wang3zc1 分钟前
golang如何实现工作流引擎_golang工作流引擎实现要点
jvm·数据库·python
m0_591364732 分钟前
如何在 Django ListView 中正确过滤当前用户的照片数据
jvm·数据库·python
ㄟ留恋さ寂寞2 分钟前
Vue.js核心基础之响应式系统与虚拟DOM渲染关联机制
jvm·数据库·python
iAm_Ike5 分钟前
怎么对MongoDB数据进行批量部分更新_BulkWrite机制与性能优化
jvm·数据库·python
weelinking7 分钟前
2026年三大主流大模型深度对比:GPT-5.5、Claude 4.6与DeepSeek V4谁更值得选择?
java·大数据·人工智能·git·python·gpt·github
橘子海全栈攻城狮8 分钟前
【最新源码】基于springboot的快递物流平台的设计与实现C102
java·开发语言·spring boot·后端·spring·web安全