回答:锁是数据库系统区别文件系统的一个关键特性,锁机制用于管理对共享资源的并发访问,保持数据的完整性和一致性。【摘自:MySQL技术内幕InnoDB存储引擎】
回答:数据库中有表锁和行锁等
lock锁:锁的对象是事务,用于锁定数据库中的对象,如表、页、行等,并且lock锁一般在commit或rollback后释放,有死锁机制。
latch锁:一般称为轻量级锁,要求锁定的时间必须非常短,在InnoDB中又可以分为mutex(互斥量)和rwlock(读写锁)。目的是用来保证并发线程操作临界资源的正确性,并且通常没有死锁检测的机制。
回答:可以分为共享锁、排他锁、意向锁、一致性非锁定读和一致性锁定读。
其中共享锁和排他锁均属于行级锁。
共享锁(S Lock):运行事务读一行数据。
排他锁(X Lock):允许事务删除或更新一行数据。
行锁的三种算法:
Record Lock:单个行记录上的锁
Gap Lock:间隙锁,锁定一个范围,但不包含记录本身。
Next-Key Lock:Gap+Record Lock锁定一个范围,并且锁定记录本身。
意向锁属于表级别的锁,又可以分为意向共享锁(IS Lock)和意向排他锁(IX Lock)。
意向共享锁(IS Lock):事务想要获得一张表中某几行的共享锁。
意向排他锁(IX Lock):事务想要获得一张表中某几行的排他锁。
一致性非锁定读:指InnoDB存储引擎通过多版本控制的方式来读取当前执行时间数据库中行的数据。如果读取的行正在执行DELETE或UPDATE操作,这时读取操作不会因此等待行上锁的释放,相反的,InnoDB存储引擎会读取一个快照数据。
一致性锁定读:InnoDB存储引擎对于SELECT语句支持两种一致性锁定读的操作:
select ... for update和select ... lock in share mode。
MVCC实现原理【MVCC多版本并发控制,指的是一种提高并发的技术。】
Multi-Version Concurrency Control。
最早的数据库系统,只有读读之间可以并发,读写,写读,写写都要阻塞。引入多版本之后,只有写写之间相互阻塞,其他三种操作都可以并行,这样大幅度提高了InnoDB的并发度。
数据库并发场景有三种,分别为:
读-读:不存在任何问题,也不需要并发控制
读-写:有线程安全问题,可能会造成事务隔离性问题,可能遇到脏读,幻读,不可重复读
写-写:有线程安全问题,可能会存在更新丢失问题,比如第一类更新丢失,第二类更新丢失
MVCC可以为数据库解决以下问题
在并发读写数据库时,可以做到在读操作时不用阻塞写操作,写操作也不用阻塞读操作,提高了数据库并发读写的性能
同时还可以解决脏读,幻读,不可重复读等事务隔离问题,但不能解决更新丢失问题
MVCC只在读取已提交和可重复 读两种隔离级别下有作用
MVCC常见的实现方式乐观锁和悲观锁
MVCC是行级锁的变种,很多情况下避免了加锁操作。
应对高并发事务, MVCC比单纯的加锁更高效;
InnoDB存储引擎在数据库每行数据的后面添加了三个字段, 不是两个!!
1.Read view一致性视图【 主要是用来做可见性判断的, 比较普遍的解释便是"本事务不可见的当前其他活跃事务", 】
2.read view快照的生成时机, 也非常关键, 正是因为生成时机的不同, 造成了RC,RR两种隔离级别的不同可见性;
在innodb中(默认repeatable read级别), 事务在begin/start
transaction之后的第一条select读操作后, 会创建一个快照(read view), 将当前系统中活跃的其他事务记录记录起来;
在innodb中(默认repeatable committed级别), 事务中每条select语句都会创建一个快照(read view);
3.undo-log 【回滚日志,通过undo读取之前的版本信息,以此实现非锁定读取!】 是MVCC的重要组成部分!
当我们对记录做了变更操作时就会产生undo记录,Undo记录默认被记录到系统表空间(ibdata)中,但从5.6开始,也可以使用独立的Undo 表空间。
Undo记录中存储的是老版本数据,当一个旧的事务需要读取数据时,为了能读取到老版本的数据,需要顺着undo链找到满足其可见性的记录。
另外, 在回滚段中的undo logs分为: insert undo log 和 update undo log insert undo
insert undo log : 事务对insert新记录时产生的undolog, 只在事务回滚时需要, 并且在事务提交后就可以立即丢弃。 update undo
update undo log : 事务对记录进行delete和update操作时产生的undo log, 不仅在事务回滚时需要,
一致性读也需要,所以不能随便删除,只有当数据库所使用的快照中不涉及该日志记录,对应的回滚日志才会被purge线程删除。
4.InnoDB存储引擎在数据库每行数据的后面添加了三个字段
分别是事务ID、回滚指针和
6字节的DB_ROW_ID字段: 包含一个随着新行插入而单调递增的行ID, 当由innodb自动产生聚集索引时,聚集索引会包括这个行ID的值,否则这个行ID不会出现在任何索引中。
5.可见性比较算法(这里每个比较算法后面的描述是建立在rr级别下,rc级别也是使用该比较算法,此处未做描述)
设要读取的行的最后提交事务id(即当前数据行的稳定事务id)为 trx_id_current
当前新开事务id为 new_id
当前新开事务创建的快照read view 中最早的事务id为up_limit_id, 最迟的事务id为low_limit_id(注意这个low_limit_id=未开启的事务id=当前最大事务id+1)
比较:
回答:通过锁机制实现了事务的隔离性,使得事务可以并发的工作,但同时也会有一些潜在的问题。锁会带来如下问题:脏读、不可重复度、丢失修改和幻读。
不可重复读和幻读区别:
不可重复读的重点是修改比如多次读取一条记录发现其中某些列的值被修改,幻读的重点在于新增或者删除比如多次读取一条记录发现记录增多或减少了。【摘自MySQL技术内幕:InnoDB存储引擎可以使用Next-Key Locking机制来避免Phantom Problem问题】
回答:死锁是指两个或两个以上的事务在执行过程中,因争夺资源而造成的一种互相等待的现象。
解决死锁的办法:一种是超时回滚,一种是采用死锁检测机制(wait-for graph等待图)
如果面试官让你举例子,可以举例下面的例子:

在 MySQL 中,gap lock 默认是开启的,即innodb_locks_unsafe_for_binlog 参数值是disable 的,且 MySQL 中默认的是 RR 事务隔离级别。
当我们执行以下查询 SQL 时,由于 order_no 列为非唯一索引,此时又是 RR 事务隔离级别,所以 SELECT 的加锁类型为 gap lock,这里的 gap 范围是 (4,+∞)。

执行查询 SQL 语句获取的 gap lock 并不会导致阻塞,而当我们执行以下插入 SQL 时,会在插入间隙上再次获取插入意向锁。插入意向锁其实也是一种 gap 锁,它与 gap lock 是冲突的,所以当其它事务持有该间隙的 gap lock 时,需要等待其它事务释放 gap lock 之后,才能获取到插入意向锁。
以上事务 A 和事务 B 都持有间隙 (4,+∞)的 gap 锁,而接下来的插入操作为了获取到插入意向锁,都在等待对方事务的 gap 锁释放,于是就造成了循环等待,导致死锁。

最后送上锁之间的兼容性表格:

推荐阅读