菜单 学习猿地 - LMONKEY

VIP

开通学习猿地VIP

尊享10项VIP特权 持续新增

知识通关挑战

打卡带练!告别无效练习

接私单赚外块

VIP优先接,累计金额超百万

学习猿地私房课免费学

大厂实战课仅对VIP开放

你的一对一导师

每月可免费咨询大牛30次

领取更多软件工程师实用特权

入驻
441
0

数据库-锁

原创
05/13 14:22
阅读数 90472

前面已经写过数据库的事务-防止并发操作数据产生的各种问题、数据库的索引-提高数据查找的速度。数据库的锁和事务一样也是防止并发操作数据产生问题,并且根据不同的事务隔离界别同一种操作加锁方式还有所不同,主要就是在效率与数据安全之间进行各种折中,我们也要在这中间做一个选择。鱼和熊掌不可兼得的道理。
前面也写过java锁的实现,主要是锁最底层的实现,而mysql完全不用考虑是怎么锁的,怎么解锁的,怎么维护锁队列的,因为关注了你也改不了。只需要了解即可。
数据库锁在面试中考到的概率也是比较大的,因为mysql数据库死锁是绕不开的问题,主要考察对锁的理解和解决死锁问题。

锁类型
主要就是分为表锁和行锁,myisam只有表锁,但是myisam几乎被抛弃了,所以对表锁有简单理解即可,而且它不支持事务,所以myisam的表锁也不可能有死锁,但是表锁的效率确实会很低。
innodb支持行锁也支持表锁,但是表锁极少用到,innodb的表锁可以和myisam的表锁理解为等号。
无论表锁还是行锁,都是读写锁分开的,所以都可以分为共享锁和排它锁,而innodb在事务中还提供一套自动添加的表级锁:意向锁,即IS(意向共享锁)IX(意向排它锁),分别是添加S/X锁之前自动添加的。
总结MySQL的锁种类:表锁、行锁;共享锁、排它锁;意向锁。

锁的实现
只讨论innodb的行锁实现,前面说了锁可能产生的重要问题就是死锁,也只有行锁非常容易产生死锁,所以问到的概率特别大。
MySQL的行锁是加在索引上的,所以用到行锁的前提就是走了索引,不然是不会使用行锁而是使用表锁。若是使用了普通索引,那么就会先锁普通索引项,然后再锁主键索引项(聚簇索引),若直接使用的主键索引,那就直接锁主键索引项。
MySQL提供间隙锁来防止幻读,间隙锁顾名思义就是加载间隙中的,例如:有数据【1,2,5,6,7,8,14】的间隙就是【3-4】、【9-13】、【14-无穷大】,一般都是在使用索引查询范围条件的时候用,加上间隙锁主要防止中间插入数据,下次查询的时候会多记录。
加锁时机是在查询语句明确使用加锁子句、添加更新和删除语句都会主动加锁,若没有事务则语句执行完成即释放锁,若有事务则事务commit才释放锁,而且根据事务隔离级别,相同的操作加的锁有可能不同。如下图:
事务隔离界别影响各种操作的加锁

死锁的产生
死锁就是两个并行执行的线程或进程互相等待对方持有而自己当前需要的资源,由于对方一直持有不释放,自己无法活得也无法继续执行,所以两个并行执行的程序就都停顿在这里。
MySQL的死锁也是如此,一般的情况就是事务A已经添加行锁1,等待行锁2,事务B已经添加行锁2,等待行锁1。然后两个事务就互相等待。
MySQL有自己的机制来检查死锁,并且提供自动解决死锁的方式,即产生死锁的多个事务里面选择一个持有资源最少的事务回滚即可。可以一定程度解决,但是不完全能解决。
死锁基本都是innodb引擎提供的行锁导致的,因为表锁不会产生死锁。

死锁的排查
首先,一般程序日志都会有死锁的异常日志,这个是MySQL返回给java的。
然后,在MySQL中适应命令 show engine innodb status \G; 就可以打印出最后产生死锁的信息,包括互相锁死的sql语句,事务id,锁的行id等信息。
最后,根据死锁信息查找程序中的问题。

避免死锁的编程规约
1.事务不能随意开,开启事务过多也会影响效率。非必要不开事务。
2.加锁顺序,提前定义好表的优先级,事务内对表的更新尽量按照这个优先级顺序更新数据就可以保证加锁的顺序都是一致的。

数据库锁与程序锁
前面说到的都是数据库锁,而数据库锁一般是程序开启事务自动添加的,可以自动规避并行执行产生的数据库中数据不一致问题。而程序锁是保证并行执行的时候程序使用的内存、io等其他资源的一致性和安全性。
同时,数据库锁也可能能成程序死锁的问题,例如两个并行执行的程序锁内,都获取两个数据库锁,并且都在等待对方释放数据库锁,这就导致两个程序锁锁死。避免这样的问题的方法和前面的避免死锁的编程规约基本相同。

参考资料
MySQL锁总结
mysql 锁问题
MySQL锁机制,行锁竟然加在索引上

发表评论

0/200
441 点赞
0 评论
收藏
为你推荐 换一批