共计 4371 个字符,预计需要花费 11 分钟才能阅读完成。
本文曾经收录到 Github 仓库,该仓库蕴含 计算机根底、Java 根底、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享 等外围知识点,欢送 star~
Github 地址:https://github.com/Tyson0314/Java-learning
为什么须要加锁
如果有多个并发申请存取数据,在数据就可能会产生多个事务同时操作同一行数据。如果并发操作不加管制,不加锁的话,就可能写入了不正确的数据,或者导致读取了不正确的数据,毁坏了数据的一致性。因而须要思考加锁。
表级锁和行级锁有什么区别?
MyISAM 仅仅反对表级锁,一锁就锁整张表,这在并发写的状况下性十分差。
InnoDB 不光反对表级锁,还反对行级锁,默认为行级锁。行级锁的粒度更小,仅对相干的记录上锁即可(对一行或者多行记录加锁),所以对于并发写入操作来说,InnoDB 的性能更高。
表级锁和行级锁比照:
- 表级锁: MySQL 中锁定粒度最大的一种锁,是针对非索引字段加的锁,对以后操作的整张表加锁,实现简略,资源耗费也比拟少,加锁快,不会呈现死锁。其锁定粒度最大,触发锁抵触的概率最高,并发度最低,MyISAM 和 InnoDB 引擎都反对表级锁。
- 行级锁: MySQL 中锁定粒度最小的一种锁,是针对索引字段加的锁,只针对以后操作的记录进行加锁。行级锁能大大减少数据库操作的抵触。其加锁粒度最小,并发度高,但加锁的开销也最大,加锁慢,会呈现死锁。
共享锁和排他锁有什么区别?
不论是表级锁还是行级锁,都存在共享锁(Share Lock,S 锁)和排他锁(Exclusive Lock,X 锁)这两类:
- 共享锁(S 锁):又称读锁,事务在读取记录的时候获取共享锁,容许多个事务同时获取(锁兼容)。
- 排他锁(X 锁):又称写锁 / 独占锁,事务在批改记录的时候获取排他锁,不容许多个事务同时获取。如果一个记录曾经被加了排他锁,那其余事务不能再对这条事务加任何类型的锁(锁不兼容)。
排他锁与任何的锁都不兼容,共享锁仅和共享锁兼容。
S 锁 | X 锁 | |
---|---|---|
S 锁 | 不抵触 | 抵触 |
X 锁 | 抵触 | 抵触 |
因为 MVCC 的存在,对于个别的 SELECT
语句,InnoDB 不会加任何锁。不过,你能够通过以下语句显式加共享锁或排他锁。
# 共享锁
SELECT ... LOCK IN SHARE MODE;
# 排他锁
SELECT ... FOR UPDATE;
意向锁有什么作用?
如果须要用到表锁的话,如何判断表中的记录没有行锁呢?一行一行遍历必定是不行,性能太差。咱们须要用到一个叫做意向锁的东东来疾速判断是否能够对某个表应用表锁。
意向锁是表级锁,共有两种:
- 动向共享锁(Intention Shared Lock,IS 锁):事务有动向对表中的某些加共享锁(S 锁),加共享锁前必须先获得该表的 IS 锁。
- 动向排他锁(Intention Exclusive Lock,IX 锁):事务有动向对表中的某些记录加排他锁(X 锁),加排他锁之前必须先获得该表的 IX 锁。
意向锁是有数据引擎本人保护的,用户无奈手动操作意向锁,在为数据行加共享 / 排他锁之前,InooDB 会先获取该数据行所在在数据表的对应意向锁。
意向锁之间是相互兼容的。
IS 锁 | IX 锁 | |
---|---|---|
IS 锁 | 兼容 | 兼容 |
IX 锁 | 兼容 | 兼容 |
意向锁和共享锁和排它锁互斥(这里指的是表级别的共享锁和排他锁,意向锁不会与行级的共享锁和排他锁互斥)。
IS 锁 | IX 锁 | |
---|---|---|
S 锁 | 兼容 | 互斥 |
X 锁 | 互斥 | 互斥 |
InnoDB 有哪几类行锁?
按锁粒度分类,有行级锁、表级锁和页级锁。
-
行级锁是 mysql 中锁定粒度最细的一种锁。示意只针对以后操作的行进行加锁。行级锁能大大减少数据库操作的抵触,其加锁粒度最小,但加锁的开销也最大。行级锁的类型次要有三类:
- Record Lock,记录锁,也就是仅仅把一条记录锁上;
- Gap Lock,间隙锁,锁定一个范畴,然而不蕴含记录自身;
- Next-Key Lock:Record Lock + Gap Lock 的组合,锁定一个范畴,并且锁定记录自身。
- 表级锁是 mysql 中锁定粒度最大的一种锁,示意对以后操作的整张表加锁,它实现简略,资源耗费较少,被大部分 mysql 引擎反对。最常应用的 MyISAM 与 InnoDB 都反对表级锁定。
- 页级锁是 MySQL 中锁定粒度介于行级锁和表级锁两头的一种锁。表级锁速度快,但抵触多,行级抵触少,但速度慢。因而,采取了折衷的页级锁,一次锁定相邻的一组记录。
按锁级别分类,有共享锁、排他锁和意向锁。
- 共享锁又称读锁,是读取操作创立的锁。其余用户能够并发读取数据,但任何事务都不能对数据进行批改(获取数据上的排他锁),直到已开释所有共享锁。
- 排他锁又称写锁、独占锁,如果事务 T 对数据 A 加上排他锁后,则其余事务不能再对 A 加任何类型的封闭。获准排他锁的事务既能读数据,又能批改数据。
- 意向锁是表级锁,其设计目标次要是为了在一个事务中揭示下一行将要被申请锁的类型。InnoDB 中的两个表锁:
动向共享锁(IS):示意事务筹备给数据行退出共享锁,也就是说一个数据行加共享锁前必须先获得该表的 IS 锁;
动向排他锁(IX):相似下面,示意事务筹备给数据行退出排他锁,阐明事务在一个数据行加排他锁前必须先获得该表的 IX 锁。
意向锁是 InnoDB 主动加的,不须要用户干涉。
对于 INSERT、UPDATE 和 DELETE,InnoDB 会主动给波及的数据加排他锁;对于个别的 SELECT 语句,InnoDB 不会加任何锁,事务能够通过以下语句显式加共享锁或排他锁。
共享锁:SELECT … LOCK IN SHARE MODE;
排他锁:SELECT … FOR UPDATE;
什么是死锁?如何避免死锁?
什么是死锁?
死锁是指两个或多个事务在同一资源上互相占用,并申请锁定对方的资源,从而导致恶性循环的景象。
如何避免死锁?
- 尽量约定固定的程序拜访表,因为穿插拜访更容易造成事务期待回路。
- 尽量避免大事务,倡议拆成多个小事务。因为大事务占用的锁资源越多,越容易呈现死锁。
- 升高数据库隔离级别,比方 RR 升高为 RC,因为 RR 隔离级别,存在 GAP 锁,死锁概率大很多。
- 死锁与索引是密不可分的,正当优化你的索引,死锁概率升高。
- 如果业务解决不好能够用分布式事务锁或者应用乐观锁
如何解决死锁?
通过 innodblockwait_timeout 来设置超时工夫,始终期待直到超时。
发动死锁检测,发现死锁之后,被动回滚死锁中的事务,不须要其余事务持续。
什么是全局锁?它的利用场景有哪些?
全局锁就是对整个数据库实例加锁,它的典型应用场景就是做全库逻辑备份,这个命令能够应用整个库处于只读状态,应用该命令之后,数据更新语句,数据定义语句,更新类事务的提交语句等操作都会被阻塞。
应用全局锁会导致的问题?
如果在主库备份,在备份期间不能更新,业务进行,所以更新业务会处于期待状态。
如果在从库备份,在备份期间不能执行主库同步的 binlog,导致主从提早。
乐观锁和乐观锁是什么?
数据库中的并发管制是确保在多个事务同时存取数据库中同一数据时不毁坏事务的隔离性和统一性以及数据库的统一性。乐观锁和乐观锁是并发管制次要采纳的技术手段。
- 乐观锁:假设会产生并发抵触,会对操作的数据进行加锁,直到提交事务,才会开释锁,其余事务能力进行批改。实现形式:应用数据库中的锁机制。
- 乐观锁:假如不会产生并发抵触,只在提交操作时查看是否数据是否被批改过。给表减少
version
字段,在批改提交之前查看version
与原来取到的version
值是否相等,若相等,示意数据没有被批改,能够更新,否则,数据为脏数据,不能更新。实现形式:乐观锁个别应用版本号机制或CAS
算法实现。
select for update 加的是表锁还是行锁
须要状况探讨:RR 和 RC 隔离级别,还有查问条件(惟一索引、主键、个别索引、无索引)
在 RC 隔离级别下
- 如果查问条件是惟一索引,会加
IX
动向排他锁(表级别的锁,不影响插入)、两把X
排他锁(行锁,别离对应惟一索引,主键索引) - 如果查问条件是主键,会加
IX
动向排他锁(表级别的锁,不影响插入)、一把对应主键的X
排他锁(行锁,会锁住主键索引那一行)。 - 如果查问条件是一般索引,如果查问命中记录 ,会加
IX
动向排他锁(表锁)、两把X
排他锁(行锁,别离对应一般索引的X
锁,对应主键的X
锁);如果没有命中数据库表的记录 ,只加了一把IX
动向排他锁(表锁,不影响插入) - 如果查问条件是无索引,会加两把锁,IX 动向排他锁(表锁)、一把 X 排他锁(行锁,对应主键的 X 锁)。
查问条件是无索引,为什么不锁表呢?MySQL 会走聚簇 (主键) 索引进行全表扫描过滤。每条记录都会加上 X 锁。然而,为了效率思考,MySQL 在这方面进行了改良,在扫描过程中,若记录不满足过滤条件,会进行解锁操作。同时优化违反了 2PL 准则。
在 RR 隔离级别
- 如果查问条件是惟一索引,命中数据库表记录时,一共会加三把锁:一把 IX 动向排他锁(表锁,不影响插入),一把对应主键的 X 排他锁(行锁),一把对应惟一索引的 X 排他锁(行锁)。
- 如果查问条件是主键,会加
IX
动向排他锁(表级别的锁,不影响插入)、一把对应主键的X
排他锁(行锁,会锁住主键索引那一行)。 - 如果查问条件是一般索引,命中查问记录的话,除了会加 X 锁(行锁),IX 锁(表锁,不影响插入),还会加 Gap 锁(间隙锁,会影响插入)。
- 如果查问条件是无索引,会加一个 IX 锁(表锁,不影响插入),每一行理论记录行的 X 锁,还有对应于 supremum pseudo-record 的虚构全表行锁。这种场景,艰深点讲,其实就是锁表了。
参考链接:https://juejin.cn/post/7199666255884009532
优化锁方面有什么倡议?
- 尽量应用较低的隔离级别。
- 精心设计索引,并尽量应用索引拜访数据,使加锁更准确,从而缩小锁抵触的机会。
- 抉择正当的事务大小,小事务产生锁抵触的几率也更小。
- 给记录集显示加锁时,最好一次性申请足够级别的锁。比方要批改数据的话,最好间接申请排他锁,而不是先申请共享锁,批改时再申请排他锁,这样容易产生死锁。
- 不同的程序拜访一组表时,应尽量约定以雷同的程序拜访各表,对一个表而言,尽可能以固定的程序存取表中的行。这样能够大大减少死锁的机会。
- 不要申请超过理论须要的锁级别。
- 除非必须,查问时不要显示加锁。MySQL 的 MVCC 能够实现事务中的查问不必加锁,优化事务性能;
最初给大家分享一个 Github 仓库,下面有大彬整顿的 300 多本经典的计算机书籍 PDF,包含 C 语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法、机器学习、编程人生 等,能够 star 一下,下次找书间接在下面搜寻,仓库继续更新中~
Github 地址:https://github.com/Tyson0314/java-books