开发者
MySQL OLTP 场景 TPS 瓶颈分析与 lock_sys 锁拆分

MySQL OLTP 场景 TPS 瓶颈分析与 lock_sys 锁拆分

mysql性能调优数据库

发表于 2026/03/17

0

背景

在 OLTP 场景里,当 update、insert、delete 等写操作占比较高时,系统吞吐量不一定能够达到理论峰值,可能会受到 MySQL 内的全局 latch 影响,从而达到性能瓶颈。  

在实际业务中,通常可以观察到以下现象:

-   CPU 利用率较低,并且随着并发数增加基本没有明显变化。

-   TPS 或 QPS在到达一定峰值后无法继续提高。

-   使用perf进行火焰图分析,发现 off-cpu 中锁相关热点函数占比大。

这些现象说明,性能瓶颈可能不在 SQL 本身,而在数据库内部的并发控制。  

图 1  sysbench 只写 off-cpu

下面将先解释 lock_sys 的工作方式,再看为什么锁拆分可以缓解这个问题。

原理

MySQL 中的每个表和行都可以看作是一种资源,事务可以请求访问资源。但是并发的事务对资源的访问可能造成冲突,因此 MySQL 设计了 Lock-sys 用于管理对表和行的访问。

Lock-sys 维护多个锁队列,管理事务对表和行资源的占用与等待。  

当一个新请求需要申请某个资源的时候,Lock-sys需要完成:

1.  在相应队列里查询资源是否已被占用;

2.  将请求入队(已授权或等待);将锁请求插入到相应的队列中(不管请求的资源是否被占用,分别标记为已授权或等待锁请求)。

3.  更新数据等待状态。

以上所有的查询与插入步骤都需要进行队列加锁保护。

数据库中,lock 和 latch 有时都被称为锁,但意义不同:

-   lock:数据库对象锁(表锁、行锁),用于事务隔离语义。  

-   latch:内存结构保护锁,用于保护内部共享结构。  

在过去,所有队列的访问均由一个 latch 管理,这意味着即使只要访问一个队列,其他所有的队列也会被锁住。这种实现方式在高并发场景下效率低下,为了解决这个问题,可以引入一种更细粒度的 latch 锁定方法。

从这里开始,核心问题就变成了:如何减少“不同队列之间的互相等待”。

1. 旧 latch

在 MySQL 中,所有的队列访问由同一个全局 latch 管理。  

即使两个事务访问的是不同队列,但也要先竞争同一把锁,导致其他所有的队列也会被锁住,这种实现方式在高并发场景下效率低下。

2. 新 latch

新的 latch 锁定方法是在原来全局大锁的基础上,把队列分为固定数量的 shard,每个 shard 用自己的 mutex 保护;同时保留一个全局大锁 global latch(读写锁)。

可以满足如下场景需求:

-   普通访问:对各个队列进行访问,先拿 global latch 的共享锁(s-latch),再获取相应 shard 的 mutex。(例如 MySQL 访问一条记录时,先对表加意向锁,再对相应记录加锁)

-   全局场景:在一些特殊场景下需要锁住所有队列,直接拿 global latch 的排他锁(x-latch)。

不同事务访问不同队列时,不需要再被同一个全局锁锁定,可以独立于对其他队列进行操作。

图 2  全局 latch 管理关系

新的 latch 锁效率提升如下图所示。在过去,如果事务 A 和事务 B 分别要访问队列 1 和队列 2,由于都需要对全局大锁上锁,这时事务 B 就会被阻塞;而在新方案里,事务 A 和事务 B 可以同时申请 global latch 的共享锁,再分别申请 mutex1 和 mutex2,也就是说事务 A 和事务 B 可以同时进行,并发度提升。

图 3  全局锁与分片锁并发效率对比

设计与实现

基于上面的原理,我可以通过以下步骤来优化锁,实现功能。

1. 双队列访问

访问两个队列获取两条记录时,过程如下:

1.  对 global_latch 执行 s-latch;

2.  标识记录所属的两个页;

3.  标识包含给定页队列的两个哈希桶;

4.  标识包含这两个桶的两个 shard id;

5.  按地址顺序对两个分片 mutex 上锁。

以上所有步骤(除步骤2外,因为我们通常已经知道这些页)可通过一行代码完成:

locksys::Shard_latches_guard guard{*block_a, *block_b};

2. 全局暂停场景

如果需要 stop-the-world,只需对 global latch 上 x-latch:

locksys::Exclusive_global_latch_guard guard{};

结论

从“背景现象 -> 原理分析 -> 设计实现”串起来看,锁拆分的目标就是减少无关队列之间的串行等待,提升高并发写场景下的有效吞吐。  

通过锁拆分技术可使 TPC-C 综合性能提升 10%。

本页内容