用Redis咋整流水号生成器,简单又高效的实现思路分享
- 问答
- 2026-01-11 14:49:37
- 4
用Redis搞一个流水号生成器,这事儿说白了,就是想找个又快又稳还不重样的法子来生成像订单号、业务编号这样的东西,Redis这玩意儿,因为所有数据都能放在内存里折腾,速度飞快,再加上它自己带的一些原子性操作,天生就适合干这个,下面我就直接开讲几种简单又直接的实现路子。
最基础的法子:用INCR命令
这个是最简单,也是最常用的一招,Redis里头有个命令叫 INCR,你给它一个key,比如我给它一个 order_id,它就能给这个key对应的数字自动加1,最关键的是,这个操作是“原子性”的(来源:Redis官方文档对INCR命令的说明),啥叫原子性?就是说,就算你有一万个人同时让Redis给 order_id 加1,Redis也会排好队,一个接一个地处理,绝对不会出现两个人拿到同一个号码的情况,这就从根本上避免了重复。
具体用起来特别省心:
- 一开始,你可以用
SET order_id 1000来设一个初始值,比如从1000开始。 - 以后每次需要新号码,就直接发一个
INCR order_id命令。 - Redis会返回给你增加后的新值,比如1001、1002...,这个就是你想要的流水号。
这个法子好处是真心简单,几行代码就搞定,而且靠着Redis的单线程模型,绝对不会出乱子,但缺点也挺明显,就是生成的号码太简单了,就是纯数字往上累加,如果业务要求流水号里得带上日期、或者区分不同业务类型,光靠它就有点不够用了。
稍微复杂点但更实用:组合键和日期重置
很多时候,我们希望的流水号是像 “ORD202405240001” 这种样子的,里面包含了日期信息,这样一看就知道是哪天生成的,而且每天的数字都从头开始计数,管理起来也方便。
这个思路的关键在于,我们不能再死守着一个key了,得让key“动起来”,比如把日期做到key的名字里,具体可以这么操作:
- 每天生成一个全新的key,key的名字可以是
order_id:20240524,这里的“20240524”就是当天的日期。 - 每天第一次需要生成流水号的时候,对这个带日期的key执行一个操作:
INCR order_id:20240524,因为这天这个key是第一次出现,Redis会把它当成0来处理,然后加1,返回1,如果我们希望号码是4位数从0001开始,可以配合另一个命令SETNX(来源:Redis官方文档对SETNX命令的说明),这个命令的意思是“如果key不存在才设置”,我们可以先SETNX order_id:20240524 0,如果设置成功了(返回1),说明是今天第一个号码,那这个key的值就是0;如果没成功(返回0),说明这个key已经存在了,就不用管了,紧接着再INCR,就能得到从1开始递增的号码了。 - 把日期和
INCR得到的数字拼起来,比如日期是20240524,INCR得到的是15,那流水号就可以是ORD202405240015。
这个方法的好处是流水号里带了日期信息,一目了然,而且每天的计数都是独立的,不会无休止地增长下去,缺点就是需要每天生成新的key,不过Redis处理这个是小菜一碟,你需要在自己程序里处理好日期的切换和号码的拼接格式(比如数字不足4位前面补零)。
应对突发情况:给号码留点余地
上面那个按天生成key的法子已经挺好了,但万一出现一种极端情况:比如在半夜23点59分生成了一个号码 ORD202405240888,然后紧接着在凌晨0点0分系统日期切换了,生成了 ORD202405250001。ORD202405240888 这个单子因为某种原因提交失败,需要重新生成一个号码,但此时日期已经变了,它如果再取号,就会变成 ORD202405250002,这就和之前的号码不连续了,对于一些严格要求单据编号绝对连续的场景,这可能是个问题。
为了解决这种“时间点临界”问题,可以引入一个“缓冲期”或者叫“号段”的概念(这种预分配号段的思想在分布式ID生成中很常见,比如百度开源的UidGenerator,来源:其项目文档或相关技术文章),咱们可以不用搞得那么复杂,但可以借鉴一下思路:
我们不用每次生成一个号码都去调一次Redis,可以一次性向Redis“申请”一小段号码回来,比如一次性要1000个,程序拿到这1000个号码(比如从5001到6000)后,就在自己内存里慢慢分配,这1000个号码没用完之前,不需要再联系Redis。
这样做的好处是:
- 大大减少了和Redis直接通信的次数,性能更高,尤其是在需要频繁生成号码的场景下。
- 因为一批号码在本地内存里,所以即使Redis网络偶尔有点小抖动,或者日期刚刚切换,只要当前这批号还没用完,本地仍然可以继续生成连续的号码,有效避免了上面提到的临界点问题。
实现上可以这样:
- 在Redis里还是用一个key来记录当前最大分配到的号码,
global_order_id。 - 当程序需要号码时,不是用
INCR,而是用INCRBY命令(来源:Redis官方文档对INCRBY命令的说明),INCRBY global_order_id 1000,这个命令会一次性把global_order_id的值增加1000,并返回增加后的值(假设是6000)。 - 程序就知道,自己拿到了从6000 - 1000 + 1 = 5001 到 6000 这1000个号码的使用权,然后程序在内存里从5001开始一个个地分配,直到6000。
- 这1000个用完了,再去Redis申请下一批。
这个法子稍微复杂一点,因为你要在程序里管理好本地这个号段,防止程序重启导致号段丢失(可能意味着一小部分号码被浪费),但对于高并发、且对连续性有要求的场景,这是个非常有效的优化。
总结一下
用Redis搞流水号生成,核心就是利用它单线程原子操作不怕并发的特性。
- 如果需求简单,就是纯数字一直往上走,用
INCR最简单。 - 如果希望流水号包含日期、业务类型等信息,并且每天重置,就用“组合key+INCR”的方式。
- 如果并发量特别大,或者对号码连续性有严苛要求,怕遇到临界点问题,可以考虑“批量申领号段”的方式,用空间换时间和稳定性。
选哪种,就看你的实际业务场景和复杂度要求了,Redis在这方面确实是个得心应手的工具。

本文由帖慧艳于2026-01-11发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:http://waw.haoid.cn/wenda/78745.html
