数据库的MVCC如何理解?

news/2025/2/25 15:43:33

数据库的MVCC如何理解?

MVCC(多版本并发控制,Multi-Version Concurrency Control)是数据库系统中的一种并发控制机制,用于允许多个事务在不互相干扰的情况下并行执行,同时保持数据的一致性和隔离性。

MVCC的核心思想是,数据库中的每一行数据都有多个版本(version),每个事务访问的是它开始时的数据版本。这样可以解决传统锁机制中的冲突问题(例如,读-写冲突),同时提高并发性。

关键概念:

  1. 版本控制
    每次对数据的修改(如插入、更新、删除)都会产生一个新的数据版本,而原始版本依然存在,直到它不再被需要。每个数据版本通常会有一个时间戳或者事务ID标记,表明它是由哪个事务创建的,及其生命周期。

  2. 事务视图
    每个事务看到的都是某一时刻一致的数据快照。事务在执行时,并不会直接读取数据库中当前的数据,而是读取符合其开始时间之前提交的事务所做修改的数据。

  3. 快照隔离
    MVCC提供了类似于快照隔离(Snapshot Isolation)的效果。即事务读取到的始终是事务开始时的数据快照,不会受到其他并发事务的修改影响。

  4. 死锁和写冲突的避免
    由于MVCC允许并行读取不同版本的数据,事务在读取数据时不会加锁,避免了读取时阻塞。但是,如果一个事务需要修改数据,它必须确保该数据没有被其他事务修改,或者修改的数据版本符合某些条件。

操作过程:

  1. 读取:当事务读取数据时,它会看到在事务开始时存在的数据版本(即该事务的视图),而不会被其他并发事务的修改影响。

  2. 写入:事务修改数据时,数据库会创建该数据的新版本,而原始版本不会立刻被删除。这时,其他事务仍然可以读取到旧版本的数据,直到当前事务提交。

  3. 提交与回滚

    • 提交:事务提交时,新的数据版本会成为有效版本,其他事务可以看到并使用这个版本。
    • 回滚:如果事务回滚,数据库会将事务的修改从版本链中删除,不会产生可见影响。

MVCC的优势:

  • 高并发:通过避免锁机制,允许更多的并发事务执行,提高性能。
  • 提高事务隔离性:各事务可以看到不同的数据版本,从而减少了事务间的冲突。
  • 避免脏读和不可重复读:由于每个事务只看到一致的数据快照,能有效避免脏读(读到未提交事务的数据)和不可重复读(同一事务两次读取到不同的数据)问题。

常见的MVCC实现:

  • PostgreSQL:通过系统列(例如xminxmax)来标记每行数据的版本。
  • MySQL(InnoDB):通过隐藏的系统列来实现行级版本控制,并使用Undo Log记录事务之前的数据版本。
  • Oracle:通过撤销日志(Undo Logs)和回滚段来实现MVCC。

总的来说,MVCC通过给每个事务提供一个数据快照,并允许多个版本并存,优化了并发性能,同时保证了数据的一致性和隔离性。

MySQL数据库MVCC的应用过程

在 MySQL 中,特别是在 InnoDB 存储引擎中,MVCC 是通过系统版本管理和回滚日志来实现的。以下是一个简单的例子,演示了 MVCC 在 MySQL 中的应用过程。

1. 准备工作:

假设我们有一个名为 users 的表,内容如下:

CREATE TABLE users (
    id INT PRIMARY KEY,
    name VARCHAR(50),
    age INT
);

我们插入一条数据:

INSERT INTO users (id, name, age) VALUES (1, 'Alice', 30);

2. 事务A:读取数据(开始时的数据快照)

假设事务A在时间T1开启:

START TRANSACTION;

此时,users 表中有一行数据:(1, 'Alice', 30)

事务A执行读取操作:

SELECT * FROM users WHERE id = 1;

事务A看到的数据是事务A开始时的快照:(1, 'Alice', 30)

3. 事务B:修改数据(创建新版本)

在事务A执行读取操作后,事务B在时间T2开启,并对 users 表的数据进行更新:

START TRANSACTION;
UPDATE users SET age = 31 WHERE id = 1;

此时,事务B对数据 id = 1age 值进行更新,变成了 31。但是,这个修改操作不会影响事务A的读取,因为事务A仍然看到它开始时的数据版本。

在内部,InnoDB 创建了一个新的数据版本,标记该版本为由事务B创建。此时,数据库中会有两个版本的 id = 1 行数据:

  • 版本1:(1, 'Alice', 30),由事务A读取。
  • 版本2:(1, 'Alice', 31),由事务B修改。

4. 事务A:提交(无影响)

