面试题答案
一键面试锁的类型
- 共享锁(S锁):又称为读锁,若事务对数据对象A加上S锁,那么其他事务只能对A再加S锁,不能加排他锁(X锁),直到事务释放A上的S锁。多个事务可以同时对同一数据对象加S锁,从而实现并发读操作。例如,在执行
SELECT ... LOCK IN SHARE MODE;
语句时,会对查询结果集的行加共享锁。 - 排他锁(X锁):也叫写锁,若事务对数据对象A加上X锁,其他事务不能再对A加任何类型的锁,直到事务释放A上的X锁。这种锁用于保证对数据的独占性修改,防止其他事务同时修改同一数据。例如,执行
SELECT ... FOR UPDATE;
语句或进行INSERT
、UPDATE
、DELETE
操作时,会对涉及的行加排他锁。
加锁时机
- SELECT语句:
- 普通的
SELECT
语句(无锁相关关键字)不会加锁,执行的是快照读,即读取的是数据的一致性视图,不影响其他事务对数据的并发操作。 - 当使用
SELECT ... LOCK IN SHARE MODE;
时,对查询结果集的行加共享锁,适用于需要读取数据并确保在读取期间数据不会被修改的场景,例如实现乐观锁机制。 - 当使用
SELECT ... FOR UPDATE;
时,对查询结果集的行加排他锁,常用于需要读取并修改数据,确保读取和修改之间数据不会被其他事务修改的场景,比如实现悲观锁机制。
- 普通的
- INSERT语句:对插入的新行加排他锁,由于新行之前不存在,所以不会与其他事务的锁冲突,但会等待其他事务对插入位置相关的间隙锁或意向锁释放。
- UPDATE语句:先对符合条件的行加排他锁,然后进行数据修改。如果修改涉及到索引列的变化,可能还会涉及到对索引的锁操作。
- DELETE语句:对符合删除条件的行加排他锁,然后删除这些行。同样,如果涉及索引,也会有相关索引的锁操作。
如何避免死锁
- 按同一顺序访问资源:事务应按照相同的顺序访问数据库中的对象(如表、行等)。例如,如果多个事务都需要访问表A和表B,那么所有事务都应先访问表A,再访问表B。这样可以避免由于访问顺序不一致而导致的死锁。
- 减少锁的持有时间:尽量缩短事务持有锁的时间,在获取到所需的锁后,尽快完成业务操作并释放锁。例如,不要在事务中进行长时间的用户交互或复杂的计算,应将这些操作放在事务外执行。
- 设置合理的事务隔离级别:不同的事务隔离级别对锁的使用和并发控制有不同的影响。例如,将事务隔离级别设置为
READ COMMITTED
相比REPEATABLE READ
会减少锁的持有时间和范围,但可能会出现不可重复读等问题,需要根据业务需求权衡选择。 - 使用死锁检测机制:InnoDB存储引擎本身具有死锁检测机制,当检测到死锁时,会自动选择一个事务作为牺牲者(通常选择回滚代价较小的事务)进行回滚,以解除死锁。可以通过适当调整
innodb_deadlock_detect
参数来控制死锁检测的行为,例如在高并发场景下,若死锁检测带来较大开销,可以考虑适当降低检测频率。 - 合理设计索引:合适的索引可以减少锁的粒度和范围。例如,通过创建覆盖索引,可以使查询仅在索引上操作,避免对数据行加锁,从而减少锁冲突的可能性。同时,索引的存在也可以加快查询速度,缩短事务执行时间,间接减少死锁发生的概率。