用Redis来搞自增主键这事儿,怎么快速又靠谱地拿到唯一ID
- 问答
- 2026-01-06 17:37:39
- 19
这事儿说白了,就是想找一个又快又不容易重复的号码,给数据库里每一条新记录当身份证,用Redis来干这个,核心就是利用它单线程和内存操作的特性,保证速度,同时通过一些技巧来保证可靠性。
最基础的法子:直接用INCR命令
Redis里头有个命令叫INCR,你给它一个键,比如叫order_id,如果这个键不存在,Redis会先把它设为0,然后加1,返回1,下次你再对同一个键用INCR,它就返回2,以此类推,这个操作是原子性的,意思是哪怕有十万个请求同时让Redis给order_id加一,Redis也会排着队一个一个处理,绝对不会出现两个请求拿到同一个号码的情况,这一点是Redis单线程模型保证的,非常牢靠。
最简单的玩法就是:
- 你的应用程序需要生成一个新ID的时候,比如要新建一个订单,就向Redis发一个命令:
INCR order_id。 - Redis返回一个数字,比如是10086。
- 你的程序就把这个10086当作新订单的主键ID,塞进数据库。
这法子快是快极了,因为就是在内存里做个加法,但有个挺明显的毛病:这个ID太“裸”了,它就是一个从1开始一直往上增的数字,别人一眼就能看出来你大概有多少订单,如果你不小心把Redis的数据清空了(比如重启了),这个order_id键没了,下次再用INCR,它又会从1开始,这可就要出大乱子了,会产生重复的ID,光用INCR,只能算“快速”,离“靠谱”还差得远。
稍微靠谱点的法子:用INCRBY,搞一个大的步长
为了解决重启后ID重复的问题,有个改进的思路,我们不让ID从1开始慢慢爬,而是让它一次跳一大步,你的应用每次不是向Redis要一个ID,而是要一批ID。
具体操作是这样的:
- 应用程序先不急着生成ID,而是先向Redis发一个命令:
INCRBY order_id 1000,这个命令的意思是,给order_id一次性加上1000,假设原来order_id是0,那么Redis会返回1000。 - 你的应用程序就知道,现在它拥有了从1到1000这一千个ID的使用权,它可以在内存里维护一个计数器,从1开始分配,一直分配到1000。
- 等这1000个ID快用完的时候(比如用到950了),再向Redis发起下一次
INCRBY order_id 1000的请求,拿到1001到2000这一批ID。
这样做的好处是,大大减少了访问Redis的次数,原来生成一个ID就要通信一次,现在生成一千个ID才通信一次,速度更快,减轻了Redis的压力,更重要的是,即使Redis重启了,order_id键丢失了,你只需要在初始化的时候,去数据库里查一下当前最大的ID是多少,比如是25500,然后执行一下SET order_id 26000(设得比最大值大一些,留出缓冲),之后再继续用INCRBY,因为ID是一批一批申请的,中间有断档也没关系,只要保证新申请的这批ID比数据库里现有的所有ID都大就行,这样就避免了重复。

但这个法子还是有点小问题,就是ID还是连续的,序列性太强,不过在实际业务中,只要不泄露业务量,问题不大。
更常用也更工业化的法子:拼接ID
在实际生产环境中,很多人不会只用干巴巴的数字当主键,而是会把ID弄得更有意义、更健壮,一个非常常见的模式是利用Redis的INCR,但生成的是ID的一部分,然后和其他信息拼起来。
举个例子,比如生成订单ID:
- 把日期时间信息加进去,比如订单ID的前8位是
20240520(表示2024年5月20日)。 - 可能还会加上一些业务编码,比如电商业务是
01,直播业务是02。 - 再用Redis生成一个序列号,这里有个关键技巧:每天重置序列号,你的Redis键可以设计成
order_id:20240520,这样,每天的第一个订单,对这个键执行INCR,得到1,拼出来的ID可能就是20240520010001,第二天,键变成了order_id:20240521,INCR又从1开始,ID就是20240521010001。
这种拼接ID的法子好处非常多:

- 一目了然:从ID就能看出是哪天生成的,属于什么业务。
- 避免单键无限增长:每天的键是分开的,不会有一个键变得超级大。
- 自然分片:按日期分布的ID,对于数据库分库分表非常友好。
- 高可靠性:即使Redis完全宕机并且数据丢失,重启后,你只需要把当天的键初始化为一个值(比如从数据库查询当天已有最大ID的后几位,再加1),就能继续运行,因为日期部分已经变了,所以绝对不会和之前的ID重复。
关于可靠性的终极思考:Redis持久化
无论用上面的哪种法子,大家最担心的还是Redis万一断电或者宕机,内存里的数据没了怎么办,这就涉及到Redis的可靠性保障机制:持久化。
Redis有两种主要的持久化方式:
- RDB(快照):隔一段时间把内存里的数据拍个照,存到硬盘上一个叫
dump.rdb的文件里,如果Redis挂了,重启的时候可以加载这个文件来恢复数据,问题是,如果刚拍完快照,然后生成了很多新ID,还没来得及拍下一个快照就宕机了,那这段时间的新ID就丢了,这会导致ID不连续,虽然不会重复(因为INCR的值已经变大了,恢复后是从旧值开始继续增),但中间会有空洞。 - AOF(追加日志):把每一个写命令(比如
INCR)都记到一个日志文件里,Redis重启的时候,把日志里的命令重新执行一遍,就能恢复数据,你可以配置AOF的策略,比如每秒钟同步一次日志到硬盘,这样最多丢失一秒内产生的数据(包括生成的ID),这种方式的可靠性就高多了。
为了保证生成ID这件事绝对靠谱,通常的建议是:
- 启用AOF持久化,并配置为每秒同步,这在性能和可靠性之间是个不错的平衡。
- 如果条件允许,搭建Redis主从复制(Replication),主Redis负责写(生成ID),从Redis实时同步数据,万一主库挂了,可以手动或自动切换到从库,服务几乎不受影响。
总结一下
用Redis搞自增主键,核心优势是快,要想同时做到靠谱,不能只靠一个简单的INCR命令。
- 对于小应用,可以接受偶尔重启后手动干预,用
INCRBY批量获取是个不错的折中方案。 - 对于正式的生产环境,结合日期和业务编码进行拼接,并且让序列号按日重置,是更通用、更健壮的做法。
- 而这一切的基石,是配置好Redis的AOF持久化,甚至搭建主从集群,来确保生成ID这个服务本身是高可用的。
只要把这些点都考虑到了,用Redis来生成唯一ID就是一件既快速又靠谱的事情。
本文由度秀梅于2026-01-06发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:https://haoid.cn/wenda/75700.html
