并发导致重复数据与id生成器生成id冲突解决方法

1 篇文章 0 订阅
1 篇文章 0 订阅

用户注册流程

在这里插入图片描述

大致流程是:判断手机号是否存在,不存在则新增;id生成依赖micro-basic-service微服务的id生成器,拿到id后用户中心服务再执行插入用户记录。
虽然加了事务,此流程并发时会存在一些问题。
并发场景:
在这里插入图片描述

第一个问题:id生成失败

micro-basic-service 会报id生成失败。
原因:两个事务/进程同时来到第④步,假如查到的table_id=10,然后进程1率先执行第⑤步,把table_id自增1最终更新数据为11;此时,进程2执行到第⑤步,设置table_id = 11,会导致update rows affected = 0 引起id生成失败。

并发模拟如下

在这里插入图片描述在这里插入图片描述
并发执行手机号注册,出现了一个失败一个成功的情况。
解决思路:
把第④和第⑤步做成原子,对查询做排它锁,使用排它锁 for update。
在这里插入图片描述
实际代码如下
在这里插入图片描述

对于并发导致的id生成失败的问题就解决了,但是还存在另一个问题。
在这里插入图片描述
可以看到,并发两个注册,虽然不报数据库主键生成失败的错误了,但是还存在另外的问题,就是重复注册。

第二个问题:重复注册

问题的关键在于

select mobile ... where mobile = 15218621444
if !mobile {
    getNextId
    insert
}

两个进程同时来到第②步,发现不存在手机号,并发走到第③至第⑥的最终插入,由于手机号没做唯一索引,引起了相同手机号注册。
只要通过了第②步,就会进入注册逻辑。
解决思路:
同个手机号并发注册时,同时只有一个进程通过第②步。
一开始我给第②步,加上select mobile … where mobile = 15218621444 for update,结果是不生效的,实验后,发现对于不存在记录的 select for update,数据库不知道你要锁哪一行,也不会阻塞表。
而后又想了这些方案:
(1)用锁表解决,比如select count(1) where 1 … for update。让他阻塞,但是锁表性能不行。
(2)另外一种是用redis缓存锁排队。
(3)还有一种最简单的是加唯一索引。
(4)对于第一种锁表的改造,用冷数据一定能命中查询来致使锁行阻塞。

如select id order by id asc limit 1 … for update ,然后再执行后面的业务逻辑
select mobile … where mobile=15218621454;
if !mobile
{
insert …
}
牺牲一个冷数据的性能来做排它锁。
于是我试验了第四种方案,并得到验证是可行的。
在这里插入图片描述

实际代码如下
在这里插入图片描述在这里插入图片描述
并发执行效果:
在这里插入图片描述
并发执行,只有一个成功。

总结:

对于先查询,后修改的场景,可以使用排它锁保证代码段落原子性。

个人觉得,多个服务之间的调用,服务2是id生成器是生成id并返回,即使服务1事务失败也不需要回滚服务2的事务,所以这个案例不需要用到分布式事务的技术。

关于 for update 的细节我整理了一篇文章如下:
数据库 for update的作用:https://blog.csdn.net/w786572258/article/details/117923902

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
MyBatis-Plus的ID生成策略中,如果使用的是雪花算法(Snowflake),它是Twitter开源的一种分布式ID生成方案,主要为了解决并发生成重复ID问题。在集群部署的情况下,可能会遇到ID重复的问题,因为每个节点都有自己的时间戳和序列号生成ID解决ID重复方法通常包括: 1. **配置全局唯一ID**: MyBatis-Plus提供了一个全局唯一ID生成器`SnowflakeIdWorker`,它会根据节点ID和毫秒数生成一个64位的ID。确保所有节点都配置相同的集群ID和序列号,这样可以保证ID的一致性。 ```java // 雪花算法配置示例 GlobalConfig global = new GlobalConfig(); global.setIdGenerator(new SnowflakeIdWorker(0, 0)); // 集群ID和序列号 ConfigUtil.initGlobal(global); ``` 2. **数据库层面的唯一索引**: 对于自增ID,可以在数据库表的主键字段上创建一个唯一索引,当插入操作发现该ID已经存在时,可以回滚事务或更新失败的记录。 3. **分布式锁**: 在高并发场景下,可能需要考虑使用分布式锁来确保同一时间只有一个节点生成新的ID,其他节点等待锁释放后才生成。 4. **分片策略**: 如果应用数据量非常大,可以考虑按照一定的规则对数据进行分片,每个分片使用独立的ID生成器,降低冲突概率。 相关问题: 1. 雪花算法是如何保证ID全局唯一的? 2. 在分布式环境中,如何配置MyBatis-Plus避免ID重复? 3. 分布式锁如何配合雪花算法在MyBatis-Plus中使用?
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值