mysql悲观锁,如何理解mysql的select for update行级锁?
发布于 作者:苏南大叔 来源:程序如此灵动~如果面向多线程高并发的需求的时候,就需要考虑数据完整一致性的问题了。这个时候,一般就会使用“锁”的概念。使用一种手段来保证数据的更新逻辑是独一被执行的。防止脏读、幻读产生的数据不一致等现象。mysql
的select ... for update
行级锁就是其中的一种悲观锁的问题解决手段。本文对select ... for update
做一下初步的探讨。
苏南大叔的“程序如此灵动”博客,记录苏南大叔的编程经验文章。测试环境:win10
,mysql@5.7.26
,mysql-front@5.3
。
前文回顾
mysql
的select ... for update
不是事务的一部分,但是需要和事务一起搭配使用才能产生效果。这里回顾一下事务的相关文章:
innodb
数据表,https://newsn.net/say/mysql-engines.htmlautocommit
属性,https://newsn.net/say/mysql-autocommit.htmlcommit
动作,https://newsn.net/say/mysql-commit.html- 事务开始的位置,(记忆点开始的位置),https://newsn.net/say/mysql-savepoint.html
InnoDB
表
需要特别说明的是:select ... for update
必须配合事务才能发挥效果。所以,也需要innodb
存储类型表,不支持MyISAM
存储类型。本文的龙套角色数据表如下所示:
DROP TABLE IF EXISTS `innodb`;
CREATE TABLE `innodb` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
INSERT INTO `innodb` VALUES (1,'苏南大叔'),(2,'sunan大叔');
事务 + for update
因为涉及到多个进程获得同一条数据的操作权限的问题,所以这里模拟了两个进程。一个使用命令行页面下的mysql
,另外一个使用mysql-front
软件操作界面。
【界面一】命令行进程:
begin;
select * from innodb where id=1 for update;
这里的begin
是关键,没有它,for update
会完全失效!
【界面二】此时在软件界面,试图修改数据:
update innodb set name='好市民苏大哥' where id=2;
成功,因为第二条数据没有被锁定。继续操作:
update innodb set name='sunan大叔叔' where id=1;
失败陷入无限等待,因为第一条数据被锁定了。
【界面一】回界面一,输入commit
命令,结束事务。
commit;
结束事务的同时,界面二更新成功。
注意:这里仅仅验证了行级锁的存在,并没有解决脏数据的问题。因为界面二的输入在解锁后,也成功写入了。它如果没有使用事务的话,这就是一条脏数据。那么,正确的做法是:对这些数据进行操作的时候,都使用事务,都使用for update
。
一些冷知识
下面是网上教程里面没有写的,仔细看哦。
行级锁
for update
的where
语句很重要,符合where
的行将会被锁定。如果不写where
的话,或者where
的是没有索引的字段的话,锁定的可是一整张表。这会引发大的问题!如果for update
查询到的是空数据的话,并不会引发任何的锁。
死锁
当L多个事务死锁的时候,即每个事务都在等待对方释放资源,因此这些事务都不能继续执行,这将导致MySQL无限期地挂起这些事务。
死锁等待的是界面一的事务结束,主动commit
或者rollback
带来的隐式commit
或者重新begin
,都可以结束事务。或者开启事务的线程丢失链接,这也会解开死锁状态。
索引
索引很重要,如果where
里面涉及到没有索引的字段,那么行级锁就会变成全表锁。例如:id=1
就是行锁,name='苏南大叔'
就是表锁,不写where
也是表锁!这个话题有些大,待后续文章更新。
终于找到能明显判断索引作用的地方了。哈哈!
参考:
结语
期待成功就需要耐得住寂寞,做好准备迎接机会的到来。苏南大叔的更多mysql
经验文章,请点击:
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。