MySQL中 全局锁 表锁 行锁

全局锁

定义

全局锁(Global Lock)是指在 MySQL 数据库中对整个数据库实例进行的锁定。当一个全局锁被施加时,所有其他的查询和操作都会被阻塞,直到锁被释放。这种锁定通常用于维护数据库的完整性,确保在进行特定操作时数据库处于一致状态。

MySQL 主要通过 FLUSH TABLES WITH READ LOCK 命令来实现全局锁。此命令会阻止任何写入操作,而允许读取操作,从而在某些需要全局查看数据的情况下保持数据的一致性。

SQL

加锁

flush table with read lock;

释放锁

unlock tables;

常出现的情况

  1. 备份

    • 在进行逻辑备份(使用 mysqldump)时,为了确保备份过程中的数据一致性,可能会使用全局锁。通过锁定数据库,确保在备份期间数据不被修改。
  2. 维护操作

    • 在执行需要对数据库进行结构修改的操作(如修改表结构、删除表等)时,可能会需要全局锁来确保这些操作不会被其他正在执行的事务影响。
  3. 高并发环境

    • 在高并发访问的环境中,某些操作可能需要全局锁以保证数据的完整性和一致性。

为何会出现全局锁

全局锁会在以下情况下出现:

  1. 人为触发

    • 通过执行特定的 SQL 命令(如 FLUSH TABLES WITH READ LOCK)来施加全局锁。
  2. 故障恢复

    • 在某些情况下,需要锁定数据库以进行故障恢复和数据修复。全局锁可以确保在处理过程中不再有其他事务对数据进行修改。
  3. 数据一致性需求

    • 在一些特定应用场景中,为了保持数据一致性,可能需要在某个时刻锁定所有数据。

如果不加锁会出现什么问题

  1. 数据不一致性

    • 如果在备份或维护期间不加锁,可能会导致备份的数据与数据库当前状态不一致。例如,在备份时,如果数据正在被修改,备份得到的数据可能是部分更新的数据,无法反映数据库的真实状态。
  2. 死锁

    • 在高并发环境中,如果多个事务在不同的顺序上请求锁,可能会导致死锁,从而使得某些事务无法完成。
  3. 数据损坏

    • 某些数据库操作(如表结构修改)如果在写操作进行时执行,可能会导致数据损坏或丢失。
  4. 难以恢复操作

    • 如果在进行数据维护时不加锁,且遇到错误或异常,可能会导致数据处于不一致的状态,恢复操作将变得复杂。

具体示例

假设有一个在线购物系统,用户不断进行商品的购买和库存的更新操作。在备份期间,如果不加全局锁,可能会发生以下情况:

  • 用户 A 正在购买一件商品,库存从 10 减少到 9。
  • 同时,备份操作也在进行,备份过程中的库存仍然是 10。
  • 一旦备份完成,用户 A 的购买事务提交,库存变为 9,而备份中的数据仍然反映库存为 10。这就导致了数据的不一致性。

注意

数据库中加全局锁,是一个比较重的操作,存在以下问题

1.如果主库进行备份,那么备份期间都不能执行更新,业务基本属于停摆状态
2.如果再从库进行备份,那么备份期间从库无法执行主库同步过来的二进制文件(binlog),会导致主从延迟

再InnoDB引擎中,我们可以在备份时加上 --single-transaction 参数来完成不加锁的一致性数据库备份


表级锁

1. 表锁(Table Lock)

定义

表锁是对整个表施加的锁,主要用于控制对表的读写访问。表锁有两种类型:

  • 共享读锁/读锁(READ LOCK):允许多个事务并发读取表,但不允许任何事务修改表。
  • 表独占写锁/写锁(WRITE LOCK):只允许一个事务修改表,并阻止其他事务读取或修改。
SQL

加锁

lock tables 表名 read/write

释放锁

unlock tables;
-- 或者关闭连接客户端锁会自动释放
常见情况
  • 大规模数据导入:在进行数据导入时,可以使用表锁来防止其他操作对表的修改,以确保数据的完整性。
  • 维护操作:在对表进行结构修改、删除等操作时,通常会使用表锁以确保没有其他事务干扰。
出现原因

表锁的出现主要是为了确保在某些关键操作期间,表的状态不会被其他事务改变,从而保证操作的原子性和一致性。

不加锁可能出现的问题
  • 数据不一致性:在进行复杂的读写操作时,如果不使用表锁,其他事务可能会修改正在操作的数据,导致数据的不一致性。
  • 错误结果:在读取数据期间,其他事务可能会对数据进行修改,导致返回的数据与预期不符。

2. 元数据锁(Metadata Lock,MDL)

定义

