前言MySQL是互联网公司用得最多的数据库而InnoDB则是MySQL生态中最常见的存储引擎。它为什么能够在大数据量、高并发的互联网业务中稳定运行今天我们来聊聊InnoDB的并发控制、锁机制和MVCC——从基础概念到内核设计的完整逻辑。这篇文章稍长一些建议收藏后慢慢品读。并发控制为什么数据库需要它想象一个场景多个请求同时对同一条数据进行操作。如果没有任何保护措施结果会是混乱的——某个线程还在修改数据另一个线程已经开始读取最后导致数据不一致。这就是为什么数据库必须有并发控制机制。从技术角度看保证数据一致性的方法通常有两类一是用锁来阻止冲突操作二是用数据多版本来让不同操作并行进行。从普通锁到读写锁的进化最朴素的想法就是用一把锁把临界区锁死操作数据前加锁操作完后释放。但这样做太粗暴了——即使只是读取数据也要等待写操作完成所有操作本质上变成了串行。这显然不够。后来人们想到了共享锁和排他锁的区分读取数据时加共享锁S锁多个读操作可以同时进行修改数据时加排他锁X锁谁都得等这样就实现了读读并行的目标。但问题依然存在一旦某个写操作开始执行所有读操作就必须阻塞。对应到数据库层面就是写事务未提交时相关数据的select会被挡住。能不能进一步突破这个瓶颈呢最近无意间获得一份阿里大佬写的刷题笔记一下子打通了我的任督二脉进大厂原来没那么难。 这是大佬写的 7701页的BAT大佬写的刷题笔记让我offer拿到手软数据多版本读写真正并行的钥匙这里引出了一个优雅的idea——数据多版本。核心思想很简单写操作不要直接修改原数据而是复制一份新版本来修改。这样并发的读操作仍然可以读取旧版本的数据。写操作也不会被读阻塞。想象这样一个过程T1时刻某个写操作开始它克隆了一份数据版本V1开始修改。T2时刻一个读操作到达它读取的仍是原版本V0的数据。T3时刻又来了一个读照样读V0。直到写操作提交新版本才会变成当前版本。这就是读写并行的秘诀。并发能力的演进就这样展开了普通锁做不到并行 → 读写锁实现读读并行 → 多版本技术实现读写并行。这个思路比具体的技术细节更重要。实现多版本的基础redo日志与undo日志在InnoDB真正如何利用多版本之前我们需要理解两样东西redo日志和undo日志。redo日志的两个使命事务提交后数据必须保证刷到磁盘。但每次都直接写磁盘太低效了——磁盘随机写是性能杀手。聪明的办法是先把修改操作写到redo日志这是顺序写快得多然后定期将数据刷到磁盘。这样既保证了ACID特性又大幅提高了吞吐量。如果数据库中途崩溃重启时可以重新执行redo日志里的操作确保所有已提交的事务都被持久化。简言之redo日志保护已提交事务。undo日志的角色undo日志做的事相反。当事务修改数据时修改前的旧版本被存入undo日志。如果事务需要回滚或数据库崩溃需要恢复这些旧版本数据就派上用场了。具体来说insert操作的undo记录新数据的主键delete和update的undo记录完整的旧数据行。回滚时直接用这些旧版本恢复就行。undo日志的真正妙用是为MVCC提供了旧版本数据的来源。回滚段undo的仓库存储undo日志的地方叫回滚段。我们用一个例子来看它如何工作。假设有个表 t(id PK, name)初始数据是体验AI代码助手代码解读复制代码1, xiaobei 2, zhangsan 3, lisi现在启动一个事务执行了几个操作但还未提交sql体验AI代码助手代码解读复制代码start trx; delete (1, xiaobei); update (3, lisi) to (3, xxx); insert (4, wangwu);此时回滚段中会出现这些记录delete之前的 (1, shenjian) 进入回滚段update之前的 (3, lisi) 进入回滚段insert的新PK 4 也进去了如果事务要回滚这些undo数据就会被用上被删的行恢复了被改的行恢复了新插入的行被删掉了。一切回到原点。InnoDB的MVCC多版本并发控制的真面目InnoDB之所以能在互联网的高并发场景中表现出色根本原因就是MVCC多版本并发控制。它通过让事务读取旧版本数据从而大幅降低锁冲突。InnoDB内核给每一行数据都加了三个隐藏属性DB_TRX_ID6字节最后修改这行数据的事务IDDB_ROLL_PTR7字节指向回滚段中undo日志的指针DB_ROW_ID6字节单调递增的行号这样设计看似简单实际上威力巨大。回滚段里的数据是历史快照永不修改。因此select语句可以放心地去读取它们完全不需要加锁。这种不加锁的一致性读就叫快照读。它是InnoDB并发高的核心秘密。所谓一致性是指事务读到的数据要么是事务开始前就存在的来自其他已提交事务要么是事务自己插入或修改的。什么是快照读除非你显式加锁否则普通的select都是快照读sql体验AI代码助手代码解读复制代码select * from t where id 2;显式加锁的读就不同了sql体验AI代码助手代码解读复制代码select * from t where id 2 lock in share mode; select * from t where id 2 for update;这两种会加上共享锁或排他锁成为当前读current read。它们会和事务的隔离级别产生复杂的交互。具体怎么工作的我们后面再展开。要点回顾并发控制的两个思路是锁和多版本。三个阶段分别是普通锁串行→ 读写锁读读并行→ 多版本读写并行redo日志通过顺序写优化了持久化性能保护已提交事务undo日志为回滚提供了基础同时也是MVCC的数据源InnoDB依靠存储在回滚段中的旧版本数据实现了快照读这种不加锁的一致性读普通select就是快照读这是InnoDB高并发的核心原因
美团面试:MySQL为什么能够在大数据量、高并发的业务中稳定运行?
前言MySQL是互联网公司用得最多的数据库而InnoDB则是MySQL生态中最常见的存储引擎。它为什么能够在大数据量、高并发的互联网业务中稳定运行今天我们来聊聊InnoDB的并发控制、锁机制和MVCC——从基础概念到内核设计的完整逻辑。这篇文章稍长一些建议收藏后慢慢品读。并发控制为什么数据库需要它想象一个场景多个请求同时对同一条数据进行操作。如果没有任何保护措施结果会是混乱的——某个线程还在修改数据另一个线程已经开始读取最后导致数据不一致。这就是为什么数据库必须有并发控制机制。从技术角度看保证数据一致性的方法通常有两类一是用锁来阻止冲突操作二是用数据多版本来让不同操作并行进行。从普通锁到读写锁的进化最朴素的想法就是用一把锁把临界区锁死操作数据前加锁操作完后释放。但这样做太粗暴了——即使只是读取数据也要等待写操作完成所有操作本质上变成了串行。这显然不够。后来人们想到了共享锁和排他锁的区分读取数据时加共享锁S锁多个读操作可以同时进行修改数据时加排他锁X锁谁都得等这样就实现了读读并行的目标。但问题依然存在一旦某个写操作开始执行所有读操作就必须阻塞。对应到数据库层面就是写事务未提交时相关数据的select会被挡住。能不能进一步突破这个瓶颈呢最近无意间获得一份阿里大佬写的刷题笔记一下子打通了我的任督二脉进大厂原来没那么难。 这是大佬写的 7701页的BAT大佬写的刷题笔记让我offer拿到手软数据多版本读写真正并行的钥匙这里引出了一个优雅的idea——数据多版本。核心思想很简单写操作不要直接修改原数据而是复制一份新版本来修改。这样并发的读操作仍然可以读取旧版本的数据。写操作也不会被读阻塞。想象这样一个过程T1时刻某个写操作开始它克隆了一份数据版本V1开始修改。T2时刻一个读操作到达它读取的仍是原版本V0的数据。T3时刻又来了一个读照样读V0。直到写操作提交新版本才会变成当前版本。这就是读写并行的秘诀。并发能力的演进就这样展开了普通锁做不到并行 → 读写锁实现读读并行 → 多版本技术实现读写并行。这个思路比具体的技术细节更重要。实现多版本的基础redo日志与undo日志在InnoDB真正如何利用多版本之前我们需要理解两样东西redo日志和undo日志。redo日志的两个使命事务提交后数据必须保证刷到磁盘。但每次都直接写磁盘太低效了——磁盘随机写是性能杀手。聪明的办法是先把修改操作写到redo日志这是顺序写快得多然后定期将数据刷到磁盘。这样既保证了ACID特性又大幅提高了吞吐量。如果数据库中途崩溃重启时可以重新执行redo日志里的操作确保所有已提交的事务都被持久化。简言之redo日志保护已提交事务。undo日志的角色undo日志做的事相反。当事务修改数据时修改前的旧版本被存入undo日志。如果事务需要回滚或数据库崩溃需要恢复这些旧版本数据就派上用场了。具体来说insert操作的undo记录新数据的主键delete和update的undo记录完整的旧数据行。回滚时直接用这些旧版本恢复就行。undo日志的真正妙用是为MVCC提供了旧版本数据的来源。回滚段undo的仓库存储undo日志的地方叫回滚段。我们用一个例子来看它如何工作。假设有个表 t(id PK, name)初始数据是体验AI代码助手代码解读复制代码1, xiaobei 2, zhangsan 3, lisi现在启动一个事务执行了几个操作但还未提交sql体验AI代码助手代码解读复制代码start trx; delete (1, xiaobei); update (3, lisi) to (3, xxx); insert (4, wangwu);此时回滚段中会出现这些记录delete之前的 (1, shenjian) 进入回滚段update之前的 (3, lisi) 进入回滚段insert的新PK 4 也进去了如果事务要回滚这些undo数据就会被用上被删的行恢复了被改的行恢复了新插入的行被删掉了。一切回到原点。InnoDB的MVCC多版本并发控制的真面目InnoDB之所以能在互联网的高并发场景中表现出色根本原因就是MVCC多版本并发控制。它通过让事务读取旧版本数据从而大幅降低锁冲突。InnoDB内核给每一行数据都加了三个隐藏属性DB_TRX_ID6字节最后修改这行数据的事务IDDB_ROLL_PTR7字节指向回滚段中undo日志的指针DB_ROW_ID6字节单调递增的行号这样设计看似简单实际上威力巨大。回滚段里的数据是历史快照永不修改。因此select语句可以放心地去读取它们完全不需要加锁。这种不加锁的一致性读就叫快照读。它是InnoDB并发高的核心秘密。所谓一致性是指事务读到的数据要么是事务开始前就存在的来自其他已提交事务要么是事务自己插入或修改的。什么是快照读除非你显式加锁否则普通的select都是快照读sql体验AI代码助手代码解读复制代码select * from t where id 2;显式加锁的读就不同了sql体验AI代码助手代码解读复制代码select * from t where id 2 lock in share mode; select * from t where id 2 for update;这两种会加上共享锁或排他锁成为当前读current read。它们会和事务的隔离级别产生复杂的交互。具体怎么工作的我们后面再展开。要点回顾并发控制的两个思路是锁和多版本。三个阶段分别是普通锁串行→ 读写锁读读并行→ 多版本读写并行redo日志通过顺序写优化了持久化性能保护已提交事务undo日志为回滚提供了基础同时也是MVCC的数据源InnoDB依靠存储在回滚段中的旧版本数据实现了快照读这种不加锁的一致性读普通select就是快照读这是InnoDB高并发的核心原因