MySQL全局锁、表锁和行锁的概念

发布时间:2021-09-16 15:02:05 作者:chen
来源:亿速云 阅读:168
# MySQL全局锁、表锁和行锁的概念

## 引言

在数据库系统中,锁机制是保证数据一致性和并发控制的核心技术。MySQL作为最流行的开源关系型数据库之一,提供了多层次的锁机制来应对不同场景下的并发访问问题。本文将深入探讨MySQL中的全局锁、表锁和行锁三大锁类型,从基本概念到实现原理,从使用场景到实战案例,全面解析MySQL锁机制的工作方式。

## 第一章:MySQL锁机制概述

### 1.1 为什么需要锁机制

数据库锁的产生源于并发操作带来的数据一致性问题。当多个事务同时访问相同数据时,可能会出现以下典型问题:

- **脏读(Dirty Read)**:事务A读取了事务B未提交的修改
- **不可重复读(Non-repeatable Read)**:事务A多次读取同一数据,期间事务B修改了该数据
- **幻读(Phantom Read)**:事务A读取某个范围数据时,事务B插入了新数据

锁机制通过限制并发访问,确保事务的隔离性,从而解决这些问题。MySQL的锁设计遵循"尽可能提高并发度"的原则,提供了不同粒度的锁选项。

### 1.2 MySQL锁的分类体系

MySQL的锁可以按照多个维度进行分类:

1. **按锁的粒度划分**:
   - 全局锁:影响整个数据库实例
   - 表锁:影响整张表
   - 行锁:影响单行或多行记录

2. **按锁的性质划分**:
   - 共享锁(S锁):允许读,阻止写
   - 排他锁(X锁):阻止其他任何锁

3. **按锁的实现方式划分**:
   - 悲观锁:假定冲突会发生,先加锁再访问
   - 乐观锁:假定冲突很少,通过版本号等机制检测冲突

4. **按锁的兼容性划分**:
   - 兼容锁:可以同时持有的锁
   - 冲突锁:不能同时持有的锁

### 1.3 锁的兼容性矩阵

| 请求锁\持有锁 | 无锁 | S锁 | X锁 |
|--------------|------|-----|-----|
| S锁          | 允许 | 允许 | 拒绝 |
| X锁          | 允许 | 拒绝 | 拒绝 |

这个兼容性矩阵是理解MySQL锁冲突的基础。值得注意的是,MySQL在实际实现中还包含多种特殊锁类型,如意向锁、间隙锁等,我们将在后续章节详细讨论。

## 第二章:全局锁详解

### 2.1 全局锁的基本概念

全局锁是MySQL中粒度最大的锁,它会对整个数据库实例加锁。当需要全局锁时,所有表都变为只读状态,数据更新语句(DML)、数据定义语句(DDL)和更新类事务的提交语句都会被阻塞。

