实践:只实践了悲观锁和共享锁
mysql 版本: 5.7. 14
创建一个 users 表 (必须是 innodb 引擎)
CREATE TABLE IF NOT EXISTS `users`(
`id` INT UNSIGNED AUTO_INCREMENT,
`name` VARCHAR(100) NOT NULL,
`emaiil` VARCHAR(40) NOT NULL,
`created_at` Date,
PRIMARY KEY ( `id` )
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
填充测试数据。
打开两个终端 ,分别连接数据库 并使用 users表
测试 for update 悲观锁
如图:
第一个终端 锁住这一行,发现第二个表 仍然可读,但是第二个表不能继续使用 for update 了。当然也不能 update 更新数据 ,直到第一个 终端 commit。
commit 之后不在事务中无论谁使用for update 都其实没什么效果。
当然其它行可以任意操作。锁也只是锁住指定的那行,其它行仍然可以操作。
测试 lock in share mode 共享锁
如图:
终端一 使用共享锁锁住这一行,终端二可以任意select 查询,也可以使用共享锁,但是使用 for update 悲观锁时,没有结果 ,需要等待终端一的commit. 或者超时报错, Lock wait timeout exceeded; try restarting transaction
此时 终端二 update 这行数据也是这个效果。
这个时候终端二也开始一个 事务:如图
这里终端一, 一直锁住这行 没有 commit , 然后发现终端二 不能在他的事务里 使用 for update 但是可以使用 lock in share mode ,当然也不能 update ,
此时我在终端一执行 update 发现也被锁了(因为终端二也执行了 lock in share mode), 此时终端二也去执行 update 会发现报错 DeadLock 然后后执行的会报错,先执行的抢到资源。
另外需要注意的是,无论 for update 还是 lock in share mode 务必要走了索引(而不是全表扫描),否则 mysql 会锁住整个表。
例如使用如下语句:
使用的 like 这个会使 users 整个表上锁,那么其它进程更新无论那行数据都要等待这个锁,或者加锁查询的时候也需要等待。
乐观锁
乐观锁的特点先进行业务操作,不到万不得已不去拿锁。即“乐观”的认为拿锁多半是会成功的,因此在进行完业务操作需要实际更新数据的最后一步再去拿一下锁就好。
乐观锁在数据库上的实现完全是逻辑的,不需要数据库提供特殊的支持。一般的做法是在需要锁的数据上增加一个版本号,或者时间戳。举例:
1.查询出商品信息
select (status,status,version) from t_goods where id=#{id}
2.根据商品信息生成订单
3.修改商品status为2
update t_goods
set status=2,version=version+1
where id=#{id} and version=#{version};
根据以上,结合实际选择合适的锁吧。
此文章通过 python 爬虫创建,原文是自己的csdn 地址: MySQL 悲观锁和乐观锁和共享锁