当前位置: 首页 > news >正文

【连载3】MySQL 的 MVCC 机制剖析

目录

  • 什么是 MVCC?
  • 数据版本的管理
  • 读操作流程
  • 写操作流程
  • C# 操作 MySQL 示例代码
  • MVCC 常见的坑
    • 1.对快照读的误解
    • 2.undo log 膨胀问题
    • 3.幻读问题
    • 4.对事务隔离级别的误用
    • 5.忽略当前读的锁机制
  • 互动

什么是 MVCC?

MVCC(Multi-Version Concurrency Control,多版本并发控制)是 MySQL 中 InnoDB 存储引擎实现隔离级别的基础机制,它通过保存数据的多个版本,实现了读写不冲突,从而提高了数据库的并发性能。
与传统的锁机制不同,MVCC 允许读操作不加锁,读操作不会阻塞写操作,写操作也不会阻塞读操作,这使得数据库在高并发场景下依然能保持良好的性能。

数据版本的管理

InnoDB 存储引擎为每行数据添加了两个隐藏列:
DB_TRX_ID:记录最后一次修改该数据的事务 ID
DB_ROLL_PTR:指向该数据的 undo log(回滚日志)记录
当事务修改数据时,InnoDB 不会直接覆盖旧数据,而是创建一个新的数据版本,并将旧版本的数据保留在 undo log 中。通过 undo log 链,我们可以追溯到数据的各个历史版本。

读操作流程

MVCC 实现了两种读操作:
**1.快照读(Snapshot Read):**读取的是数据的快照版本,不加锁,普通的 SELECT 语句就是快照读
2.当前读(Current Read): 读取的是数据的最新版本,需要加锁,如 SELECT … FOR UPDATE、SELECT … LOCK IN SHARE MODE

快照读通过事务的 Read View(读视图)来确定可见的数据版本。Read View 包含了当前活跃事务的 ID 列表,通过比较数据版本的 DB_TRX_ID 与 Read View 中的事务 ID,来判断该版本是否可见。

写操作流程

当事务修改数据时,InnoDB 会:

  • 为该事务分配一个唯一的事务 ID(TRX_ID)
  • 创建数据的新版本,并将新版本的 DB_TRX_ID 设置为当前事务 ID
  • 将旧版本的数据写入 undo log,并更新新版本的 DB_ROLL_PTR 指向该 undo log 记录
  • 事务提交后,该版本成为最新版本

C# 操作 MySQL 示例代码

下面是一个 C# 操作 MySQL 的示例,展示了在并发场景下 MVCC 的效果:

using MySqlConnector;
using System;
using System.Threading.Tasks;class MvccExample
{private static string connectionString = "server=localhost;database=test;user=root;password=your_password;";private const int MaxRetryCount = 3;  // 最大重试次数private const int RetryDelayMs = 1000;  // 重试延迟时间static async Task Main(string[] args){// 初始化测试数据await InitializeTestData();// 启动两个并发任务模拟多版本并发控制var task1 = Task.Run(Transaction1);var task2 = Task.Run(Transaction2WithRetry);await Task.WhenAll(task1, task2);// 查看最终结果await ShowFinalResult();}static async Task InitializeTestData(){using (var connection = new MySqlConnection(connectionString)){await connection.OpenAsync();// 创建测试表using (var command = new MySqlCommand("CREATE TABLE IF NOT EXISTS products (" +"id INT PRIMARY KEY AUTO_INCREMENT, " +"name VARCHAR(50) NOT NULL, " +"price DECIMAL(10,2) NOT NULL)", connection)){await command.ExecuteNonQueryAsync();}// 清空表并插入测试数据using (var command = new MySqlCommand("TRUNCATE TABLE products; " +"INSERT INTO products (name, price) VALUES ('测试商品', 100.00)", connection)){await command.ExecuteNonQueryAsync();}}}static async Task Transaction1(){using (var connection = new MySqlConnection(connectionString)){await connection.OpenAsync();using (var transaction = await connection.BeginTransactionAsync()){try{Console.WriteLine("事务1: 开始");// 读取商品价格(快照读)using (var command = new MySqlCommand("SELECT price FROM products WHERE id = 1", connection, transaction)){var price = (decimal)await command.ExecuteScalarAsync();Console.WriteLine($"事务1: 读取到价格: {price}");}// 模拟处理时间,让另一个事务有机会执行await Task.Delay(2000);// 更新商品价格using (var command = new MySqlCommand("UPDATE products SET price = price * 1.1 WHERE id = 1", connection, transaction)){await command.ExecuteNonQueryAsync();Console.WriteLine("事务1: 价格提高10%");}// 再次读取价格(当前读)using (var command = new MySqlCommand("SELECT price FROM products WHERE id = 1 FOR UPDATE", connection, transaction)){var price = (decimal)await command.ExecuteScalarAsync();Console.WriteLine($"事务1: 更新后读取到价格: {price}");}await transaction.CommitAsync();Console.WriteLine("事务1: 提交成功");}catch (Exception ex){await transaction.RollbackAsync();Console.WriteLine($"事务1: 发生错误并回滚: {ex.Message}");}}}}static async Task Transaction2WithRetry(){int retryCount = 0;while (retryCount < MaxRetryCount){try{await Transaction2Implementation();return;}catch (MySqlException ex) when (ex.Number == 1205)  // 锁等待超时错误码{retryCount++;if (retryCount >= MaxRetryCount){Console.WriteLine($"事务2: 已达到最大重试次数({MaxRetryCount}),操作失败");return;}Console.WriteLine($"事务2: 锁等待超时,将在{RetryDelayMs}ms后重试(第{retryCount}次)");await Task.Delay(RetryDelayMs);}catch (Exception ex){Console.WriteLine($"事务2: 发生错误: {ex.Message}");return;}}}static async Task Transaction2Implementation(){using (var connection = new MySqlConnection(connectionString)){await connection.OpenAsync();// 设置事务超时时间为10秒using (var transaction = await connection.BeginTransactionAsync(System.Data.IsolationLevel.RepeatableRead)){try{Console.WriteLine("事务2: 开始");await Task.Delay(500);// 读取商品价格using (var command = new MySqlCommand("SELECT price FROM products WHERE id = 1", connection, transaction)){var price = (decimal)await command.ExecuteScalarAsync();Console.WriteLine($"事务2: 读取到价格: {price}");}// 更新商品价格using (var command = new MySqlCommand("UPDATE products SET price = price * 1.2 WHERE id = 1", connection, transaction)){await command.ExecuteNonQueryAsync();Console.WriteLine("事务2: 价格提高20%");}await transaction.CommitAsync();Console.WriteLine("事务2: 提交成功");}catch (Exception ex){await transaction.RollbackAsync();throw;  // 抛出异常让重试机制处理}}}}static async Task ShowFinalResult(){using (var connection = new MySqlConnection(connectionString)){await connection.OpenAsync();using (var command = new MySqlCommand("SELECT price FROM products WHERE id = 1", connection)){var price = (decimal)await command.ExecuteScalarAsync();Console.WriteLine($"最终价格: {price}");}}}
}

