数据库MVCC详解
MVCC
1.基本介绍
数据库:MySQL。【很多主流数据库都使用了MVCC,比如MySQL的InnoDB引擎、PostgreSQL、Oracle】
MVCC,全称Multi-Version Concurrency Control,即多版本并发控制。是数据库管理系统中的一种并发控制方法。
MVCC的基本原理: 它通过保存数据的历史版本来实现,这样读操作不会被写操作阻塞,写操作也不会被读操作阻塞。这样的话,提高了并发性能。比如说,当一个事务开始读取数据时,它会看到数据库在某个时间点的快照,之后即使其他事务修改了数据,这个事务看到的还是旧版本的数据,直到它提交或者回滚。
不同的隔离级别(如读已提交、可重复读、串行化)在MVCC中的实现方式不同。例如,在读已提交级别下,每次读取都会获取最新的快照,而可重复读则是在事务开始时确定快照,后续读取都基于这个快照,从而避免不可重复读的问题。
- 当前读和快照读
- 当前读:像select lock in share mode(共享锁), select for update ; update, insert ,delete(排他锁)这些操作都是一种当前读,为什么叫当前读?就是它读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。
- 快照读:像不加锁的select操作就是快照读,即不加锁的非阻塞读;快照读的前提是隔离级别不是串行级别,串行级别下的快照读会退化成当前读;之所以出现快照读的情况,是基于提高并发性能的考虑,快照读的实现是基于多版本并发控制,即MVCC,可以认为MVCC是行锁的一个变种,但它在很多情况下,避免了加锁操作,降低了开销;既然是基于多版本,即快照读可能读到的并不一定是数据的最新版本,而有可能是之前的历史版本
MVCC就是为了实现读-写冲突不加锁,而这个读指的就是快照读, 而非当前读,当前读实际上是一种加锁的操作,是悲观锁的实现
当前读 <==> 悲观锁; 快照读 <=> 乐观锁?
2.mvcc实现原理
数据库不知道的秘密。
每行记录除了我们自定义的字段外,还有数据库隐式定义的DB_TRX_ID,DB_ROLL_PTR,DB_ROW_ID等字段
- DB_TRX_ID:6byte,最近修改(修改/插入)事务ID:记录创建这条记录/最后一次修改该记录的事务ID ---------【事务ID】
- DB_ROLL_PTR:7byte,回滚指针,指向这条记录的上一个版本(存储于rollback segment里) -----------------【回滚指针】
- DB_ROW_ID:6byte,隐含的自增ID(隐藏主键),如果数据表没有主键,InnoDB会自动以DB_ROW_ID产生一个聚簇索引 ----------【隐藏主键】
- 实际还有一个删除flag隐藏字段, 既记录被更新或删除并不代表真的删除,而是删除flag变了 --------【删除标记】
① MVCC 核心机制
- 版本链:每行数据通过隐藏字段
DB_TRX_ID(事务ID)和DB_ROLL_PTR(回滚指针)维护多个版本。 - ReadView:事务启动时生成一个“快照”,记录当前活跃事务的 ID 列表,用于判断数据版本的可见性。
- 事务隔离级别:不同隔离级别下,ReadView 的生成规则不同(例如“读已提交”每次读都生成新快照,“可重复读”使用事务启动时的快照)。
②示例
有这样一张表,里面有记录。
| id | name | age | DB_TRX_ID | DB_ROLL_PTR |
|---|---|---|---|---|
| 1 | Alice | 25 | 100 | 0x0000 |
初始版本由事务 txid=100 提交生成;DB_ROLL_PTR 指向旧版本(初始为 NULL)。
这个时候有两个事务来了。
- 事务A(txid=200):执行
UPDATE user SET age=26 WHERE id=1。 - 事务B(txid=201):在事务A提交前,执行
SELECT * FROM user WHERE id=1。
事务A 更新数据,InnoDB引擎,会为这行数据创建一个新版本,并修改 DB_TRX_ID=200,DB_ROLL_PTR 指向旧版本(事务100提交的版本)。
这个时候,还没有A事务还没有提交,新版本(V2)还未提交,旧版本(V1)仍然存在。
id | name | age | DB_TRX_ID | DB_ROLL_PTR 【这个是新版本V2】
---|-------|-----|-----------|------------
1 | Alice | 26 | 200 | 0x1234(指向旧版本V1)丨丨丨丨↓id | name | age | DB_TRX_ID | DB_ROLL_PTR 【这个是旧版本V1】
---|-------|-----|-----------|------------
1 | Alice | 25 | 100 | 0x0000
然后,事务B读取数据,事务B 启动时会生成一个 ReadView,记录当前活跃事务的 ID 列表。假设此时只有事务A(txid=200)未提交,活跃事务列表为 [200]
事务B 根据 ReadView 判断数据版本的可见性:
- 规则:若数据版本的
DB_TRX_ID小于当前事务的txid,且该事务已提交,则可见。 - 当前数据最新版本是 V2(
DB_TRX_ID=200),但事务B 的 ReadView 显示txid=200是活跃的(未提交),因此 V2 不可见。 - 事务B 沿着回滚指针(
DB_ROLL_PTR)找到旧版本 V1(DB_TRX_ID=100),判断100 < 201且已提交,因此返回 V1 的数据:
故,事务B读到的如下:
id | name | age
---|-------|-----
1 | Alice | 26
可以看到上述读写,好像没有加锁。。
③可见性规则
InnoDB 判断数据版本是否可见的逻辑如下:
- 如果数据版本的
DB_TRX_ID小于当前事务的txid,且该事务已提交 → 可见,【怎么看提交了没有呢?看事务的活跃列表】。 - 如果数据版本的
DB_TRX_ID等于当前事务的txid→ 可见(事务可以看到自己的修改)。 - 如果数据版本的
DB_TRX_ID大于当前事务的txid→ 不可见(属于未来的修改)。 - 如果数据版本的
DB_TRX_ID在事务的 ReadView 活跃列表中 → 不可见(该事务尚未提交)。
MVCC 通过维护数据多版本和 ReadView 机制,实现读写不阻塞和高并发:
- 写操作:生成新版本,不影响其他事务的读操作。
- 读操作:基于快照读取旧版本,无需加锁。
- 提交后的版本:对新事务可见,旧事务仍看到历史版本。
这种机制在保证事务隔离性的同时,极大提升了数据库并发性能。
3.补充知识点
MySQL 中的 undo log、redo log 和 binlog 是三种核心日志,各自承担不同的职责,共同保障数据库的事务性、持久性和高可用性
下面三个是参考deepseek的解释。
① Undo Log(回滚日志)
作用
- 事务回滚:记录数据修改前的旧值,用于事务回滚时恢复原始数据。
- MVCC(多版本并发控制):提供历史版本数据,支持一致性非锁定读(Consistent Non-locking Read)。
工作机制
- 当执行
INSERT、UPDATE或DELETE时,InnoDB 会将修改前的数据保存到 undo log。 - 事务回滚:通过 undo log 逆向操作,恢复数据到修改前的状态。
- MVCC 实现:其他事务读取数据时,若当前版本不可见(如未提交),则通过 undo log 找到可见的历史版本。
见上面的mvcc。
② Binlog(二进制日志)
作用
- 主从复制:记录所有数据库修改操作,用于主库到从库的数据同步。
- 数据恢复:支持按时间点恢复数据(Point-in-Time Recovery, PITR)。
工作机制
- 逻辑日志:记录 SQL 语句或行的逻辑变化(取决于
binlog_format:STATEMENT、ROW、MIXED)。 - 写入时机:事务提交后写入 binlog(与 redo log 不同)。
- 刷盘控制:由
sync_binlog参数控制刷盘频率。
我来举个例子:
-- 事务C: 删除一行数据
BEGIN;
DELETE FROM users WHERE id = 1;
COMMIT;-- 提交后,binlog 记录该 DELETE 语句(或行变化)。从库读取 binlog 并重放,保持数据一致性。
③ Redo Log(重做日志)
作用
- 崩溃恢复:确保事务的持久性(Durability)。即使数据库崩溃,已提交的事务修改不会丢失。
- Write-Ahead Logging (WAL):修改数据前,先记录重做日志到磁盘。
工作机制
- 事务提交时,先将所有修改的物理页变化记录到 redo log(顺序写入,性能高)。
- 刷盘策略:由
innodb_flush_log_at_trx_commit控制:1(默认):每次提交都刷盘,保证崩溃恢复不丢数据。0或2:延迟刷盘,性能更高但可能丢失部分数据。
- 崩溃恢复:重启后,通过 redo log 重放未写入数据文件的修改。
我来举个例子:
-- 事务B: 插入一条新数据
BEGIN;
INSERT INTO users (id, name) VALUES (2, 'Bob');
COMMIT; -- 提交时,redo log 记录插入操作的物理页修改,之后数据可能还未写入磁盘。
-- 若此时崩溃,重启后根据 redo log 恢复该插入操作。
以更新操作为例子:
- 事务执行:修改数据前,将旧值记录在undo log;最终还是要修改磁盘数据的,还记录一下修改的物理页位置 redo log
- 事务提交:redo log标记为
prepare状态并刷盘。 写入 binlog 并刷盘。将 redo log 标记为commit状态(两阶段提交,保证一致性)。 - 崩溃恢复: 若崩溃发生在 binlog 写入前,事务回滚(通过 undo log)。 若 binlog 已写入,则根据 redo log 重放修改。
1. 为什么需要 redo log 和 binlog 两种日志?
- redo log 是 InnoDB 引擎层的物理日志,保证崩溃恢复和持久性。
- binlog 是 MySQL Server 层的逻辑日志,支持主从复制和跨引擎数据恢复。
- 二者结合通过“两阶段提交”确保数据一致性。
2. undo log 会被删除吗?
- 当事务提交且没有其他事务需要访问旧版本数据时,undo log 可以被清理。
- 长事务可能导致 undo log 堆积(“版本膨胀”),影响性能。
3. binlog 和 redo log 的写入顺序?
-
事务提交时,先写 redo log(prepare) → 再写 binlog → 最后写 redo log(commit)。
-
这是为了确保崩溃恢复时,binlog 和 redo log 的一致性(两阶段提交)。
-
undo log:保障事务的原子性和 MVCC。
-
redo log:保障事务的持久性和崩溃恢复。
-
binlog:保障数据可复制性和逻辑恢复。
end.参考
参考:https://www.cnblogs.com/jelly12345/p/14889331.html
相关文章:
数据库MVCC详解
MVCC 1.基本介绍 数据库:MySQL。【很多主流数据库都使用了MVCC,比如MySQL的InnoDB引擎、PostgreSQL、Oracle】 MVCC,全称Multi-Version Concurrency Control,即多版本并发控制。是数据库管理系统中的一种并发控制方法。 MVCC的…...
MySQL数据库基础篇
目录 SQL的分类 数据定义语言(DDL)---Data Definition Language 数据操作语言(DML) ---Data Manipulation Language 数据查询语言(DQL) ---Data Query Language 数据控制语言(DCL) ---Data Control Language 事务控制语言(TCL) --- Transaction Cont…...
Rust函数、条件语句、循环
文章目录 函数**语句与表达式**条件语句循环 函数 Rust的函数基本形式是这样的 fn a_func(a: i32) -> i32 {}函数名是蛇形风格,rust不在意函数的声明顺序,只需要有声明即可 函数参数必须声明参数名称和类型 语句与表达式 这是rust非常重要的基础…...
AI比人脑更强,因为被植入思维模型【17】万物联系思维模型
万物联系,万物,并不孤立。 定义 万物联系思维模型是一种强调世界上所有事物都相互关联、相互影响的思维方式。它认为任何事物都不是孤立存在的,而是与周围的环境、其他事物以及整个宇宙构成一个有机的整体。这种联系不仅包括直接的因果关系,还涵盖了间接的、潜在的、动态的…...
Android Compose 约束布局(ConstraintLayout、Modifier.constrainAs)源码深度剖析(十二)
Android Compose 约束布局(ConstraintLayout、Modifier.constrainAs)源码深度剖析 一、引言 在 Android 开发中,布局是构建用户界面的基础。随着 Android 开发技术的不断发展,Jetpack Compose 作为一种全新的声明式 UI 框架应运…...
【MySQL篇】复合查询
目录 前言: 1,多表查询 2,自连接 3,子查询 3.1,单行子查询 3.2,多行子查询 3.3,多列子查询 3.3,在from子句中使用子查询 4,合并查询 4.1,union …...
点亮STM32最小系统板LED灯
对于如何点亮板载LED灯只需要掌握如何初始化GPIO引脚,并改变GPIO引脚的电平即可实现点亮或者熄灭LED。 Led_INFO led_info {0}; led_info 是一个结构体变量,类型为 Led_INFO,用于存储LED的状态信息。这里初始化为 {0},表示所有成…...
unsloth微调QwQ32B(4bit)
unsloth微调QwQ32B(4bit) GPU: 3090 24G unsloth安装部署 pip 安装 pip install unsloth --index https://pypi.mirrors.usrc.edu.cn/simplesource /etc/network_turbopip install --force-reinstall --no-cache-dir --no-deps githttps://github.com/unslothai/unsloth.git…...
基于腾讯云大模型知识引擎×DeepSeek的高等职业学校单独招生二级学院考前咨询系统
1、主要思路 通过大模型知识引擎DeepSeek搭建高等职业学校单独招生二级学院考前咨询专有问答,使得专业老师能够更好的服务考试学生,有利于二级学院能够更好的进行考试宣传,招来优秀学子! 2、创作过程 2.1、本地部署大模型的缺陷…...
【Linux】线程库
一、线程库管理 tid其实是一个地址 void* start(void* args) {const char* name (const char *)args;while(true){printf("我是新线程 %s ,我的地址:0x%lx\n",name,pthread_self());sleep(1);}return nullptr; }int main() {pthread_t tid…...
数组作为哈希表的妙用:寻找缺失的第一个正数
数组作为哈希表的妙用:寻找缺失的第一个正数 大家好,我是Echo_Wish,今天我们来探讨一个经典的算法问题——“缺失的第一个正数”。听起来可能有点简单,但它实际上是一个非常有意思且富有挑战性的题目,在面试中常常会碰…...
物化视图详解:数据库性能优化的利器
物化视图(Materialized View)作为数据库性能优化的核心手段,通过预计算和存储查询结果,显著提升了复杂查询的效率。本文将深入剖析物化视图的工作原理、应用场景及最佳实践,帮助企业在合适的场景中充分发挥其性能优势。…...
【C++】类和对象(匿名对象)
匿名对象 用 类型(实参) 定义出来的对象叫做匿名对象,相比之前我们定义的 类型 对象名(实参) 定义出来叫有名对象匿名对象生命周期只在当前一行,一般临时定义一个对象当前用一下即可,就可以定义匿名对象。 class A { public:A(int a 0):_a…...
一文读懂 GPT 与 BERT:预训练逻辑及差异剖析
在自然语言处理(NLP)领域,预训练语言模型GPT(Generative Pretrained Transformer)和 BERT(Bidirectional Encoder Representations from Transformers)作为杰出代表,备受关注。本文将…...
【算法】十大排序算法(含时间复杂度、核心思想)
以下是 **十大经典排序算法** 的时间复杂度、空间复杂度及稳定性总结,适用于面试快速回顾:排序算法对比表 排序算法最佳时间复杂度平均时间复杂度最差时间复杂度空间复杂度稳定性核心思想冒泡排序O(n)O(n)O(n)O(1)稳定相邻元素交换,大数沉底…...
渐进式滑坡多场信息演化特征与数据挖掘研究
标题:渐进式滑坡多场信息演化特征与数据挖掘研究 内容:1.摘要 摘要:在地质灾害频发的背景下,研究渐进式滑坡多场信息演化特征与数据挖掘具有重要的实际意义。本研究旨在深入探究渐进式滑坡在不同阶段的多场信息(如应力场、位移场、渗流场等&…...
蓝桥杯备考-》单词接龙
很明显,这道题是可以用DFS来做的,我们直接暴力搜索,但是这里有很多点是我们需要注意的。 1.我们如何确定两个单词能接上? 比如touch和choose 应该合成为touchoose 就是这样两个单词,我们让一个指针指着第一个字符串…...
解锁C++模板参数:开启泛型编程新世界
目录 C++ 模板:编程世界的瑞士军刀 一、模板参数初相识 1.1 类型参数 1.2 非类型参数 1.3 模板模板参数 二、模板参数推导大揭秘 2.1 推导规则深度剖析 2.2 推导成功场景展示 2.3 推导失败场景解析 三、模板参数实战应用 3.1 通用算法实现 3.2 容器类设计 3.3 元…...
计算机视觉yolov8模型应用-学习笔记
计算机视觉yolov8模型应用-学习笔记 YOLOv8是由Ultralytics公司在2023年1月10日发布的一款深度学习模型。它是YOLOv5的重大更新版本,支持图像分类、物体检测和实例分割任务。这一版本在发布前就受到了广泛关注,并在发布后迅速成为目标检测领域的热门…...
【网络层协议】NAT技术内网穿透
IP地址数量限制 我们知道,IP地址(IPv4)是一个4字节32位的整数,那么一共只有2^32也就是接近43亿个IP地址,而TCP/IP协议栈规定,每台主机只能有一个IP地址,这就意味着,一共只有不到43亿…...
SQL中的索引是什么
在 SQL 中,索引(Index) 是一种用于加速数据检索的数据库对象,通过建立特定的数据结构(如 B树、哈希表等),帮助数据库系统快速定位目标数据。以下是关于索引的详细分类、工作原理、使用场景和最佳…...
TensorFlow面试题及参考答案
目录 什么是 TensorFlow 的计算图?详细描述 TensorFlow 计算图的组成结构(节点、边、会话) 它与动态图(Eager Execution)的区别是什么?TensorFlow 静态计算图与动态图(Eager Execution)的区别及适用场景是什么? 解释张量(Tensor)的概念及其在 TensorFlow 中的作用…...
go-zero学习笔记
内容不多,只有部分笔记,剩下的没有继续学下去,包括路由与处理器、日志中间件、请求上下文 文章目录 1、go-zero核心库1.1 路由与处理器1.2 日志中间件1.3 请求上下文 1、go-zero核心库 1.1 路由与处理器 package mainimport ("github…...
在Ubuntu 22.04 中安装Docker的详细指南
这里写目录标题 前言一、安装 Docker1. 卸载旧版本(如有)2. 更新系统并安装依赖工具3. 添加 Docker 官方 GPG 密钥4. 设置 Docker 仓库5. 安装 Docker Engine6. 验证安装 二、配置 Docker 镜像加速1. 修改 Docker 配置文件2. 重启 Docker 服务3. 验证加速…...
十亿级流量削峰实战:LinkedBlockingQueue缓冲池的工程化实现
《十亿级流量削峰实战:LinkedBlockingQueue缓冲池的工程化实现》 本文将以电商秒杀系统为背景,深度解析如何通过LinkedBlockingQueue构建百万QPS级异步缓冲系统,包含容量计算模型、拒绝策略选择、监控埋点方案等完整实施细节,并提…...
深入理解 C++11 智能指针:独占、共享与弱引用的完美管理
文章目录 std::unique_ptr(独占式智能指针)std::shared_ptr(共享式智能指针)std::weak_ptr(弱引用智能指针)示例展示:智能指针的原理内存泄漏**什么是内存泄漏,内存泄漏的危害****如…...
AI Agent开发大全第四课-提示语工程:从简单命令到AI对话的“魔法”公式
什么是提示语工程?一个让AI“听话”的秘密 如果你曾经尝试过用ChatGPT或者其他大语言模型完成任务,那么你一定遇到过这样的情况:明明你的问题是清晰的,但答案却离题万里;或者你认为自己提供的信息足够详尽,可结果还是不理想。问题出在哪?很多时候并不是因为AI不够聪明,…...
大模型架构记录 【综述-文字版】
名词解释: Prompt :提示词,是一个非常关键的概念,它指的是用户输入的文本或指令,用于引导语言模型生成相应的回答或执行特定任务。 Prompt Engineering:(提示工程) 是一种通过设计…...
WebSocket:开启实时通信的新篇章
在当今的互联网应用中,实时交互已经成为不可或缺的一部分。无论是实时的在线聊天、股票行情更新,还是多人在线游戏,都需要一种高效的双向通信机制。而这正是 WebSocket 的用武之地。 本文将带你深入了解 WebSocket,探索其工作原理…...
【论文笔记】Transformer
Transformer 2017 年,谷歌团队提出 Transformer 结构,Transformer 首先应用在自然语言处理领域中的机器翻译任务上,Transformer 结构完全构建于注意力机制,完全丢弃递归和卷积的结构,这使得 Transformer 结构效率更高…...
