MySQL的INSERT ... FOR UPDATE你用过吗
引言
了不起最近处理了一个生产问题,具体情况是这样的:
就是两个sql语句在同一个代码块中,第一个sql语句是对设备状态根据设备ID批量删除,第二个sql语句是批量插入,插入的设备ID和删除的设备ID是同一个。
本地和测试环境当时都好好的,可是上了生产环境,报错了

错误是存在重复的唯一索引,代码逻辑很简单

可以看到就是一个批量删除,再批量插入,测试环境当时没有用多个实例进行测试,上产是有多个实例的,错误的发生情况当天下班后一直分析到晚上12点,终于分析出来了,如下图:

可以看到是同时来了两个请求,分别落到了2台机器,这段代码在2个机器上执行就不能保证顺序性了,这个要加锁的话也是分布式锁。
不过还可以通过数据库的行锁来实现,接下来我们就一起来看看INSERT ... FOR UPDATE语句。
MySQL的 INSERT ... FOR UPDATE语句可以用于在插入新行的同时对这些行进行加锁,从而防止其他事务对这些行进行修改。下面简要介绍 INSERT ... FOR UPDATE语句的原理和应用场景,并举例说明其具体用法。
一、INSERT ... FOR UPDATE语句的原理
INSERT ... FOR UPDATE语句会在执行插入操作时,对插入的新行进行排他锁(Exclusive Lock)的加锁操作,从而避免其他事务对这些新行进行修改。
这种锁定机制同样是基于MySQL的事务隔离级别实现的,只有在可重复读(REPEATABLE READ)和串行化(SERIALIZABLE)隔离级别下,INSERT ... FOR UPDATE语句才能生效。
二、INSERT ... FOR UPDATE语句的应用场景
- 防止重复插入
在某些业务场景下,可能需要对某些数据进行唯一性约束,避免重复插入相同的数据。使用 INSERT ... FOR UPDATE语句可以避免并发的情况下,多个事务同时插入相同的数据。
- 实现悲观锁
在某些业务场景下,需要对某些数据进行严格的控制,确保数据不会被其他事务修改。使用 INSERT ... FOR UPDATE语句可以实现悲观锁,确保数据的安全性。
- 实现乐观锁
使用 INSERT ... FOR UPDATE语句可以实现乐观锁机制,即在插入数据的同时,记录数据的版本号或时间戳等信息,如果在插入时发现版本号或时间戳已经存在,则说明其他事务已经插入了相同的数据,需要进行相应的处理。
三、INSERT ... FOR UPDATE语句的应用举例
下面举例说明 INSERT ... FOR UPDATE语句的具体用法。
假设有一个用户表user,其中包含用户ID和用户名两个字段,现在需要往用户表中插入新用户,假设插入流程如下:
- 根据用户ID查询用户是否已经存在
- 如果用户不存在,则插入新用户
在这个过程中,需要对用户ID进行唯一性约束,避免重复插入相同的用户。
可以使用如下的SQL语句来实现插入操作:
START TRANSACTION;
SELECT * FROM user WHERE user_id = '123' FOR UPDATE;
INSERT INTO user(user_id, username) VALUES ('123', 'John');
COMMIT;
在这个SQL语句中,SELECT ... FOR UPDATE语句用于对选定的行进行加锁,防止其他事务对这些行进行修改,从而避免重复插入相同的用户。如果查询结果为空,则可以插入。
四、生产问题的解决
了不起这边处理的方式就很简单了,直接将批量插入sql的语句末尾加上了 FOR UPDATE。
语法参考:
insert into tablename values() on duplicate key update columnName = valueName;
由于具体涉及公司隐私就不贴sql语句了,原理就是当执行插入的时候,如果已经有这条数据存在,那么就更新它,就不会存在说已经插入了的数据在插入一条会报重复Key这个情况。
简单的INSERT ... FOR UPDATE你掌握了吧,后面我们继续学习。
- 点赞
- 收藏
- 关注作者
评论(0)