全局锁的典型使用命令是:
```sql
FLUSH TABLES WITH READ LOCK;  -- 加全局读锁
UNLOCK TABLES;                -- 释放全局锁

2.2 全局锁的实现原理

在MySQL内部,全局锁通过以下机制实现:

  1. 在加锁阶段:

    • 关闭所有打开的表
    • 清空查询缓存
    • 阻塞所有更新操作
  2. 在持有阶段:

    • 所有表只能进行读操作
    • 任何写操作会被挂起
  3. 在释放阶段:

    • 恢复正常的表访问
    • 被阻塞的操作按顺序执行

值得注意的是,全局锁是通过MySQL服务器层实现的,而不是存储引擎层。

2.3 全局锁的使用场景

全局锁主要用于以下场景:

  1. 全库逻辑备份:确保备份数据的一致性
  2. 主从同步初始化:确保主库在同步时数据不变化
  3. 重大架构变更:防止变更期间数据被修改

2.4 全局锁的优缺点分析

优点: - 实现简单,保证数据绝对一致 - 适用于需要全库静止的特殊场景

缺点: - 阻塞所有写操作,业务影响大 - 长时间持有可能导致连接堆积 - 不适用于高并发生产环境

2.5 替代方案:InnoDB的MVCC备份

由于全局锁的严重性,生产环境通常使用InnoDB的事务特性来替代:

-- 使用事务和一致性视图备份
START TRANSACTION WITH CONSISTENT SNAPSHOT;
-- 执行备份操作
-- ...
COMMIT;

这种方式利用MVCC(多版本并发控制)实现一致性读,不会阻塞写操作,是更优的选择。

第三章:表级锁详解

3.1 表锁的基本类型

MySQL中的表级锁主要分为:

  1. 普通表锁

    • 表共享读锁(TABLE READ LOCK)
    • 表独占写锁(TABLE WRITE LOCK)
  2. 元数据锁(MDL)

    • 保证表结构变更时的安全性
  3. 意向锁

    • 表明事务将要获取的行锁类型

3.2 普通表锁的操作方式

表锁的加锁语法:

LOCK TABLES 
    tbl_name [[AS] alias] lock_type
    [, tbl_name [[AS] alias] lock_type] ...

lock_type:
    READ [LOCAL] | [LOW_PRIORITY] WRITE

示例:

-- 加读锁
LOCK TABLES users READ;
-- 加写锁
LOCK TABLES users WRITE;
-- 释放锁
UNLOCK TABLES;

3.3 元数据锁(MDL)详解

MDL(Metadata Lock)是MySQL 5.5引入的重要特性,用于解决DDL操作与DML操作的冲突:

MDL锁的生命周期: 1. 事务开始时获取 2. 事务提交后释放 3. 不会自动降级

3.4 意向锁的作用原理

意向锁是表级锁,用于表明事务将要获取的行锁类型:

意向锁的主要目的是为了快速判断表内是否有行被锁定,避免全表扫描检查行锁状态。

3.5 表锁的竞争与死锁

表锁可能导致严重的竞争问题。例如:

-- 会话1
LOCK TABLES t1 WRITE;
-- 会话2
LOCK TABLES t2 WRITE;
-- 会话1
LOCK TABLES t2 WRITE; -- 阻塞
-- 会话2
LOCK TABLES t1 WRITE; -- 死锁

MySQL检测到这种循环依赖后会选择牺牲一个事务,通常选择回滚修改量较小的事务。

3.6 表锁的性能影响

表锁对性能的影响主要体现在: 1. 并发度降低 2. 查询响应时间增加 3. 系统吞吐量下降

通过以下命令可以监控表锁等待:

SHOW STATUS LIKE 'Table_locks%';

第四章:行级锁详解

4.1 行锁的基本类型

InnoDB引擎实现了以下几种行锁:

  1. 记录锁(Record Lock)

    • 锁定索引中的具体记录
    • 最基本的行锁类型
  2. 间隙锁(Gap Lock)

    • 锁定索引记录之间的间隙
    • 防止幻读的关键
  3. 临键锁(Next-Key Lock)

    • 记录锁+间隙锁的组合
    • 默认的行锁模式
  4. 插入意向锁(Insert Intention Lock)

    • 特殊的间隙锁
    • 优化并发插入性能

4.2 行锁的实现机制

InnoDB行锁通过索引实现:

  1. 当查询使用索引时:

    • 只锁定符合条件的索引项
    • 通过索引项找到对应的数据页
  2. 当查询不使用索引时:

    • 退化为表锁
    • 锁定整张表的所有行

这也是为什么强调SQL语句要走索引的重要原因。

4.3 不同隔离级别下的行锁

行锁的行为受事务隔离级别影响:

隔离级别 脏读 不可重复读 幻读 使用的锁类型
READ UNCOMMITTED 可能 可能 可能 无锁
READ COMMITTED 不可能 可能 可能 记录锁
REPEATABLE READ 不可能 不可能 可能 记录锁+间隙锁(默认)
SERIALIZABLE 不可能 不可能 不可能 记录锁+间隙锁+更严格限制

4.4 行锁的加锁规则

InnoDB行锁的加锁遵循以下原则:

  1. 基本加锁单位:next-key lock(前开后闭区间)
  2. 索引等值查询
    • 找到记录:退化为记录锁
    • 未找到:退化为间隙锁
  3. 索引范围查询
    • 访问到的所有记录和间隙都加锁
  4. 唯一索引
    • 等值查询可优化锁范围

4.5 行锁的死锁问题

行锁更容易导致死锁,典型场景:

  1. 会话交叉更新: “`sql – 会话1 UPDATE accounts SET balance = balance - 100 WHERE id = 1; UPDATE accounts SET balance = balance + 100 WHERE id = 2;