MVCC 常见的坑

1.对快照读的误解

很多开发者认为在同一个事务中,多次执行相同的 SELECT 语句会得到相同的结果,但实际上这只在 REPEATABLE READ 隔离级别下成立。如果使用 READ COMMITTED 隔离级别,每次查询都会获取新的快照,可能看到其他事务已提交的修改。

2.undo log 膨胀问题

MVCC 需要保存数据的多个版本,这会导致 undo log 不断增长。如果存在长事务,会阻止 undo log 的回收,可能导致磁盘空间耗尽。

3.幻读问题

在 REPEATABLE READ 隔离级别下,MVCC 可以解决不可重复读问题,但无法完全解决幻读问题。需要使用间隙锁(Gap Lock)来防止幻读。

4.对事务隔离级别的误用

很多开发者不了解不同隔离级别下 MVCC 的行为差异,错误地选择了隔离级别。例如,在需要严格一致性的场景下使用了 READ COMMITTED 级别。

5.忽略当前读的锁机制

使用 SELECT … FOR UPDATE 等当前读操作时,会加行锁,如果不注意可能导致死锁或长时间阻塞。

互动

MVCC 是 MySQL 中非常重要但也比较复杂的机制,理解它对于编写高效、正确的数据库操作代码至关重要。
你在使用 MySQL 时遇到过哪些与 MVCC 相关的问题?是如何解决的?欢迎在评论区分享你的经验和见解。如果对 MVCC 机制还有任何疑问,也可以提出来,我们一起讨论学习!

http://www.dtcms.com/a/430552.html

相关文章:

  • C++封装和继承特性
  • Linux(操作系统)文件系统--对打开文件的管理
  • 【Unity笔记】Unity XR 模式下 Point Light 不生效的原因与解决方法
  • 图片设计网站推荐wordpress下载的主题怎么安装
  • 分布式存储分片核心:从哈希取模到Redis哈希槽,从哈希类到非哈希类
  • C++ 操作 Redis
  • 旅游网站开发文献综述沈阳做网站大约要多少钱
  • 精美个人网站wordpress设置网站主题
  • PyCharm保姆级详细使用手册(Python新手快速上手篇)
  • 3.springboot-容器功能-@注解
  • python开发手机网站开发今天时政新闻热点是什么
  • 【网络编程】深入 HTTP:从报文交互到服务构建,洞悉核心机制
  • java面试0119-java中创建对象的方式?
  • 线程中互斥锁和读写锁相关区别应用示例
  • 网站开发logo绍兴网页设计
  • 2017主流网站风格win7 iis配置网站 视频教程
  • wordpress同步微信公众号seo外包是什么
  • 如何评价一个网站做的好不好展厅网站
  • wordpress站点克隆vip影视建设网站官网
  • 网站免费申请注册软件开发人员犯罪
  • 优秀个人网站设计模板互联网技术发展现状
  • 云南做网站价格网站的策划书
  • 做本地网站要服务器吗自动化毕设题目网站开发
  • 网站后端技术有哪些文学网站做编辑
  • 做淘客应该知道的网站咸阳学校网站建设费用
  • 适合女生做的网站投资公司网站设计
  • 专业网站维护如何免费建立自己的网页
  • 做社交网站的预算怎样查询网站空间
  • 网站重购出行南宁app软件下载
  • html怎么做成网站打开免费百度啊