元数据加锁过程是系统自动控制的,无需显示使用,在访问一张表的时候会自动加上,元数据锁主要作用是维护表元数据得数据一致性,在表上有活动事务的时候,不可以对元数据进行写入操作。(避免DML和DDL冲突,保证读写的正确性)在MySQL 5.5中引入了元数据锁,当对一张表进行增删改查时,加MDL读锁(共享);当对表结构进行变更操作的时候,加MDL写锁(排他)

常见情况
  • 表结构变更:在执行 ALTER TABLEDROP TABLE 或者 CREATE INDEX 等操作时,元数据锁会被触发。
  • 事务中的DDL操作:当一个事务对表进行 DDL 操作时,元数据锁可以防止其他事务对该表进行任何 DML 操作。
出现原因

元数据锁的存在是为了保护数据库的结构不被同时修改,确保在执行 DDL 操作时不会影响到正在进行的 DML 操作。

不加锁可能出现的问题
  • 元数据损坏:如果在对表执行 DDL 操作时不加锁,可能导致数据字典的损坏,进而影响数据库的稳定性和可用性。
  • 不一致的表结构:在读取表结构或进行其他操作时,如果没有适当的锁机制,可能导致表结构不一致,影响查询和数据修改的正确性。

3. 意向锁(Intent Lock)

意向锁是 MySQL 中的一种重要锁机制,主要用于协调行级锁和表级锁之间的关系。通过意向锁,数据库可以在不直接锁定整个表的情况下,指示某个事务将要对表中的特定行进行锁定。这种机制能够有效避免死锁和锁竞争,提升并发性能。

查看意向锁
select * from performance_schema.data_locks;
意向锁的种类
  • 意向共享锁(IS):表示事务希望在某个行上获取共享锁。

    由语句 select … lock in share mode添加

  • 意向排他锁(IX):表示事务希望在某个行上获取排他锁。

    由insert、update、delete、select … for update 添加

兼容性
表共享锁(read)表锁排他锁(write)
意向共享锁(IS)兼容互斥
意向排他锁(IX)互斥互斥

意向锁直接不存在互斥


好的,我们可以通过一个更通俗易懂的例子来说明意向锁(Intent Lock)的工作机制,同时结合数据表格的形式来帮助理解。

场景描述

假设我们有一个名为 employees 的表,包含以下字段:

IDNameSalary
1Alice60000
2Bob55000
3Charlie70000
4David50000
5Eva65000

现在,我们有两个事务(Transaction A 和 Transaction B)需要对这个表进行操作。我们将通过这个例子来展示意向锁的必要性及其作用。

假设无意向锁
  1. 事务 A 开始,事务A欲更新 id为 3 的数据,对数据行加了一个行锁
  2. 此时事务B 想要对表加表锁,需要扫描表中数据去判断表中是否有锁,如果有所需要判断锁的类型从而判定能否加锁,因存在全表扫描效率很低
存在意向锁
  1. 事务 A 开始,事务A欲更新 id为 3 的数据,对数据行加了一个行锁,对表加一个意向锁
  2. 此时事务B 想要对表加表锁,判断表的意向锁和想要加的锁是否兼容,如果兼容则加锁成功,如果不兼容则出现阻塞,阻塞到事务A完成
结果分析
  1. 避免死锁

    • 由于意向锁的存在,事务 B 清楚地知道它不能直接对该表加锁。这样可以避免它与事务 A 之间可能出现的死锁情况。
  2. 提高并发性能

    • 如果没有意向锁,事务 B 可能会在没有必要的情况下等待整个表的锁,这会导致性能下降。在意向锁的机制下,事务 B 知道需要等待事务 A 先完成,才能安全地进行操作。

行级锁

1. 行级锁(Row-Level Lock)

定义

行级锁是数据库中最细粒度的锁定机制,允许多个事务同时在同一张表中操作不同的行。行级锁提升了数据库的并发性,因为它允许多个事务同时进行,而不互相干扰。

特点
  • 高并发:多个事务可以同时对不同的行进行操作。
  • 低锁争用:由于锁定的范围较小,行级锁减少了锁竞争。
示例

假设我们有一个 employees 表,如下所示:

IDNameSalary
1Alice60000
2Bob55000
3Charlie70000
  • 事务 A 更新 ID = 1 的记录:

    START TRANSACTION;
    UPDATE employees SET Salary = 65000 WHERE ID = 1;
    
  • 事务 B 更新 ID = 2 的记录:

    START TRANSACTION;
    UPDATE employees SET Salary = 60000 WHERE ID = 2;
    

在这个示例中,事务 A 和事务 B 可以同时进行,因为它们操作的是不同的行(ID = 1 和 ID = 2)。这展示了行级锁的高并发性。


