MySQL锁——针对实习面试
目录
- MySQL锁
- 表级锁和行级锁有什么区别?
- 行级锁的使用有什么注意事项?
- InnoDB的锁的类型?
- 说一下共享锁和排他锁?
- 共享锁(S锁)
- 排他锁(X锁)
- 你知道意向锁吗?
- 说一下乐观锁和悲观锁?
- 悲观锁(Pessimistic Locking)
- 乐观锁(Optimistic Locking)
- 总结
- 什么是死锁?怎么避免?
- 什么是死锁?
- 死锁的特征(四个必要条件)
- 怎么避免死锁?
- 参考
MySQL锁
表级锁和行级锁有什么区别?
表级锁
- 锁定力度:锁定整个表,对表中的所有行都生效。当一个事务对表加上表级锁时,其他事务不能同时对该表加任何锁
- 并发性能:由于锁定了整个表,所以在同一时间只能有一个事务对表进行写操作,读操作也可能被阻塞,这限制了并发性能
- 锁的开销:锁的开销较小,因为只需要锁定整个表,不需要为每一行单独维护锁
- 死锁风险:由于锁定粒度较大,死锁的风险相对较低
- 隔离级别:通常与较低的隔离级别(如读未提交)相关联,因为它们提供的隔离级别较低
- 适用场景:适用于小表或需要对整个表进行批量操作的场景,以及在低并发环境下
行级锁
- 锁定力度:锁定表中的单个行记录。允许多个事务同时对表中的不同行记录加锁,提高了并发性
- 并发性能:由于只锁定特定的行,所以可以允许多个事务同时对表中的不同行进行读写操作,提高了数据库的并发性能
- 锁的开销:锁的开销较大,因为需要为每一行维护锁信息,尤其是在有大量行被锁定时
- 死锁风险:由于锁定粒度较小,多个事务可能涉及多行,增加了死锁的风险
- 隔离级别:通常与较高的隔离级别(如可重复读、串行化)相关联,因为它们提供的隔离级别较高
- 适用场景:适用于大表或需要对表中个别行进行操作的场景,以及在高并发环境下
行级锁的使用有什么注意事项?
行级锁依赖于索引。只有通过索引条件检索数据时,InnoDB才使用行级锁;否则,会退化为表级锁。因此,确保查询条件中使用索引是提高并发性能的关键。
InnoDB的锁的类型?
InnoDB存储引擎提供了多种锁类型来管理事务并发和数据一致性。以下是InnoDB中常见的锁类型:
-
行级锁(Row-level Locks):
- 共享锁(S锁,Shared Locks):允许事务读取一行数据。
- 排他锁(X锁,Exclusive Locks):允许事务更新或删除一行数据,阻止其他事务对该行加任何锁。
-
意向锁(Intention Locks):
- 意向共享锁(IS锁,Intention Shared Locks):表明事务想要在更细粒度上加共享锁。
- 意向排他锁(IX锁,Intention Exclusive Locks):表明事务想要在更细粒度上加排他锁。
- 意向锁是内部使用的锁,用于在多粒度锁定系统中表明锁的意图。
-
表级锁(Table-level Locks):
- InnoDB的表级锁实际上是一种元数据锁(MDL),用于防止数据字典的更改,保证DDL操作的安全性。
-
临键锁(Next-Key Locks):
- 结合了行锁和间隙锁,锁定一个记录并且锁定该记录前面间隙的锁。用于处理范围查询和防止幻读。
-
间隙锁(Gap Locks):
- 锁定一个范围,但不包括记录本身。用于防止幻读,确保索引记录的间隙不会改变。
-
记录锁(Record Locks):
- 锁定一个具体的记录,防止其他事务修改这条记录。
-
自增锁(AUTO-INC Locks):
- 一种特殊的表级锁,用于处理自增字段的值分配,确保自增值的唯一性。
-
辅助锁(Auxiliary Locks):
- 用于InnoDB内部的一些特殊操作,比如外键约束检查。
了解这些锁的类型和它们的行为对于优化数据库性能和处理并发问题至关重要。不同的锁类型适用于不同的场景,合理使用可以提高数据库的并发性能和数据一致性。
说一下共享锁和排他锁?
共享锁(S锁)
- 定义:共享锁是一种允许多个事务同时读取同一数据行的锁。当一个事务对数据行加上共享锁时,其他事务也可以对该行加共享锁并读取它,但不能修改它。
- 兼容性:共享锁与其他共享锁是兼容的,也就是说,多个事务可以同时对同一数据行持有共享锁。但是,共享锁与排他锁是不兼容的,即如果一个事务对数据行持有共享锁,其他事务就不能对该行加排他锁。
- 用途:主要用于读取操作,确保数据的一致性读取。在事务隔离级别为读已提交(Read Committed)或可重复读(Repeatable Read)时,事务在读取数据时会加共享锁。
排他锁(X锁)
- 定义:排他锁是一种只允许一个事务独占访问数据行的锁。当一个事务对数据行加上排他锁时,它可以读取并修改这行数据,而其他事务既不能加共享锁也不能加排他锁。
- 兼容性:排他锁与其他任何类型的锁都不兼容,这意味着如果一个事务对数据行持有排他锁,其他事务就不能对该行加任何类型的锁,直到排他锁被释放。
- 用途:主要用于数据的写入操作,包括插入(INSERT)、更新(UPDATE)和删除(DELETE)。在事务隔离级别为可重复读或串行化(Serializable)时,事务在修改数据时会加排他锁。
S锁和X锁的兼容关系
S锁 | X锁 | |
---|---|---|
S锁 | 不冲突 | 冲突 |
X锁 | 冲突 | 冲突 |
你知道意向锁吗?
相关可看上文“InnoDB的锁的类型?”
说一下乐观锁和悲观锁?
乐观锁和悲观锁是两种不同的并发控制策略,它们用于处理多事务环境下的数据一致性和完整性问题。这两种锁的命名反映了它们对事务冲突发生可能性的预期态度。
悲观锁(Pessimistic Locking)
- 定义:悲观锁假设在事务中,数据冲突很可能会发生,因此在事务开始时就对数据进行锁定,以防止其他事务修改数据。
- 适用场景:适用于写操作多、冲突多的场景,或者数据竞争激烈的环境。
- 实现方式:通常通过数据库的锁机制实现,如行级锁、表级锁等。在事务开始时加锁,在事务结束时释放锁。
- 优点:可以保证数据在事务中的一致性和完整性,避免数据冲突。
- 缺点:可能导致数据库性能下降,特别是在高并发环境下,因为锁会阻塞其他事务的执行,增加事务等待时间。
乐观锁(Optimistic Locking)
- 定义:乐观锁假设在事务中,数据冲突发生的可能性很小,因此不会在事务开始时就锁定数据。它通常在事务提交时检查在事务过程中数据是否被其他事务修改过。
- 适用场景:适用于读操作多、冲突少的场景,或者数据竞争不激烈的环境。
- 实现方式:通常有两种方法,一种是通过数据版本控制,另一种是通过时间戳。数据版本控制是在数据表中增加一个版本字段或时间戳字段,事务开始时记录版本号或时间戳,在事务提交时检查版本号或时间戳是否发生变化,如果发生变化,则表示数据被其他事务修改过,事务需要回滚。
- 优点:减少了锁的使用,可以提高数据库的并发性能,特别是在高并发读操作的场景下。
- 缺点:在数据冲突真正发生时,事务可能需要回滚和重试,这可能会导致性能问题,尤其是在高冲突环境下。
总结
- 悲观锁是一种更加保守的策略,适用于冲突可能性高的环境,通过锁定资源来确保事务的一致性。
- 乐观锁是一种更加宽松的策略,适用于冲突可能性低的环境,通过检测冲突来减少锁的使用,提高并发性能。
什么是死锁?怎么避免?
什么是死锁?
死锁是指在多任务环境中,两个或多个进程在执行过程中因争夺资源而造成的一种僵局。当每个进程都持有一些资源,并且请求其他进程持有的资源时,若这些请求都不能得到满足,就会导致所有进程都无法继续执行,陷入“僵局”状态。在数据库系统中,死锁通常发生在两个或多个事务中,它们互相等待对方释放资源(如行锁、表锁等),但都没有进展。
死锁的特征(四个必要条件)
- 互斥条件:资源不能被多个进程共享,只能由一个进程独占。
- 占有和等待条件:进程至少占有一个资源,并且等待获取其他进程占有的资源。
- 不可抢占条件:资源只能由占有它的进程自愿释放,不能被强制抢占。
- 循环等待条件:存在一个进程-资源的循环链,每个进程都在等待下一个进程所占有的资源。
怎么避免死锁?
避免死锁通常涉及破坏上述四个条件中的一个或多个。以下是一些常见的避免死锁的策略:
-
资源分配图:
- 通过构建资源分配图来检测循环等待条件。如果检测到可能发生死锁的循环等待,就推迟或取消其中一个进程的资源请求。
-
顺序分配资源:
- 为所有资源编号,所有进程必须按编号顺序请求资源,这样可以破坏循环等待条件。
-
资源的一次性分配:
- 要求每个进程在开始时就一次性请求其所有可能需要的资源,如果无法满足,则不分配任何资源,这样可以避免占有和等待条件。
-
银行家算法:
- 在操作系统中,银行家算法通过预分配资源和检查安全状态来避免死锁。
-
超时和重试:
- 对于数据库事务,如果检测到死锁,可以让其中一个事务回滚并重试,以此释放资源。
-
锁管理策略:
- 使用锁管理策略,如锁定超时机制,如果一个事务在一定时间内无法获得所有需要的锁,它将放弃当前持有的锁并回滚。
-
减少锁的粒度:
- 减少锁的粒度可以减少锁争用,例如使用行级锁代替表级锁。
-
避免长事务:
- 长事务持有锁的时间更长,增加了死锁的风险,应尽量避免。
-
死锁检测和解决:
- 许多数据库管理系统内置了死锁检测机制,可以在检测到死锁时自动解决。
通过这些策略,可以在一定程度上避免或减少死锁的发生,从而提高系统的稳定性和性能。
参考
部分参考Javaguide的关于面试的文档(准备Java面试的同学强烈推荐!)
链接:https://javaguide.cn/