2026-01-01 13:16:09
MySQL优化事务锁等待的核心是减少锁冲突、缩短锁持有时间,并合理设计事务逻辑,具体可从以下方面入手:
缩短事务执行时间
避免耗时操作:长时间运行的事务会持续持有锁,增加其他事务等待概率。应避免在事务中执行复杂计算、网络请求或大批量数据处理等耗时操作。例如,不要在事务中进行大规模数据的排序、聚合计算,或者调用外部API获取数据。
合理开启事务:只在真正需要一致性写入时才开启事务,读操作可使用一致性非锁定读(如RC隔离级别下的快照读)。例如,对于一些仅需要查询数据展示的场景,无需开启事务。
提前准备数据:避免在事务中频繁查询或判断,提前准备好事务所需的数据。比如,在执行更新操作前,先通过一次查询获取需要更新的数据,而不是在事务中多次查询。
合理设计索引
确保WHERE条件字段有索引:没有合适索引时,MySQL可能加更多行锁甚至间隙锁,显著提升死锁和等待概率。尤其是UPDATE和DELETE操作,要确保WHERE条件字段有索引。例如,对一个经常根据用户ID进行更新的表,应在用户ID字段上创建索引。
使用覆盖索引:覆盖索引可减少回表,降低锁范围。覆盖索引是指一个索引包含了查询所需的所有字段,这样查询时就不需要再回到数据表中获取数据。例如,查询某个用户的姓名和年龄,如果有一个包含用户ID、姓名和年龄的联合索引,就可以直接从索引中获取数据,而不需要回表。
分析执行计划:使用EXPLAIN分析执行计划,确认是否走索引。通过EXPLAIN可以查看SQL语句的执行过程,包括是否使用了索引、使用的索引类型等信息。如果发现没有使用索引,可以检查索引是否创建正确,或者SQL语句是否可以优化。
按固定顺序访问多表或多行数据
保持更新顺序一致:多个事务以不同顺序修改相同资源容易引发死锁,MySQL会自动检测并回滚其中一个,但重试仍影响性能。在所有应用代码中,更新多张表或多个行时保持一致的顺序,例如先改用户表再改订单表。
批量更新按主键排序:对主键或唯一键批量更新时,按主键排序后再执行。这样可以减少锁冲突的概率,提高并发性能。
选择合适的隔离级别
使用READ COMMITTED隔离级别:大部分场景使用READ COMMITTED足够,避免REPEATABLE READ下的间隙锁过度锁定。READ COMMITTED隔离级别下,事务只能读取到已经提交的数据,减少了锁的范围和持续时间。
谨慎使用READ UNCOMMITTED隔离级别:如果允许脏读且对一致性要求低,可考虑READ UNCOMMITTED,但需谨慎。READ UNCOMMITTED隔离级别下,事务可以读取到未提交的数据,可能会导致数据不一致的问题。
评估乐观锁替代悲观事务:高并发写场景下,可评估是否可用乐观锁替代悲观事务。乐观锁通常通过版本号或时间戳来实现,在更新数据时检查版本号或时间戳是否发生变化,如果发生变化则说明数据被其他事务修改过,当前事务需要重新获取数据并重试。
监控与诊断锁等待
查看INNODB STATUS信息:通过SHOW ENGINE INNODB STATUS中的LATEST DETECTED DEADLOCK和TRANSACTIONS部分,可以查看最新的死锁信息和事务信息,帮助定位锁等待问题。
查询锁等待关系表:查询information_schema.innodb_lock_waits和相关表分析等待关系。这些表记录了锁的等待情况,包括等待锁的事务ID、持有锁的事务ID、锁的类型等信息。
开启慢查询日志:开启慢查询日志,找出执行时间长、可能持锁久的SQL。通过分析慢查询日志,可以找到需要优化的SQL语句,对其进行索引优化、SQL重写等操作。