Redis 实现高并发全局唯一 ID 生成器(附完整代码)

在分布式系统中,我们经常需要生成全局唯一 ID:订单号、用户号、日志追踪 ID……如果还在用数据库自增 ID 或 UUID,要么性能不够,要么不易排序、浪费存储空间。
下面是一种Redis 自增 + 时间戳的高性能 ID 生成器实现方式,轻量、简单。


1. 为什么需要全局唯一 ID 生成器?

在单体应用时代,我们常用数据库的自增 ID。
但在分布式架构下,多个节点同时写数据库,或者有多个数据库分片时,就容易出现 ID 冲突或难以维护的主键策略。

常见方案:

  • UUID:天然唯一,但过长(32位十六进制),不可排序,存储索引效率低。
  • Snowflake 算法:经典的 Twitter 雪花算法,需要维护机器号、数据中心号,应用层实现较复杂。
  • Redis 自增:天然分布式,性能高,可按业务/时间维度生成序列号。

今天这篇文章就是基于 Redis 自增的方案。


2. 设计思路

我们希望一个 ID 既能:

  • 保证唯一:不同请求的 ID 不重复;
  • 趋势递增:便于按时间排序;
  • 高性能:在高并发下依旧稳定。

所以我们可以把 ID 分两部分:

高 32 位:时间戳(秒级),表示从某个固定时间点开始经过的秒数
低 32 位:序列号,表示当天第几个 ID

拼接公式:

return (时间戳 << 32) | 序列号

这样就得到一个 64 位 long 类型的 ID。


3. 完整代码实现

直接上代码:

/**
 * 全局唯一ID生成器
 */
@Component
public class RedisIdWorker {
    // 起始时间戳(2022-01-01 00:00:00 UTC)
    private static final long BEGIN_TIMESTAMP = 1640995200L;
    // 序列号占用位数
    private static final int COUNT_BITS = 32;

    private final StringRedisTemplate stringRedisTemplate;

    public RedisIdWorker(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    /**
     * 生成全局唯一ID
     * @param keyPrefix 业务前缀(如 "order"、"user")
     */
    public long nextId(String keyPrefix) {
        // 1. 生成时间戳(秒)
        long timeStamp = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC) - BEGIN_TIMESTAMP;

        // 2. 生成序列号:每天一个key,避免数字无限增长
        String date = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));
        long count = stringRedisTemplate.opsForValue()
                .increment("icr:" + keyPrefix + ":" + date);

        // 3. 拼接时间戳和序列号
        return timeStamp << COUNT_BITS | count;
    }
}

4. 关键点解析

4.1 BEGIN_TIMESTAMP

这是一个基准时间戳(UTC 秒)。我们只存储从这个时间点开始的秒数,可以少占用位数。
比如这里设定的是 2022-01-01 00:00:00 UTC

4.2 COUNT_BITS

序列号部分占用 32 位,表示一天最多可以生成 2^32 ≈ 42亿个 ID。
可以按需调整,如果业务量小,也可以调小。

4.3 “序列号回滚以天为单位”是什么意思?

这里每天生成的 Redis key 带有日期:

icr:order:2023:09:10

第二天自动用新的 key:

icr:order:2023:09:11

这样每天的序列号都从 1 开始计数(“回滚”),避免序列号无限增大。

4.4 使用 Redis 的好处

  • Redis 单线程自增操作 INCR 是原子性的;
  • 生成速度快(微秒级),支撑高并发;
  • 分布式多节点共用一个 Redis,天然全局唯一。

5. 使用示例

在业务代码中直接注入:

@Autowired
private RedisIdWorker redisIdWorker;

public void createOrder() {
    long orderId = redisIdWorker.nextId("order");
    // 保存订单时使用 orderId
}

Redis 中自动生成类似这样的 key:

icr:order:2023:09:10 = 10001

最终返回的 long 型 ID 在数据库中可存为 bigint,既短又可排序。


6. 对比其它方案

方案唯一性可排序存储效率实现复杂度
数据库自增简单(但不适合分布式)
UUID简单
Snowflake中等(需维护机器号)
Redis自增+时间戳简单、轻量

可以看到,这个方案非常适合中小型分布式系统。


7. 可能的优化

  • 多 Redis 节点容灾:用 Redis 集群或者哨兵模式保证高可用。
  • 批量预取:如果要减少 Redis 调用,可以批量拿一段序列号缓存在本地。
  • 毫秒级时间戳:如果业务需要更高精度,可以改成毫秒。

8. 总结

本文基于 SpringBoot + Redis,结合时间戳 + 自增序列号,实现了一个高并发全局唯一 ID 生成器,既保证唯一性、趋势递增,又轻量好用。

你可以把这个类直接丢进项目里,几乎零改动就能用,非常适合订单号、流水号、日志追踪 ID 等场景。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