事务A继续执行并提交:

COMMIT;

虽然事务A提交了,但它看到的仍然是事务开始时的快照(版本1),因为事务B修改的数据对事务A不可见。

5. 事务B:提交(创建新版本)

事务B提交时,新的数据版本((1, 'Alice', 31))会被正式保存,并且对其他事务可见:

COMMIT;

此时,版本1的数据 age = 30 不再有效,版本2成为当前最新的数据。其他事务(如事务C)将看到版本2的数据。

6. 最终效果

  • 事务A:在提交之前,事务A看到的是版本1(age = 30)。
  • 事务B:修改并提交后,版本2(age = 31)成为数据库的最新版本。
  • 其他事务:如果另一个事务在事务B提交后执行,它将看到版本2的数据(age = 31)。

MVCC实现细节:

  1. Undo Log:在事务B修改数据时,InnoDB 会将原始数据(版本1)记录在 Undo Log 中。这样,事务A可以读取旧版本的数据,不受事务B修改的影响。

  2. 隐藏列:每行数据会有两个隐藏的列(在查询时不可见),例如:

    • xmin:表示哪个事务创建了这个版本。
    • xmax:表示哪个事务删除了这个版本。

    事务A在读取数据时,检查每一行数据的 xminxmax,确保读取的是在事务A开始时已经提交的版本。

总结:

  • 事务A 看到的是它开始时的数据快照,而不会受到 事务B 的更新影响,即使 事务B 已经修改了数据。
  • 事务B 在提交时创建了一个新的数据版本,其他事务在提交后会看到这个新的版本。
  • 事务A 在提交后无法看到 事务B 的修改,直到它自己开始一个新的事务并获得新的数据版本。

通过这样的方式,MVCC 让多个事务并行执行而不会相互干扰,提高了数据库的并发性,同时保持了数据的一致性和隔离性。

MVCC带来的缺点有哪些?如何解决?

尽管 MVCC(多版本并发控制)数据库并发性和事务隔离性方面具有明显优势,但也带来了一些潜在的缺点和挑战。以下是 MVCC 的几个常见缺点以及可能的解决方案:

1. 空间消耗(存储开销)

MVCC 通过保存多个数据版本来实现并发控制。每次数据发生变化时,都会创建一个新版本,这会导致数据库存储空间的增加,尤其是在高并发、高频繁更新的场景下。

解决方案:
  • 版本清理:大多数 MVCC 实现(如 MySQL InnoDB)使用了自动清理机制,例如垃圾回收(GC),定期删除那些不再有任何事务需要的旧版本数据。InnoDB 使用 purge 操作清理已提交事务的过时版本。
  • 定期维护:定期执行数据库优化操作,如 OPTIMIZE TABLE,以清理碎片和释放空间。
  • 合理的保留策略:通过配置合理的事务超时和垃圾回收策略,确保旧版本的数据被及时回收,减少存储压力。

2. 性能下降(GC 和清理延迟)

由于 MVCC 需要维护多个数据版本,数据库在执行查询、插入、更新时需要额外的检查和操作,尤其是清理过时版本时,可能会影响数据库的性能。在高并发的环境中,版本的管理和回收(如清理)也可能导致性能瓶颈。

解决方案:
  • 延迟清理:通过延迟清理机制,将垃圾回收操作放在低峰时段进行,减少对高并发事务的影响。
  • 优化回收算法:优化版本清理和垃圾回收的算法,例如使用 undo logs 或者通过增量清理技术,以减少性能开销。
  • 表分区:对于大表,可以采用分区表的方式,将数据分散存储和管理,避免单表的巨大开销。

3. “幻读”问题(Phantom Read)

MVCC 通过提供不同事务的数据快照来解决脏读、不可重复读的问题,但它不能完全解决 幻读(Phantom Read) 问题。幻读是指在一个事务中进行多次查询时,查询结果发生变化。例如,一个事务查询了某个范围的数据,但在该事务期间,其他事务插入了新数据,导致查询的结果在同一事务中发生变化。

解决方案:
  • 加强隔离级别:使用 Serializable(可串行化) 隔离级别来避免幻读。在这个隔离级别下,数据库会锁住读到的数据范围,防止其他事务在该范围内插入或修改数据。
  • 基于锁的控制:结合 锁机制(如行锁、表锁等)来避免幻读。在某些数据库系统中,您可以使用 SELECT FOR UPDATE 来锁定查询结果集中的行,防止其他事务对这些行进行修改。
  • 使用查询范围控制:在设计应用程序时,尽量避免需要跨越多个查询的数据修改。通过在单次操作中完成数据变更,可以避免部分幻读的场景。

