最近碰到了MySQL中的for update
语法,这是用来加锁的,将学习的过程记录下来
悲观锁(Pessimistic Lock)认为每次读取数据都是不安全的,所以每次读取数据的时候都会加锁。
比如mysql里的for update
就属于这类,可以锁一行,也可锁住整个表,加锁之后别人就无法同时进行操作了。
悲观锁一般用于写操作比较多的地方。
乐观锁(Optimistic Lock)认为每次读取数据的时候是安全的,只有在更新操作的时候才会判断是否有别人修改过数据。
这种锁一般通过其它方式来控制,比如通过版本号机制等。
乐观锁一般用于读操作比较多的地方,这样可以提高吞吐量。
先来一个测试用的表:
CREATE TABLE `user` (
`id` bigint(10) NOT NULL AUTO_INCREMENT COMMENT '自增主键',
`user_name` varchar(20) NOT NULL COMMENT '用户名',
`remark` varchar(20) NOT NULL COMMENT '备注',
PRIMARY KEY (`id`),
UNIQUE KEY `uniq_user_name` (`user_name`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='用户表';
表中数据:
+----+-----------+---------+
| id | user_name | remark |
+----+-----------+---------+
| 1 | user1 | remark1 |
| 2 | user2 | remark2 |
| 3 | user3 | remark3 |
+----+-----------+---------+
这样来使用for update
BEGIN;
SELECT * FORM user WHERE id=1 for update;
COMMIT;
注意:
for update
必须在BEGIN/COMMIT
块中才会起作用我们来测试一下,通过两个终端分别进行操作,如图:
我们在第一个终端中开启事务使用了行锁,但未commit
,第二个终端中再查询该行则会被阻塞,处于等待状态。
直到第一个终端commit
之后,第二个终端才会读取到被锁住的那一行的数据。
若等待时间过长(终端1迟迟未commit),则终端2会产生超时错误。
主要是测试下,什么情况下是 Row Lock,什么情况下是 Table Lock。
以下情况是 Row Lock
-- 使用了主键索引
select * from user where id=1 for update;
-- 使用了其它索引
select * from user where user_name='user1' for update;
-- 使用 like 但是可以唯一确定一行
select * from user where user_name like 'user1%' for update;
以下情况是 Table Lock
-- 主键不明确
select * from user where id != 1 for update;
-- 没有用上任何索引
select * from user where remark='remark1' for update;
select * from user where user_name like '%user1' for update; -- 虽然能确定一行,但是没用上索引
-- 使用了 like 不能唯一确定一行
select * from user where user_name like 'user%' for update; -- 这条用上了索引
select * from user where user_name like '%user%' for update; -- 这条没有用上索引
-- 未查到数据,但是未使用任何索引
select * from user where remark='test' for update;
以下情况 无 Lock
-- 未查到数据,但是使用了索引
select * from user where user_name='user9' for update;