2. 行级锁的间隙锁(Gap Lock)

定义

间隙锁是一种专门的锁定机制,主要用于防止幻读。在执行范围查询时,间隙锁锁定了行与行之间的“间隙”,以防止其他事务在这个间隙中插入新的行。

特点
  • 防止幻读:间隙锁可以防止其他事务插入新行,从而避免在同一事务中多次查询时得到不同的结果。
  • 较高的锁定范围:除了锁定实际的行,间隙锁还锁定了行之间的空间。
示例

继续使用 employees 表,假设我们有以下记录:

IDNameSalary
1Alice60000
2Bob55000
3Charlie70000
  • 事务 A 执行以下查询,查找 ID 在 1 到 2 之间的员工:
    START TRANSACTION;
    SELECT * FROM employees WHERE ID BETWEEN 1 AND 2 FOR UPDATE;
    

此时,事务 A 会在 ID = 1 和 ID = 2 的行之间加上间隙锁,这样任何其他事务都不能在 ID = 1 和 ID = 2 之间插入新行。

  • 事务 B 尝试插入 ID = 1.5 的记录:
    INSERT INTO employees (ID, Name, Salary) VALUES (1.5, 'David', 50000);
    

由于事务 A 在 ID = 1 和 ID = 2 之间加了间隙锁,事务 B 将被阻塞,直到事务 A 提交或回滚。


3. 临表锁(Table Lock)

定义

临表锁是对整个表的锁定机制,通常用于需要对表中的所有行进行操作的情况。临表锁分为共享锁(S)和排他锁(X)。

特点
  • 锁定范围大:锁定整个表,所有对该表的读写操作都会被阻塞,直到锁被释放。
  • 较低的并发性:由于锁定整个表,临表锁可能导致其他事务的等待,从而降低并发性能。
示例

使用 employees 表:

IDNameSalary
1Alice60000
2Bob55000
3Charlie70000
  • 事务 A 需要对整个表加排他锁进行一些批量更新操作:

    START TRANSACTION;
    LOCK TABLES employees WRITE;
    UPDATE employees SET Salary = Salary * 1.1; -- 可能是给所有员工加薪
    COMMIT;
    
  • 事务 B 尝试在事务 A 期间读取 employees 表:

    START TRANSACTION;
    SELECT * FROM employees;
    

由于事务 A 已经对 employees 表加了临表锁,事务 B 将被阻塞,直到事务 A 提交或回滚。


4. 临键锁(Next-Key Lock)

定义

临键锁(Next-Key Lock)是一种结合了行级锁和间隙锁的锁定机制。它不仅锁定了记录行本身,还锁定了行与行之间的间隙,这种锁的设计目的是为了防止幻读。

特点
  • 组合锁定:同时锁定行和行之间的间隙,以防止其他事务在这个间隙中插入新记录。
  • 防止幻读:通过锁定间隙,确保在一个事务内的多次查询结果一致。
示例

使用 employees 表,结构如下:

IDNameSalary
1Alice60000
2Bob55000
3Charlie70000

假设我们有两个事务(Transaction A 和 Transaction B)进行如下操作:

事务 A 的操作
  • 事务 A 执行一个范围查询,并希望获取 ID 在 1 到 3 之间的员工记录:
    START TRANSACTION;
    SELECT * FROM employees WHERE ID BETWEEN 1 AND 3 FOR UPDATE;
    

此时,事务 A 会对 ID = 1 和 ID = 3 的行加上临键锁,同时也会锁定 ID = 1 和 ID = 2 之间的间隙。

事务 B 的操作
  • 事务 B 尝试插入一个新的记录,ID = 2.5:
    START TRANSACTION;
    INSERT INTO employees (ID, Name, Salary) VALUES (2.5, 'David', 50000);
    

由于事务 A 已经对 ID = 1 和 ID = 3 的行加了锁,并且锁定了 ID = 1 和 ID = 2 之间的间隙(因为 ID = 2 是范围查询的结束条件),事务 B 将会被阻塞,无法在 ID = 1 和 ID = 2 之间插入新行。


总结

  • 行级锁 提供了较高的并发性,允许多个事务同时操作不同的行。
  • 间隙锁 用于防止幻读,锁定行之间的间隙,从而避免新行的插入。
  • 临表锁 锁定整个表,通常用于需要对表进行批量操作的场景,但可能导致较低的并发性。
  • 临键锁 结合了行级锁和间隙锁的特性,既锁定指定的行,也防止在行与行之间插入新的记录,从而有效防止幻读。

这些锁机制在数据库的并发控制中起着至关重要的作用,通过合理地使用它们,可以确保数据的一致性和完整性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值