Skip to content

MySQL的锁机制是其核心特性之一,用于管理对共享资源的并发访问,确保数据的一致性和完整性。 不同的存储引擎(如InnoDB和MyISAM)支持不同粒度的锁。

一、MySQL锁的分类

MySQL的锁可以从多个维度进行分类,主要可以分为按粒度划分按属性/模式划分以及按算法划分

1. 按锁定的粒度划分

这是最常见的分类方式,指锁定的资源范围大小。

锁类型描述优点缺点适用引擎
全局锁 (Global Lock)锁定整个数据库实例。一个典型命令是 FLUSH TABLES WITH READ LOCK,常用于数据库备份。可以方便地进行整库备份,确保数据一致性。开销大,会阻塞所有更新操作,对业务影响大。所有引擎
表级锁 (Table-level Lock)直接锁定整张表。开销小,加锁快,不会出现死锁。锁定粒度大,并发冲突概率高,并发度最低。MyISAM, InnoDB
页级锁 (Page-level Lock)锁定数据页,粒度介于表锁和行锁之间。开销和并发能力介于表锁和行锁之间。会出现死锁,目前已较少使用。BDB引擎
行级锁 (Row-level Lock)仅锁定被操作的行记录。锁定粒度最小,并发冲突概率最低,并发度最高。开销大,加锁慢,可能会出现死锁。InnoDB

注意:

  • MyISAM 存储引擎主要使用表级锁,因此并发写入性能较差。
  • InnoDB 存储引擎同时支持表级锁行级锁,默认使用行级锁,提供了更好的并发性能。

2. 按属性/模式划分

这描述了锁的兼容性。

锁类型别名描述兼容性
共享锁 (Shared Lock)S锁, 读锁允许多个事务同时读取同一资源,但阻止其他事务获取排他锁。 事务可以通过 SELECT ... LOCK IN SHARE MODE (MySQL 8.0 之前) 或 SELECT ... FOR SHARE (MySQL 8.0 及之后) 显式地为记录加共享锁。与其他共享锁(S锁)兼容,与排他锁(X锁)互斥。
排他锁 (Exclusive Lock)X锁, 写锁只允许持有该锁的事务对资源进行读写,阻止其他任何事务获取该资源的任何锁。 INSERT, UPDATE, DELETE 操作会自动为涉及的数据行加上排他锁。 也可以通过 SELECT ... FOR UPDATE 显式添加。与任何其他锁(S锁或X锁)都互斥。
意向锁 (Intention Lock)IS锁, IX锁意向锁是表级锁,用于表示事务打算在表中的某些行上设置行级锁(共享或排他)。 它能让InnoDB在判断表级锁兼容性时,无需扫描整个表的行级锁,从而提高效率。它分为意向共享锁(IS)和意向排他锁(IX)。意向锁之间是互相兼容的。

3. 按算法划分 (InnoDB特有)

InnoDB的行级锁是基于索引实现的,其具体实现分为以下几种。

锁类型描述目的
记录锁 (Record Lock)这是标准的行锁,它锁定的是索引记录本身。精确地锁定某一条记录,防止被其他事务修改。
间隙锁 (Gap Lock)锁定一个索引记录之间的“间隙”,但不包括记录本身。防止其他事务在这个间隙中插入新的记录,从而避免“幻读”问题。这主要在**可重复读(REPEATABLE READ)**隔离级别下起作用。
临键锁 (Next-Key Lock)它是记录锁间隙锁的组合,锁定一个范围,并包括记录本身(左开右闭区间)。 InnoDB在可重复读隔离级别下默认使用临键锁。既能锁定记录本身,又能防止幻读,是InnoDB默认的行锁算法。
插入意向锁 (Insert Intention Lock)一种特殊的间隙锁,在INSERT操作之前设置。 它表明有事务想要在某个间隙中插入数据,但只有在不与现有间隙锁或临键锁冲突时才会成功。提高并发插入的性能。

二、如何查询和监控锁信息

当出现查询缓慢、事务阻塞等问题时,查看当前的锁信息至关重要。

1. 使用 SHOW 命令

  • 查看表是否被锁:

    sql
    SHOW OPEN TABLES WHERE In_use > 0;

    如果 In_use 列的值大于0,说明该表当前被加了表锁或正在被某个事务使用。

  • 查看InnoDB引擎状态: 这是一个功能非常强大的命令,可以提供详细的InnoDB运行状态,包括最近的死锁信息、事务和锁等待情况。

    sql
    SHOW ENGINE INNODB STATUS\G

    在输出结果中,重点关注 LATEST DETECTED DEADLOCKTRANSACTIONS部分。

2. 查询系统信息库 (information_schema / performance_schema)

MySQL将锁的相关信息存储在系统表中,通过查询这些表可以获得更精确的锁数据。

  • 对于 MySQL 5.7 及更早版本: 主要使用 information_schema 库中的表。

    sql
    -- 查看当前所有事务
    SELECT * FROM information_schema.INNODB_TRX;
    
    -- 查看当前出现的锁
    SELECT * FROM information_schema.INNODB_LOCKS;
    
    -- 查看锁等待关系
    SELECT * FROM information_schema.INNODB_LOCK_WAITS;
  • 对于 MySQL 8.0 及更高版本: 锁相关的信息被移到了 performance_schema 库中,提供了更高效和详细的监控。 information_schema.INNODB_LOCKS 等表已被废弃。

    sql
    -- 查看当前出现的锁
    SELECT * FROM performance_schema.data_locks;
    
    -- 查看锁等待关系
    SELECT * FROM performance_schema.data_lock_waits;

    以下是一个查询锁等待的实用SQL语句(适用于MySQL 8.0):

    sql
    SELECT
        r.trx_id AS waiting_trx_id,
        r.trx_mysql_thread_id AS waiting_thread,
        r.trx_query AS waiting_query,
        b.trx_id AS blocking_trx_id,
        b.trx_mysql_thread_id AS blocking_thread,
        b.trx_query AS blocking_query
    FROM
        performance_schema.data_lock_waits w
    JOIN
        information_schema.innodb_trx b ON b.trx_id = w.blocking_engine_transaction_id
    JOIN
        information_schema.innodb_trx r ON r.trx_id = w.requesting_engine_transaction_id;

总结与建议

  • 选择合适的存储引擎:对于需要高并发和事务支持的场景,应优先选择 InnoDB。 对于读密集且不需要事务的场景,MyISAM 可能是个不错的选择。
  • 优化SQL和索引:InnoDB的行锁是基于索引的,如果SQL查询没有命中索引,可能会导致行锁升级为表锁,从而大大降低并发性能。 因此,合理设计索引至关重要。
  • 控制事务大小:尽量保持事务简短,减少锁的持有时间,可以有效降低锁冲突的概率。

Released under the MIT License.