在MySQL Innodb引擎事务处理中使用共享锁,排他锁(读写锁)
文章目录
文档更新说明
- 最后更新 2018年04月27日
- 首次更新 2018年04月27日
前言
忽然想起了去年在项目的并发处理中遇到了一次更新丢失的问题, 感觉最近又可能会接触到这部分开发, 赶紧整理一下资料学习学习~
共享锁,排他锁
数据库领域里边好好几种锁,本文只针对两种锁来讲.共享锁又称读锁,排他锁又称写锁.锁有表锁和行锁,本文只针对事务里的行锁.
MySQL事务中,行锁有两个语法
select *** lock in share mode 这个是设置共享锁(读锁)
select *** for update 这个是设置读锁排他锁(写锁)
两种锁的作用
- 加共享锁, 允许其他进程跟着加共享锁, 但是其他进程修改数据会堵塞, 其他进程加排他锁也会堵塞.
- 加排他锁, 允许其他进程直接读数据, 但是不允许其他进程加共享锁 or 加排他锁.当然也不允许其他进程修改数据.
其实解决我遇到的更新丢失问题,用排他锁就可以解决了.我有多个事务同时读取,并准备修改数据.只需要保证读取的时候加了排他锁,这样其他事务就无法在加排他锁的情况下读取了.这样就避免了更新丢失问题了.
创建一个用于测试的表
CREATE TABLE `user_charge` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` varchar(255) DEFAULT NULL,
`llt_in` bigint(20) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `user_id` (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1;
其中的llt_in, 就是我们的要用来修改的字段.(不要纠结名字,我只是直接把项目的数据表复制出来修改而已😌.)
使用写锁的效果
测试读锁
case1
打开终端, 进入mysql,开始我们的效果演示.
session1:
set autocommit = 0;
begin; //开启事务
select * from user_charge where user_id = 1 lock in share mode\G;
session2:
select * from user_charge where user_id = 1 lock in share mode\G;
此时session2可以直接顺利读取.说明同一行的共享锁可以在多个进程中添加.
case2
接着上面的情况继续.
session2:
select * from user_charge where user_id = 1 for update;
#或者执行下面一行
update user_charge SET llt_in = 6 where user_id = 1;
此时发现这行执行之后session2堵塞了. 所以,一个事务加了共享锁之后, 其他进程如果要加排他锁或者修改数据,都会堵塞.
测试写锁
case1
session1:
set autocommit = 0;
begin; #开启事务
select * from user_charge where user_id = 1 for update;
session2:
#select * from user_charge where user_id = 1; #提示: 直接读取是没问题的
select * from user_charge where user_id = 1 lock in share mode;
#或者执行下一行
select * from user_charge where user_id = 1 for update;
此时发现这行执行之后session2堵塞了. 所以,一个事务加了排他锁之后,其他进程如果要加共享锁或者排他锁都是不行的,当然修改就更不行了,你可以自己试试~
补充说明
上面例子没有说怎么解除堵塞,其实很简单,只要有锁的进程执行commit,就可以释放锁了,其他进程就不会堵塞了.