4. 长事务的影响

MVCC 使得每个事务都有自己独立的数据快照,但这也意味着长事务(运行时间较长的事务)会导致数据库的资源被占用更长时间。特别是在高并发环境下,长事务会导致更多的旧数据版本被保留,增加存储压力,并可能阻塞其他事务。

解决方案:
  • 事务控制:避免长时间运行的事务,尽量将事务操作分解为多个短小事务。
  • 事务超时设置:为事务设置合理的超时机制,避免事务运行时间过长。
  • 定期检查和优化:监控事务的执行时间,定期优化慢查询和数据库的执行计划,确保事务能尽快完成。

5. 死锁问题

尽管 MVCC 在一定程度上减少了锁争用,但它仍然不能完全避免死锁的发生,尤其是在事务需要多个版本的数据时,可能会引发死锁。

解决方案:
  • 死锁检测:许多数据库系统(如 InnoDB)内置了死锁检测机制,会自动检测并处理死锁。当检测到死锁时,数据库会回滚某个事务以解除死锁。
  • 合理的事务顺序:避免多个事务同时请求相同的数据项,采用一种统一的事务执行顺序(如锁顺序规则)来减少死锁的可能性。
  • 行级锁优化:通过尽量使用行级锁,而不是表级锁,减少事务之间的竞争和死锁发生的几率。

总结:

虽然 MVCC 在并发控制方面提供了显著的优势,但它的缺点主要包括存储空间消耗、性能开销、幻读问题、长事务的影响以及死锁问题。为了应对这些挑战,可以通过优化存储管理、使用更强的隔离级别、控制事务的长度、引入死锁检测等手段来解决这些问题,从而保持系统的高效运行。


http://www.niftyadmin.cn/n/5865665.html

相关文章

基于python+django的宠物商店-宠物管理系统源码+运行步骤

该系统是基于pythondjango开发的宠物商店-宠物管理系统。是给师妹开发的课程作业。现将源码开放给大家。大家学习过程中,如遇问题可以在github咨询作者。加油 演示地址 前台地址: http://pet.gitapp.cn 后台地址: http://pet.gitapp.cn/adm…

mac升级系统后聚焦Spotlight Search功能无法使用,进入安全模式可解

mac升级系统后,聚焦功能无法使用,表现为: 1)快捷键无法唤起聚焦框 2)点击右上角 聚焦图标(放大镜),没有任何反应 解决方案: 1)聚焦重建索引,无…

ArcGIS Pro中生成带计曲线等高线的全面指南

一、引言 在地理信息系统(GIS)领域,等高线作为表达地形起伏的重要视觉元素,被广泛应用于地图制作、空间分析以及地形可视化等方面。ArcGIS Pro,作为Esri公司推出的新一代GIS平台,提供了强大的空间分析和地…

嵌入式产品级-超小尺寸游戏机(从0到1 硬件-软件-外壳)

Ultra-small size gaming console。 超小尺寸游戏机-Pico This embedded product is mainly based on miniaturization, followed by his game functions are also very complete, for all kinds of games can be played, and there will be relevant illustrations in the fo…

jupyterhub on k8s 配置用户名密码 简单版

如果只是小组内使用 不想共用密码 也不想搞复杂认证方案 那么就直接通过map(用户名,密码md5值)来制定密码 config.yaml部分内容 hub:config:JupyterHub:shutdown_on_logout: true # 用户logout 自动stop jupyter pod,家目录下所有文件会被保存到pvc 即启动后之前家目录下…

图神经网络实战(24)——基于LightGCN构建推荐系统

图神经网络实战(24)——基于LightGCN构建推荐系统 0. 前言1. Book-Crossing 数据集介绍2. Book-Crossing 数据集预处理3. 构建 LightGCN 架构3.1 LightGCN 架构2. 实现 LightGCN3.3 损失函数3.4 模型训练与测试3.5 生成推荐 小结系列链接 0. 前言 推荐系…

springboot411-基于Java的自助客房服务系统(源码+数据库+纯前后端分离+部署讲解等)

💕💕作者: 爱笑学姐 💕💕个人简介:十年Java,Python美女程序员一枚,精通计算机专业前后端各类框架。 💕💕各类成品Java毕设 。javaweb,ssm&#xf…

详解Flask Flash Message(消息闪现)

目录 安装 Flask 入门:Flask flash() 基本示例 进阶:使用 Flask-WTF Flash 登录结果消息 详解:get_flashed_messages() 详解:flash() 消息的完整生命周期 Flask 提供 flash() 用于向 用户传递临时消息,通常用于: • 表单提交成功或失败 • 用户登录、注册、退出提…