MySQL数据库锁
MySQL数据库锁主要有:
1、共享锁也叫读锁(select…in share mode)
使用共享锁的事务会锁柱被筛选出来的数据行,别的事务只能读取这些数据,但不能修改,如果修改会被阻塞住。
2、排他锁也叫写锁(select…for update)
使用共享锁的事务会锁柱被筛选出来的数据行,别的事务即不能读取这些数据,也不能修改这些数据,如果读取或修改会被阻塞住。
3、记录锁
锁住索引查询筛查选出来的数据行,当按索引条件执行修改操作时会自动应用记录锁,比如:
UPDATE table_name SET column_name = new_value WHERE index_column = some_value;
4、间隙锁
锁住数据行之间的间隙,比如 select * from table where id between 3 and 7 for update。假如表中只有id为4和6的数据,id为5的数据间隙也会被锁住,不允许插入或删除id为5 数据。间隙锁被设计用在可重复读隔离级别下避免幻读。使用写锁select…for update。数据库就会自动应用间隙锁。
5、表锁
锁住整张表,上锁:lock tables table_name write;
解锁:unlock tables;
6、页级锁
控制对数据库页的并发访问,由数据库系统自动管理。
7、意向锁
分为意象共享锁,意象排他锁
表明事物的意图,比如事务要给某些数据行放置一个排他锁,数据库系统会自动的首先给这些数据行所在表加一个意象排他锁。不需要人为介入,数据库系统会自动应用。
真正常用的(相对来说),也就是排他锁 select…for update
读已提交隔离级别下使用排他锁,筛选条件用主键可以防止幻读,主键间隙也会被锁定。可以解决不可重复读和幻读。
可重复读隔离级别下使用排他锁,可以解决幻读。
能不用锁还是不要用锁,毕竟MySQL的优势就是支持高并发量
不同隔离级别下的锁
读未提交
事务的读写都不加锁,但基本上也很少用这种隔离级别,毕竟有脏读,除非是只读不写的业务场景
串行化
事物之间加排他锁,这种场景也不常用,毕竟没有事务的并发,性能太低
读已提交
事务中读的时候不加锁,并发性能火力全开。
写的时候加锁,写完立马解锁。这里写完实际指的是事务提交。没有手动开启事务,每个修改数据的sql都是自动开启事务,自动提交事务。如果使用@Transactionnal这样的注释,就等到注释的方法结束自动提交事务。
可重复读
事务中读操作也会对目标数据加读锁,允许其他事务读取,但不能修改要等待锁释放。
写操作跟读已提交一样,等到事务提交释放锁。
这里需要注意锁的范围,不管是查询还是更新,如果查询条件没有加索引就会全表扫描,全表都会被锁住。
读已提交并发情况
读已提交隔离级别下,只要某个事务先执行到更新操作,就会锁住要更新的数据,直到事务提交,其他要更新目标数据的事务都得等待,此时所有事务读取的目标数据都是一致的,如果目标数据的目标字段不冲突,各个抢到锁的事务一次提交事务即可,没有更新冲突的情况,如果两个或两个以上的事务要更新的目标字段冲突,也会按抢到锁的顺序一次更新,目标字段的更新结果就是最后一个事务的更新结果。由于所有并发的事务都是基于同一个原目标数据做计算,所以即使是只有最后一个事务的更新也被保留下来,也是数据一致的,因为原数据已经是最新的数据。
此模式有个问题:就是幻读,更新目标数据期间由于没有加间隙锁,可能会有符合更新条件的数据插入进来,也可能会有符合更新条件的数据被删除。如果是有符合更新条件的数据被删除,大不了更新失败。有符合更新条件的数据插入进来,就会导致少更新数据。实际业务场景中,这种情况很少出现。,大不了再更新一次。如果确实有这种业务场景而且很频繁,可以考虑整个定时任务,定期更新。
隔离级别选择
读已提交隔离级别下并发性高于可重复读,而且读已提交隔离级别下,如果要更新的数据被另一个事务锁住,MySQL会再确认一下,要更新的数据是否真的需要被锁,如果发现要更新的数据实际不需要被锁(需要更新的数据被锁住可能是查询条件没有加索引导致扫描全表,锁全表),此条数据就会解锁不影响本次更新。这个特性有人叫半一致读,我也是参考了这篇文章https://zhuanlan.zhihu.com/p/150248692,然后做实验确实如此。
所以综合考量,虽然MySQL默认隔离级别是可重复读,但实际应用中用读已提交更能利用它的并发性能。
MySQL读已提交隔离级别下只要合理使用事务就能保证数据的一致性,不需要手动加锁,更新操作会自动那个加上排他锁
MySQL之所以把默认隔离级别设为可重复读,看别人说是历史原因:5.0版本之前主从复制的bin log格式只有statement,即sql原始执行语句,从机执行这些同步数据的sql语句时,不能保证跟主机一样的事务顺序,导致同步的数据跟主机不一致,可参看这个https://www.cnblogs.com/yizhiamumu/p/16726845.html。所以用了可重复读,这种模式下保证事务同步进行,但串行提交,保证同步结果。