解读mysql的for update用法
作者:韩师学子--小倪
mysql的for update用法
for update是在数据库中上锁用的,可以为数据库中的行上一个排它锁。
当一个事务的操作未完成时候,其他事务可以读取但是不能写入或更新。
for update的使用场景
如果遇到存在高并发并且对于数据的准确性很有要求的场景,是需要了解和使用for update的。
比如涉及到金钱、库存等。一般这些操作都是很长一串并且是开启事务的。
如果库存刚开始读的时候是1,而立马另一个进程进行了update将库存更新为0了,而事务还没有结束,会将错的数据一直执行下去,就会有问题。
所以需要for upate 进行数据加锁防止高并发时候数据出错。
记住一个原则:一锁二判三更新
for update如何使用
使用姿势:
select * from table where xxx for update
for update的锁表
InnoDB默认是行级别的锁,当有明确指定的主键时候,是行级锁。否则是表级别。
例子:
假设表foods ,存在有id跟name、status三个字段,id是主键,status有索引。
例1: (明确指定主键,并且有此记录,行级锁)
SELECT * FROM foods WHERE id=1 FOR UPDATE; SELECT * FROM foods WHERE id=1 and name='咖啡色的羊驼' FOR UPDATE;
例2: (明确指定主键/索引,若查无此记录,无锁)
SELECT * FROM foods WHERE id=-1 FOR UPDATE;
例3: (无主键/索引,表级锁)
SELECT * FROM foods WHERE name='咖啡色的羊驼' FOR UPDATE;
例4: (主键/索引不明确,表级锁)
SELECT * FROM foods WHERE id<>'3' FOR UPDATE; SELECT * FROM foods WHERE id LIKE ‘3' FOR UPDATE;
for update的注意点
1.for update 仅适用于InnoDB,并且必须开启事务,在begin与commit之间才生效。
2.要测试for update的锁表情况,可以利用MySQL的Command Mode,开启二个视窗来做测试。
for update的疑问点
当开启一个事务进行for update的时候,另一个事务也有for update的时候会一直等着,直到第一个事务结束吗?
答:会的。除非第一个事务commit或者rollback或者断开连接,第二个事务会立马拿到锁进行后面操作。
如果没查到记录会锁表吗?
答:会的。表级锁时,不管是否查询到记录,都会锁定表。
for update和for update nowait区别(前者阻塞其他事务,后者拒绝其他事务)
for update锁住表或者锁住行,只允许当前事务进行操作(读写),其他事务被阻塞,直到当前事务提交或者回滚,被阻塞的事务自动执行
for update nowait 锁住表或者锁住行,只允许当前事务进行操作(读写),其他事务被拒绝,事务占据的statement连接也会被断开
mysql排它锁(FOR UPDATE) 场景
场景一
当前使用for UPDATE查询,其他地方查询 -- 其他地方也使用for UPDATE会堵塞,其他地方未使用for UPDATE不会堵塞
1. 当前A(不区分是否为事务里)使用for UPDATE查询
SELECT * FROM saas_employee_label_person where id = 1 for UPDATE
1.1 其他地方B(不在上一个连接里或事务里,不区分是否为事务里)使用for UPDATE 查询(需要获取锁)
SELECT * FROM saas_employee_label_person where id = 1 for UPDATE
会发生堵塞等待前一个A锁释放(A事务提交,或者A结束运行(A为非事务场景))
1.2 其他地方C(不区分是否为事务里)不使用for UPDATE 查询(无需获取锁)
SELECT * FROM saas_employee_label_person where id = 1
不会堵塞,可以查询
场景二
当前使用for UPDATE查询,其他地方UPDATE更新 -- 会堵塞等待
2. 当前A(不区分是否为事务里)使用for UPDATE查询
SELECT * FROM saas_employee_label_person where id = 1 for UPDATE
2.1 其他地方B(不区分是否为事务里)使用UPDATE更新数据
UPDATE saas_employee_label_person set employee_id = 1111 where id = 1;
会发生堵塞等待前一个A锁释放(A事务提交,或者A结束运行(A为非事务场景))
场景三
当前事务使用UPDATE更新,其他地方使用for UPDATE查询 -- 会堵塞等待
3. 当前事务A 使用UPDATE更新
BEGIN; UPDATE saas_employee_label_person set employee_id = 11121 where id = 1;
3.1 其他地方B(不区分是否为事务里)使用 for UPDATE查询
SELECT * FROM saas_employee_label_person where id = 1 for UPDATE
会发生堵塞等待前一个事务A提交才可以获得锁
场景四
当前非事务使用UPDATE更新,其他地方使用for UPDATE查询 -- 不会堵塞
4. 当前A(非事务下) 使用UPDATE更新
UPDATE saas_employee_label_person set employee_id = 11121 where id = 1;
4.1 其他地方B(不区分是否为事务里)使用 for UPDATE查询
SELECT * FROM saas_employee_label_person where id = 1 for UPDATE
不会堵塞,前一个update先执行完,所以不影响,这里一定是update先执行完在走for UPDATE查询的场景,否者就是场景二了
场景五
当前普通查询,其他地方使用for UPDATE查询 -- 不影响,不会堵塞
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。