– 会话2 UPDATE accounts SET balance = balance - 200 WHERE id = 2; UPDATE accounts SET balance = balance + 200 WHERE id = 1;


2. **并发插入**:
   多个事务在相同间隙插入不同记录

可以通过以下方式减少死锁:
- 事务尽量小且快
- 按固定顺序访问记录
- 合理设置索引

### 4.6 行锁的性能优化

优化行锁性能的关键点:

1. **索引设计**:
   - 确保查询使用合适的索引
   - 避免索引失效导致表锁

2. **事务控制**:
   - 减少事务持有锁的时间
   - 避免长事务

3. **监控分析**:
   ```sql
   SHOW ENGINE INNODB STATUS;  -- 查看锁等待
   SELECT * FROM performance_schema.events_waits_current;  -- 当前等待事件

第五章:锁的监控与诊断

5.1 锁等待的监控方法

MySQL提供了多种监控锁的方式:

  1. information_schema

    SELECT * FROM information_schema.INNODB_TRX;  -- 当前运行事务
    SELECT * FROM information_schema.INNODB_LOCKS;  -- 锁信息
    SELECT * FROM information_schema.INNODB_LOCK_WTS;  -- 锁等待
    
  2. performance_schema

    SELECT * FROM performance_schema.metadata_locks;  -- MDL锁
    
  3. SHOW命令

    SHOW OPEN TABLES WHERE In_use > 0;  -- 被锁定的表
    

5.2 常见的锁问题诊断

  1. 锁等待超时

    -- 错误:Lock wait timeout exceeded; try restarting transaction
    -- 解决方案:增加innodb_lock_wait_timeout参数或优化事务
    
  2. 死锁检测

    SHOW ENGINE INNODB STATUS\G
    -- 查看LATEST DETECTED DEADLOCK部分
    
  3. 长时间运行事务

    SELECT * FROM information_schema.INNODB_TRX 
    ORDER BY TIME_MS DESC LIMIT 5;
    

5.3 锁相关的性能指标

关键性能指标监控:

-- 表锁等待
SHOW STATUS LIKE 'Table_locks%';

-- 行锁等待
SHOW STATUS LIKE 'Innodb_row_lock%';

-- 死锁次数
SHOW STATUS LIKE 'Innodb_deadlocks';

5.4 pt-deadlock-logger工具

Percona提供的死锁监控工具:

pt-deadlock-logger u=root,p=password,h=localhost

可以持续监控并记录死锁事件,便于后续分析。

第六章:锁优化实践

6.1 锁优化的基本原则

  1. 减小锁粒度

    • 优先使用行锁而非表锁
    • 合理设计索引
  2. 缩短持有时间

    • 事务尽量短小
    • 锁获取尽量靠后
  3. 降低锁冲突

    • 访问顺序一致化
    • 热点数据分散

6.2 索引设计对锁的影响

良好的索引设计可以显著减少锁冲突:

  1. 覆盖索引

    • 减少回表操作
    • 减少锁定的数据量
  2. 合适的主键

    • 自增主键减少插入竞争
    • 避免随机主键导致页分裂
  3. 复合索引

    • 使查询尽可能使用索引
    • 遵循最左前缀原则

6.3 事务设计的最佳实践

优化事务设计的建议:

  1. 控制事务大小

    • 避免在事务中包含用户交互
    • 批量操作分批次提交
  2. 合理设置隔离级别

    • 默认使用REPEATABLE READ
    • 明确需要时才提高级别
  3. 避免交叉访问

    • 按固定顺序访问表和记录
    • 减少死锁概率

6.4 应用层优化策略

应用层可以采用的优化:

  1. 乐观锁实现

    UPDATE products 
    SET stock = stock - 1, version = version + 1 
    WHERE id = 100 AND version = 5;
    
  2. 排队机制

    • 对热点数据采用队列处理
    • 减少并发冲突
  3. 读写分离

    • 查询走从库
    • 减轻主库锁压力

第七章:特殊场景下的锁处理

7.1 在线DDL操作的锁处理

MySQL 8.0对DDL操作进行了重要改进:

  1. INSTANT算法

    • 添加列等操作瞬间完成
    • 不阻塞DML
  2. INPLACE算法

    • 需要重建表但不阻塞DML
    • 如添加索引
  3. COPY算法

    • 传统方式,锁表时间长
    • 如修改列数据类型

可以通过ALGORITHM选项指定:

ALTER TABLE users ADD COLUMN age INT, ALGORITHM=INPLACE;

7.2 外键约束与锁

外键约束会引入额外的锁:

  1. 父表操作

    • 更新父表记录需要检查子表
    • 获取子表的共享锁
  2. 子表操作

    • 插入/更新子表需要检查父表
    • 获取父表的共享锁

可以通过以下方式优化: - 合理设计外键索引 - 考虑不使用外键,改由应用维护

7.3 自增主键的锁机制

InnoDB处理自增主键的锁:

  1. 传统模式

    • 使用表级AUTO-INC锁
    • 影响并发插入性能
  2. 交错模式(8.0默认)

    • 使用轻量级锁
    • 更高并发但可能不连续

配置参数:

innodb_autoinc_lock_mode = 2  -- 交错模式

7.4 全文索引的锁特性

全文索引操作的特殊锁行为:

  1. FTS_DOC_ID列

    • 有特殊的锁保护机制
    • 影响并发插入性能
  2. 优化建议

    • 避免频繁更新全文索引列
    • 考虑使用专业搜索引擎

第八章:MySQL 8.0的锁改进

8.1 原子DDL特性

MySQL 8.0引入的原子DDL特性:

  1. 完全成功或完全失败

    • 不再存在部分成功的情况
    • 元数据更新原子化
  2. 崩溃安全

    • 服务器崩溃后自动恢复
    • 不会遗留中间状态

8.2 新增的SKIP LOCKED选项

处理锁等待的新方式:

SELECT * FROM orders 
WHERE status = 'pending'
FOR UPDATE SKIP LOCKED;  -- 跳过已锁定的行

适用于任务队列等场景,避免阻塞。

8.3 NOWT选项

立即返回错误而非等待:

SELECT * FROM products 
WHERE id = 100
FOR UPDATE NOWT;  -- 如果被锁定立即报错

适用于低延迟要求的场景。

8.4 性能架构的增强

MySQL 8.0增强了锁监控:

  1. 更多instrumentation

    • 更详细的锁等待统计
    • 更细粒度的监控指标
  2. 可视化工具支持

    • Performance Schema集成更好
    • 便于监控工具展示

第九章:实际案例分析

9.1 案例一:备份导致的业务停顿

现象: - 每日备份期间业务响应变慢 - 大量查询处于”Waiting for global read lock”状态

分析: - 备份工具使用了FLUSH TABLES WITH READ LOCK - 大表导致锁定时间过长

解决方案: 1. 改用InnoDB热备份工具 2. 使用–single-transaction选项 3. 在业务低峰期执行备份

9.2 案例二:MDL锁等待链

现象: - 简单的ALTER TABLE操作卡住 - 大量查询处于”Waiting for table metadata lock”

分析: - 有长时间运行的事务未提交 - 该事务

推荐阅读:
  1. mysql表锁和行锁有哪些区别
  2. mysql表锁和行锁区别是什么

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

mysql

上一篇:linux中df命令

下一篇:如何实现控制输出颜色的shell脚本

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》