MySql 基础
更新: Invalid Date 字数: 0 字 时长: 0 分钟
MySql 基础
MySQL 常见存储引擎有哪些?
MySQL 常见的存储引擎有 InnoDB、MyISAM、MEMORY,常用的是 InnoDB 和 MyISAM。
MySQL 5.0 之后默认是用 InnoDB。
MySQL 中 InnoDB 和 MyISAM 的区别?
InnoDB:
- 支持外键
- 支持行锁
- 支持事务机制
- 支持 MVCC 机制
- 支持崩溃恢复(Redo Log / Undo Log)
MyISAM:
- 只支持表级锁
- 不支持事务
- 不支持外键
为什么 InnoDB 支持事务?
InnoDB 之所以支持事务,是因为它通过 Redo Log、Undo Log、MVCC 和锁机制 共同实现了事务的 ACID 特性。
Redo Log 保证事务的持久性
Undo Log 用于事务回滚并支持 MVCC
MVCC 实现多版本并发控制,提高读写并发能力
锁机制 保证事务隔离性
其中 MVCC 通过在每行数据中维护隐藏字段(trx_id、roll_ptr)以及 Undo Log 形成多版本链,从而实现一致性读。
Redo Log、Undo Log、Binlog 三者到底有什么区别?
Redo Log 和 Undo Log 属于 InnoDB 存储引擎层。在 MySQL 的 InnoDB 存储引擎中,Redo Log 和 Undo Log 是实现事务机制的重要日志。
Redo Log(重做日志)主要用于保证事务的持久性(Durability):InnoDB 采用 WAL(Write-Ahead Logging)机制,当数据被修改时,会先将修改记录写入 redo log,然后再将数据页写入磁盘。由于 redo log 是顺序写磁盘,因此效率很高。如果数据库发生崩溃,可以通过 redo log 重新执行已经提交但尚未写入磁盘的数据修改,从而实现崩溃恢复。
Undo Log(回滚日志)主要用于事务回滚和 MVCC:当事务修改数据时,InnoDB 会先记录数据修改前的旧值到 undo log 中。如果事务需要回滚,就可以通过 undo log 恢复到修改之前的状态。另外,Undo Log 还用于实现 MVCC,多版本并发控制。每一行记录都会通过 roll_pointer 指向对应的 undo log,从而形成版本链,使得不同事务可以读取到不同版本的数据。
Binlog 属于 MySQL Server 层。Binlog 主要用于 MySQL 层的数据复制和恢复。
DISTINCT 的原理是什么?
DISTINCT 的作用是对查询结果进行去重,MySQL 在执行 DISTINCT 时通常有三种实现方式。
排序去重。MySQL 会先对 DISTINCT 字段进行排序,使相同的值排列在一起,然后只保留每组中的第一条记录。这种方式在执行计划中通常会出现 Using filesort 和 Using temporary。临时表 + 唯一索引去重。MySQL 会创建一个临时表,并在 DISTINCT 字段上建立唯一索引,当数据插入临时表时,如果遇到重复值就会因为唯一索引冲突而被丢弃,从而实现去重。利用索引直接去重。如果 DISTINCT 的字段上存在合适的索引,例如 B+Tree 索引,那么 MySQL 可以直接扫描有序索引,并跳过重复值,这种方式性能最好,并且执行计划中通常会显示 Using index。
此外,DISTINCT 在某些情况下可以被优化成 GROUP BY,因此它们的执行策略有很多相似之处。实际性能主要取决于是否能够利用索引,以及是否需要临时表和排序操作。
LIMIT 的常见使用场景?
分页查询(最常见):这是最常见的场景,例如 LIMIT offset,size 用于实现列表分页。
sqlSELECT * FROM user ORDER BY id LIMIT 0,10;TopN 查询:例如查询销量最高的前 10 个商品,通常配合 ORDER BY 使用。
只取一条数据:避免扫描多条,提高查询效率
分批删除或更新数据:用于避免一次性操作大量数据导致长事务或锁表。
sqlDELETE FROM log LIMIT 1000;批量处理任务:例如任务系统中每次只拉取 100 条任务进行处理。
LIMIT 深分页问题?
一般认为:offset > 10000,就属于深分页。
原因: MySQL 不能直接跳到第 xxxx 条。 执行流程是:
1 扫描数据
2 排序(如果有 ORDER BY)
3 读取 1000010 条
4 丢弃前 1000000 条
5 返回 10 条优化方案:
子查询 + 覆盖索引(经典优化),减少回表
先用索引找到分页起点:
sqlSELECT * FROM user WHERE id >= ( SELECT id FROM user ORDER BY id LIMIT 1000000,1 ) LIMIT 10;执行逻辑:
text子查询: 只扫描索引找到第1000000个id 主查询: 从该id开始查10条ID 游标分页(最推荐)
只查索引再回表,子查询只扫描索引。不如游标分页稳定
sqlSELECT * FROM user WHERE id IN ( SELECT id FROM user ORDER BY id LIMIT 1000000,10 );
ORDER BY 是如何排序的?
MySQL 排序只有两种路径:
- 利用索引排序(最优)
- 索引排序的条件:索引顺序一致;排序方向一致;不包含函数/表达式;WHERE + ORDER BY 联合使用
- filesort(额外排序):一次读取,内存消耗大
单路排序(Single-Pass)
text1 读取所有字段 2 放入 sort_buffer 3 排序 4 直接返回双路排序(Two-Pass,旧版本常见):内存占用小,需要回表(随机 IO)
text1 读取排序字段 + 主键 2 排序 3 根据主键回表取数据
总结
MySQL 中 ORDER BY 的排序主要有两种实现方式:一种是利用索引排序,另一种是通过 filesort 进行排序。
第一种是 索引排序。如果 ORDER BY 的字段能够命中索引,并且满足最左前缀原则、排序方向一致等条件,MySQL 可以直接按照 B+Tree 索引的顺序读取数据,这种方式性能最好,并且执行计划中通常会显示 Using index。
第二种是 filesort 排序。当无法利用索引时,MySQL 会使用 filesort 算法进行排序。filesort 并不一定是磁盘排序,而是 MySQL 内部的一种排序实现。它主要有两种方式:单路排序和双路排序。单路排序会将所有字段加载到 sort_buffer 中排序,内存消耗较大;双路排序只排序排序字段和主键,排序后再回表取数据,减少内存使用但会增加随机 IO。
如果 sort_buffer 不够,MySQL 还会将数据写入磁盘进行归并排序。此外,在 ORDER BY + LIMIT 场景下,MySQL 会做优化,只维护 TopN 数据,而不进行全量排序。
因此在实际开发中,优化 ORDER BY 的核心是尽量让排序走索引,避免出现 Using filesort 和 Using temporary。
UNION 和 UNION ALL 的区别是什么?
UNION 和 UNION ALL 的核心区别在于是否去重。UNION 会对多个查询结果进行合并后去重,而 UNION ALL 只是简单地将结果集进行拼接,不做去重处理。
在执行层面上,UNION ALL 的实现比较简单,只是将多个结果集顺序追加返回,因此性能较高。而 UNION 在合并结果后还需要进行去重操作,MySQL 通常会通过排序或者临时表加唯一索引的方式来实现去重,这可能会涉及 filesort 或临时表,从而带来额外的性能开销。
因此在实际开发中,如果可以确定结果集不存在重复数据,应该优先使用 UNION ALL,以获得更好的性能;只有在必须去重的场景下才使用 UNION。
INNER JOIN、LEFT JOIN、RIGHT JOIN 的区别是什么?
INNER JOIN、LEFT JOIN 和 RIGHT JOIN 的核心区别在于返回结果的范围不同。
INNER JOIN 只返回两张表中满足连接条件的记录,也就是交集部分;
LEFT JOIN 会返回左表的全部记录,同时返回右表中匹配的数据,如果右表没有匹配则用 NULL 填充;
RIGHT JOIN 则相反,会返回右表的全部记录,左表没有匹配的部分用 NULL 填充。
在执行层面上,MySQL 通常使用嵌套循环连接来实现 JOIN,即以一张表作为驱动表,逐行去另一张表中查找匹配数据。如果没有匹配,INNER JOIN 会丢弃该行,而 LEFT JOIN 或 RIGHT JOIN 会补 NULL。
另外一个常见的陷阱是 WHERE 条件会影响 LEFT JOIN 的结果,例如在 LEFT JOIN 后对右表字段加 WHERE 条件,会把 NULL 过滤掉,从而导致 LEFT JOIN 退化成 INNER JOIN。因此在实际开发中,通常建议将过滤条件写在 ON 子句中。
在使用上,一般推荐使用 LEFT JOIN 替代 RIGHT JOIN,并确保连接字段上有索引,以提高查询性能。
索引
对索引的理解?
索引是数据库中用于提高查询效率的一种数据结构,可以理解为数据的目录。通过索引可以快速定位数据,而不需要进行全表扫描。
在 MySQL 的 InnoDB 存储引擎中,索引默认采用 B+Tree 结构。B+Tree 是一种多路平衡搜索树,具有高度低、查询效率稳定的特点,通常可以在 O(log n) 的时间复杂度内完成查找,并且能够有效减少磁盘 IO。
索引的主要优点是可以显著提高查询速度,并支持范围查询、排序和分组操作。但它也有一定的代价,比如会占用额外的存储空间,并且在插入、更新和删除数据时需要维护索引,从而影响写性能。
此外,在 InnoDB 中主键索引是聚簇索引,数据和索引存储在一起,而普通索引的叶子节点存储的是主键值,查询时可能需要回表。因此在实际开发中,需要根据查询场景合理设计索引,以在查询性能和写入性能之间做权衡。
MySQL 支持哪些索引?
MySQL 支持多种索引类型,可以从功能、结构和存储方式三个维度来理解。
从功能上看,主要包括主键索引、唯一索引、普通索引和联合索引。其中主键索引是唯一且不能为空的,在 InnoDB 中对应聚簇索引;唯一索引用于保证字段唯一性;普通索引是最基本的索引;联合索引则是多个字段组合形成的索引,遵循最左前缀原则。
从数据结构上看,MySQL 默认使用的是B+Tree 索引,此外还支持 Hash 索引(主要用于 Memory 引擎)、全文索引(FullText,用于文本搜索)以及空间索引(Spatial,用于地理数据)。
从存储方式上看,在 InnoDB 中索引分为聚簇索引和非聚簇索引。聚簇索引就是主键索引,数据和索引存储在一起;非聚簇索引的叶子节点存储的是主键值,查询时通常需要回表。
在实际开发中,需要根据查询场景合理选择索引类型,以在查询性能和写入性能之间取得平衡。
什么是覆盖索引?
覆盖索引是指查询所需要的所有字段都可以从索引中直接获取,而不需要再回表查询数据的一种优化方式。也就是说,查询结果被索引“覆盖”了。
在 InnoDB 中,普通二级索引的叶子节点存储的是主键值,因此在查询时通常需要先通过索引找到主键,再根据主键去主键索引中查找完整数据,这个过程称为回表。而如果查询的字段都包含在索引中,就可以直接从索引中返回结果,从而避免回表。
覆盖索引的优势在于减少了一次 B+Tree 查找和磁盘 IO,因此可以显著提升查询性能。在执行计划中,通常会看到Using index,表示使用了覆盖索引。
在实际开发中,可以通过合理设计联合索引,使查询字段尽量被索引覆盖,从而提高查询效率,但也需要注意控制索引大小,避免带来额外的存储和维护成本。
什么是最左前缀原则?
最左前缀原则是指在使用联合索引时,查询条件必须从索引的最左列开始,并且中间不能跳过列,否则索引将无法被完全利用。
例如对于联合索引 (a, b, c),可以支持 (a)、(a,b)、(a,b,c) 这样的查询,但不能直接使用 (b) 或 (c),因为联合索引在底层 B+Tree 中是先按 a 排序,再按 b、c 排序的,如果缺少最左列,就无法快速定位数据范围。
此外,如果查询中出现范围条件,例如 b > 2,那么后面的列 c 将无法继续使用索引,因为范围查询会中断索引的连续性。
因此在实际开发中,设计联合索引时应将查询中最常用、区分度最高的字段放在最左边,以提高索引的利用率。
什么是回表查询?
回表查询是指在使用二级索引进行查询时,先通过索引找到对应的主键值,然后再根据主键到聚簇索引中查找完整数据的过程。
在 InnoDB 存储引擎中,主键索引是聚簇索引,其叶子节点存储的是整行数据,而普通索引的叶子节点只存储主键值。因此,当查询的字段不在索引中时,就需要先通过二级索引定位到主键,再通过主键索引获取完整数据,这个过程就叫回表。
回表查询的代价在于需要进行两次 B+Tree 查找,并且可能产生额外的随机 IO,因此会影响查询性能。
在实际开发中,可以通过设计覆盖索引,使查询字段尽量包含在索引中,从而避免回表,提高查询效率。
什么是索引下推?
索引下推(Index Condition Pushdown,ICP)是 MySQL 的一种查询优化技术,它会将部分 WHERE 条件下推到存储引擎层,在索引遍历过程中就进行过滤,从而减少回表次数。
在没有索引下推的情况下,MySQL 会先通过索引找到满足条件的数据,然后回表到主键索引获取完整记录,再在 Server 层进行条件过滤。而启用索引下推后,存储引擎在遍历索引时就会判断部分条件,只有满足条件的数据才会回表,从而显著减少回表带来的随机 IO 开销。
索引下推主要适用于二级索引场景,并且只能对索引中包含的字段进行下推。通过 EXPLAIN 可以看到 “Using index condition” 来判断是否启用了该优化。
本质上,索引下推的核心价值在于“减少回表次数”,从而提升查询性能。
索引失效
哪些情况下索引会失效?
未遵循最左前缀原则,无法从有序结构的起点开始查找
使用函数或表达式,对索引列做了计算 → 索引值被破坏
隐式类型转换
LIKE 以 % 开头,无法确定起始位置
使用 OR(部分情况):
sqlWHERE a = 1 OR b = 2如果 a、b 没有同时命中索引,会放弃索引,全表扫描
范围查询中断索引(联合索引)
使用 != 或 NOT IN
"SELECT *" 导致回表(不是完全失效,但性能差)
数据分布问题,如果:数据分布极不均匀,优化器可能:直接走全表扫描
B+Tree
为什么 MySQL 使用 B+Tree?
MySQL 选择 B+Tree 作为索引结构,主要是因为它在磁盘 IO 场景下具有非常优秀的性能。
首先,相比二叉树或红黑树,B+Tree 的每个节点可以存储更多的索引键值,因此树的高度更低,通常只需要 2 到 4 层,这样可以显著减少磁盘 IO 次数。
其次,B+Tree 的非叶子节点只存储索引,不存储数据,这进一步提高了单个节点的存储能力,使查询更加高效。同时所有数据都存储在叶子节点上,使得每次查询路径一致,性能更加稳定。
此外,B+Tree 的叶子节点之间通过链表连接,天然支持范围查询和顺序扫描,这对于数据库中常见的 BETWEEN、ORDER BY 等操作非常重要。
相比之下,Hash 索引虽然在等值查询上性能很好,但不支持范围查询和排序,因此不适合作为通用索引结构。
因此,综合考虑 IO 性能、范围查询和排序能力,MySQL 最终选择了 B+Tree 作为索引的底层实现。
B+Tree 和 BTree 的区别是什么?
B-Tree 和 B+Tree 的主要区别在于数据存储方式和查询结构。
在 B-Tree 中,所有节点(包括非叶子节点)既存储索引也存储数据,因此查询可以在任意节点结束。而在 B+Tree 中,非叶子节点只存储索引,所有数据都存储在叶子节点上,并且叶子节点之间通过链表连接。
这种结构带来了几个重要优势:首先,由于非叶子节点不存数据,B+Tree 每个节点可以存储更多的索引键,从而降低树的高度,减少磁盘 IO 次数;其次,所有查询都必须走到叶子节点,使查询路径更加稳定;最后,叶子节点的链表结构使得范围查询和顺序扫描非常高效。
因此,相比 B-Tree,B+Tree 更适合数据库这种以磁盘 IO 和范围查询为主的场景,这也是 MySQL 选择 B+Tree 作为索引结构的原因。
B+Tree 和 Hash 索引有什么区别?
B+Tree 和 Hash 索引的核心区别在于数据结构和适用场景不同。
B+Tree 是一种有序的多叉平衡树结构,所有数据存储在叶子节点,并且叶子节点之间通过链表连接,因此它不仅支持等值查询,还支持范围查询、排序以及最左前缀匹配,非常适合数据库这种需要频繁进行范围扫描和排序的场景。
而 Hash 索引是基于哈希函数实现的,通过对 key 进行 hash 计算直接定位数据位置,在等值查询时性能非常高,理论上可以达到 O(1)。但是由于 Hash 是无序结构,它不支持范围查询、排序,也无法利用最左前缀规则,同时还存在哈希冲突问题。
此外,从存储角度来看,B+Tree 更适合磁盘存储,因为它访问具有局部性,而 Hash 索引通常是随机访问,不利于磁盘 IO。
因此,在数据库中,尤其是 MySQL 的 InnoDB 存储引擎中,默认使用 B+Tree 作为索引结构,而 Hash 索引只在特定场景(如 Memory 引擎或自适应哈希索引)中使用。
B+Tree 的高度一般是多少?
在 MySQL 的 InnoDB 存储引擎中,B+Tree 的高度通常是非常低的,一般在 2 到 4 层之间,极少超过 5 层。
这是因为 InnoDB 的数据页大小默认是 16KB,而 B+Tree 的非叶子节点只存储索引键和指针,因此一个节点可以存储成百上千个键值,从而使得树的扇出非常大。
在这种情况下,树的高度增长非常缓慢,例如三层 B+Tree 理论上就可以存储上亿级别的数据,因此查询时通常只需要 2 到 4 次磁盘 IO,就可以完成一次查找。
这也是 MySQL 能够在大数据量场景下依然保持高查询性能的重要原因之一。
MySQL 事务
什么是事务?
事务是数据库中一组逻辑操作单元,这组操作要么全部成功执行,要么全部失败回滚,是数据库保证数据一致性的重要机制。
在实际场景中,比如转账操作,必须保证扣款和加款要么同时成功,要么同时失败,这正是通过事务来实现的。
事务的四大特性(ACID)是什么?
事务具有 ACID 四大特性:
原子性指事务是不可分割的最小执行单元,要么全部执行成功,要么全部回滚;原子性是通过undo log实现的,undo log 记录了数据修改前的状态,当事务回滚时可以利用它恢复数据;一致性指事务执行前后,数据必须从一个一致状态转变到另一个一致状态;一致性是通过原子性、隔离性和持久性共同保证的。隔离性指多个事务之间互不干扰;隔离性则是通过锁机制和MVCC实现的,锁用于控制写操作之间的并发,而 MVCC 通过多版本机制实现读写不阻塞,提高并发性能;持久性指事务一旦提交,其结果会被永久保存,即使系统发生故障也不会丢失。持久性是通过redo log实现的,MySQL 采用 WAL(Write-Ahead Logging)机制,事务提交时先写 redo log,即使系统崩溃也可以通过日志恢复数据;
MySQL 如何开启事务?
MySQL 开启事务主要有三种方式。
- 第一种是通过显式语句 BEGIN 或 START TRANSACTION 来开启事务,这是最常用也是最推荐的方式;
- 第二种是将 autocommit 设置为 0,即关闭自动提交,这样每条 SQL 都会在事务中执行,需要手动执行 COMMIT 或 ROLLBACK;
- 第三种是隐式事务,当执行 INSERT、UPDATE、DELETE 等 DML 语句时,如果当前不是自动提交模式,MySQL 会自动开启事务。
此外需要注意,MySQL 默认是 autocommit=1,也就是每条 SQL 默认是一个独立事务;同时像 DDL 语句(如 CREATE、ALTER)会隐式提交事务,这是实际开发中的一个常见坑点。
事务的四种隔离级别是什么?
事务的隔离级别主要用于解决并发访问时的数据一致性问题,一共有四种:READ UNCOMMITTED(读未提交)、READ COMMITTED(读已提交)、REPEATABLE READ(可重复读) 和 SERIALIZABLE(序列化)。
READ UNCOMMITTED 是最低级别,允许读取未提交的数据,会产生脏读、不可重复读和幻读;
READ COMMITTED 只能读取已提交数据,解决了脏读问题,但仍然存在不可重复读和幻读;
REPEATABLE READ 保证同一事务中多次读取结果一致,解决了脏读和不可重复读问题,在 MySQL 的 InnoDB 引擎中通过 MVCC 和间隙锁机制也解决了幻读问题,这也是 MySQL 的默认隔离级别;
SERIALIZABLE 是最高级别,通过强制事务串行执行,解决所有并发问题,但性能最差。
总体来说,隔离级别越高,一致性越好,但并发性能越低,因此实际使用中需要在一致性和性能之间做权衡。
MySQL 默认隔离级别是什么?为什么?
MySQL 默认的事务隔离级别是 REPEATABLE READ(可重复读)。
之所以选择这个级别,是因为它在一致性和并发性能之间取得了较好的平衡。REPEATABLE READ 能够解决脏读和不可重复读问题,在标准 SQL 中虽然仍然存在幻读,但 MySQL 的 InnoDB 存储引擎通过 MVCC 和间隙锁机制,对幻读进行了优化和避免,使其在大多数场景下也能保证较高的一致性。
同时,在该隔离级别下,MySQL 通过 MVCC 实现了读写不阻塞,提高了并发性能。因此相比 READ COMMITTED 更稳定一致,相比 SERIALIZABLE 性能更高,这也是 MySQL 默认选择它的主要原因。
MVCC(多版本并发控制机制)
什么是 MVCC?
MVCC(多版本并发控制)是一种用于提升数据库并发性能的机制,其核心思想是通过维护数据的多个版本,实现读写不加锁,从而避免读写冲突。
在 MySQL 的 InnoDB 存储引擎中,MVCC 的实现依赖于 undo log、隐藏字段和 ReadView。每一行数据都会有一个版本链,通过 undo log 记录历史版本;同时每行数据包含事务 ID 和回滚指针等隐藏字段,用于定位版本;在读取数据时,系统会生成一个 ReadView,根据事务 ID 判断当前版本是否可见,如果不可见则沿着版本链查找历史版本。
MVCC 主要用于实现一致性读,可以解决不可重复读问题,并显著提高并发性能。但对于当前读(如 UPDATE、SELECT FOR UPDATE),仍然需要依赖锁机制(如间隙锁)来解决幻读问题。
因此,MVCC 本质上是通过“多版本 + 可见性判断”实现高并发的一种机制。
MVCC 的实现原理是什么?MVCC 解决了什么问题?
MVCC 主要解决的是数据库中的读写冲突问题,在保证数据一致性的前提下提升系统的并发性能。
在没有 MVCC 的情况下,读写操作通常需要加锁,会导致读写互相阻塞,从而降低系统吞吐量。而 MVCC 通过为数据维护多个版本,使读操作可以读取历史快照版本,写操作生成新版本,从而实现读写不阻塞。
在 MySQL 的 REPEATABLE READ 隔离级别下,MVCC 还能保证同一事务中多次读取数据结果一致,从而解决不可重复读问题。同时,对于普通的快照读场景,也可以避免幻读。
总体来说,MVCC 是通过“多版本 + 可见性判断”的方式,用空间换时间,在保证一致性的同时显著提升数据库并发性能。
ReadView 是什么?
ReadView 是 MySQL InnoDB 存储引擎中 MVCC 的核心组件,用于判断某个数据版本对当前事务是否可见。本质上它是一种可见性规则集合。
ReadView 中主要包含当前系统中活跃事务的 ID 列表、最小事务 ID、最大事务 ID 以及创建该 ReadView 的事务 ID。在进行查询时,系统会根据数据版本中的事务 ID,与 ReadView 中的信息进行对比,从而判断该版本是否对当前事务可见。如果不可见,则会通过 undo log 查找历史版本,直到找到可见的数据。
在不同隔离级别下,ReadView 的生成时机不同。在 READ COMMITTED 下,每次查询都会生成新的 ReadView;而在 REPEATABLE READ 下,只在第一次查询时生成 ReadView,后续复用,从而保证同一事务中多次读取结果一致。
undo log 在 MVCC 中的作用是什么?
在 MySQL 的 InnoDB 存储引擎中,undo log 在 MVCC 中主要用于提供历史版本数据,并构建版本链。
每当数据发生更新时,undo log 会记录修改前的旧值,同时通过回滚指针将多个版本串联起来,形成一个版本链。在进行查询时,系统会先获取当前数据版本,再结合 ReadView 判断该版本是否对当前事务可见,如果不可见,则会沿着 undo log 逐步回溯,查找历史版本,直到找到可见的数据。
因此,undo log 为 MVCC 提供了多版本数据的基础,使得读操作可以读取历史快照,从而实现读写不阻塞,提高数据库的并发性能。同时需要注意,undo log 的本质是用于事务回滚,在 MVCC 中是被复用来实现多版本机制的。
MVCC 在什么隔离级别下生效?
MVCC 主要在 READ COMMITTED 和 REPEATABLE READ 两个隔离级别下生效。在这两个级别中,普通的 SELECT 查询属于快照读,会通过 MVCC 读取数据的历史版本,从而实现读写不阻塞。
其中,在 READ COMMITTED 隔离级别下,每次查询都会生成新的 ReadView,因此可能出现不可重复读;而在 REPEATABLE READ 隔离级别下,事务只在第一次查询时生成 ReadView,后续复用,从而保证多次读取结果一致。
而在 READ UNCOMMITTED 下,由于允许读取未提交数据,一般不会使用 MVCC;在 SERIALIZABLE 隔离级别下,所有读操作都会加锁,退化为基于锁的并发控制机制,也不会使用 MVCC。
需要注意的是,即使在支持 MVCC 的隔离级别下,对于 UPDATE、DELETE 或 SELECT FOR UPDATE 这类当前读操作,仍然是通过加锁来实现的,而不是通过 MVCC。
