使用MySQL排它锁来实现分布式全局唯一ID
最近在项目重构的过程中,遇到需要使用分布式全局唯一ID的地方。
首先来说一下什么是分布式全局唯一ID。分布式全局唯一ID指的是在分布式,高并发的情况下生成的全局唯一ID。具体可以考虑下面的场景进行理解。
假设有一个用户注册的功能,要为每个用户生成唯一的用户ID作为用户标识。表结构如下图所示。
CREATE TABLE `user` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`uid` bigint unsigned NOT NULL,
PRIMARY KEY (`id`),
unique key `uid` (`uid`)
)ENGINE=InnoDB;
为了应对大量用户注册的请求,我们启动了多个用户注册的服务,这些用户注册的服务都需要插入user表。那么问题来了,该如何保证uid的全局唯一性呢?
结合项目具体的情况(原有项目已经在运行,已经生成了原始数据),采用了MySQL排他锁来实现。
具体SQL如下
INSERT INTO user(uid) select max(uid)+1 from user for update;
通过使用 select ... for update 创建排它锁,保证在同一个时刻,其他的插入线程处于阻塞状态,从而保证了 max(uid)+1 生成的uid的全局唯一性。
完成了插入操作,如果我们还需要返回刚生成的uid,应该怎么做呢?
可以使用下面的SQL进行查询。
select uid from user where id=(select last_insert_id());
需要注意的是 last_insert_id() 函数只对当前插入的线程有效。如果插入完成后,其他线程来使用select last_insert_id() 进行查询,查询到的是这个线程自己上一次的插入值,是查询不到插入线程插入的值的。
其他分布式全局唯一ID的生成策略,可以查看这篇文章,写的很详细。
https://juejin.cn/post/6844904016141369352