面试题答案
一键面试意向锁
- 作用:
- 意向锁是为了在获取行锁前,表明事务对该表即将加锁的意向。它主要用于表级锁与行级锁之间的协调,使得在表级锁操作时,能够快速判断是否可以获取锁,避免全表扫描行锁情况,从而提高加锁效率。
- 类型:
- 意向共享锁(IS):事务打算给数据行加共享锁(S锁)前,必须先获得该表的意向共享锁。例如,执行
SELECT...LOCK IN SHARE MODE
语句时,如果要给某些行加共享锁,会先获取表的IS锁。 - 意向排他锁(IX):事务打算给数据行加排他锁(X锁)前,必须先获得该表的意向排他锁。比如执行
SELECT... FOR UPDATE
语句时,如果要给某些行加排他锁,会先获取表的IX锁。
- 意向共享锁(IS):事务打算给数据行加共享锁(S锁)前,必须先获得该表的意向共享锁。例如,执行
- 协作方式:
- 当事务请求行锁(共享锁或排他锁)时,首先会获取对应表的意向锁(IS或IX)。如果表上已有与该意向锁冲突的锁(如已有IX锁,再请求IS锁就会冲突),则无法获取意向锁,进而无法获取行锁。只有当获取了意向锁后,才能继续获取行锁。例如,事务A持有表的IX锁,事务B请求行共享锁,由于事务A的IX锁与事务B的IS锁冲突,事务B获取IS锁失败,进而无法获取行共享锁。
行锁
- 作用:
- 行锁是针对数据行进行加锁,允许不同事务同时访问不同行的数据,从而提高并发性能。它可以最大程度地减少锁的粒度,降低锁争用的范围,使得在高并发场景下,多个事务可以并发操作不同行的数据而不相互阻塞。
- 类型:
- 共享锁(S锁):又称读锁,多个事务可以同时获取同一行数据的共享锁,用于读取操作。当一个事务持有某行的共享锁时,其他事务可以获取该行的共享锁,但不能获取排他锁。例如,多个事务可以同时执行
SELECT...LOCK IN SHARE MODE
语句对同一行数据加共享锁来读取数据。 - 排他锁(X锁):又称写锁,一个事务获取某行的排他锁后,其他事务不能再获取该行的任何锁(包括共享锁和排他锁)。用于数据的修改操作,确保在同一时间只有一个事务可以修改某行数据,防止数据不一致。如执行
SELECT... FOR UPDATE
语句会给选中行加排他锁。
- 共享锁(S锁):又称读锁,多个事务可以同时获取同一行数据的共享锁,用于读取操作。当一个事务持有某行的共享锁时,其他事务可以获取该行的共享锁,但不能获取排他锁。例如,多个事务可以同时执行
- 协作方式:
- 行锁与意向锁协作时,意向锁为行锁获取提供了表级的预判断。例如,事务要获取某行的共享锁,先获取表的IS锁,再获取行的共享锁;要获取某行的排他锁,先获取表的IX锁,再获取行的排他锁。如果表级意向锁获取失败,行锁也无法获取。
高并发场景下可能引发的问题
- 死锁:
- 原因:两个或多个事务在获取锁的过程中,互相等待对方释放锁,形成循环等待的局面。例如,事务A持有行1的排他锁,等待获取行2的排他锁;事务B持有行2的排他锁,等待获取行1的排他锁,此时就会发生死锁。
- 影响:死锁会导致事务无法继续执行,浪费系统资源,严重影响系统的并发性能。
- 锁争用:
- 原因:在高并发场景下,大量事务同时竞争相同行锁或意向锁,导致许多事务需要等待锁的释放,从而降低系统的并发处理能力。例如,多个事务同时对热门数据行进行写操作,都需要获取排他锁,就会造成锁争用。
- 影响:锁争用会增加事务的等待时间,降低系统吞吐量,甚至可能导致某些事务长时间等待而超时。
解决方法
- 死锁解决方法:
- 设置死锁检测:MySQL默认开启死锁检测机制,当检测到死锁时,会自动回滚其中一个事务(通常选择回滚代价较小的事务),以打破死锁。可以通过
innodb_deadlock_detect
参数开启或关闭该功能,默认是开启的。 - 调整事务顺序:按照固定顺序访问资源,避免循环等待。例如,所有事务都按照主键从小到大的顺序获取锁,这样可以降低死锁发生的概率。
- 设置死锁检测:MySQL默认开启死锁检测机制,当检测到死锁时,会自动回滚其中一个事务(通常选择回滚代价较小的事务),以打破死锁。可以通过
- 锁争用解决方法:
- 优化SQL语句:尽量减少对同一行数据的频繁读写操作,避免不必要的锁持有时间。例如,将多个小事务合并成一个大事务,减少锁的获取次数。
- 合理设置锁超时时间:通过
innodb_lock_wait_timeout
参数设置事务等待锁的最长时间,当等待时间超过该值时,事务自动回滚,避免事务长时间等待锁。 - 使用乐观锁:在一些场景下,可以使用乐观锁机制。乐观锁假设在大多数情况下不会发生冲突,只在更新数据时检查数据是否被其他事务修改。例如,可以使用版本号字段,每次更新数据时版本号加1,更新前检查版本号是否与读取时一致,不一致则重新读取数据并更新。