Mysql杂志(三十四)——MVCC、日志分类

我们之前在第三十二篇的时候说过https://blog.csdn.net/qq_45041250/article/details/153202270?spm=1001.2014.3001.5501,数据库的隔离性是由MVCC+锁的机制来实现的。锁的相关内容主包上一篇已经说的很明白了,今天来说一下MVCC多版本控制。

什么是MVCC

MVCC(Multi-Version Concurrency Control)是InnoDB实现非锁定读 的核心机制,通过维护数据的多个历史版本,实现:读不阻塞写 ​、写不阻塞读 ​、避免脏读/不可重复读 ​。实现这个功能的主要功臣就是ReadView ,我们之前也讲过四个隔离级别,分别是读未提交、读已提交、可重复读、串行读,其中读未提交本身就是脏读所以不需要MVCC,而串行读是全部加锁一个一个排队来,所以不存在脏读、幻读所以也不需要MVCC,所以MVCC服务的对象就是读已提交、可重复读这2个隔离级别,现在其他的都不需要管,只需要记住 读已提交不管在不在一个事务,都会生成新的ReadView,而可重复读,在同一个事物中查同一行数据只会生成一个ReadView,主包认为这个就是可重复读的名字由来。

ReadView

字段 大小 作用 示例
DB_TRX_ID 6B 最后修改事务ID 10023
DB_ROLL_PTR 7B 回滚指针 0x7FAB89
DB_ROW_ID 6B 隐式自增ID 1024

这个是我们之前讲索引的时候讲的行格式中的三个隐藏字段,其中DB_ROLL_PTR这个指向的就是undolog。

这个是ReadView的主要字段,其实每个ReadView还有个trx_id,也就是创建他的事务id,如果是查询且没有加事务,那么这个trx_id就是0,也就是MVCC不管加没加事务都会生成一个Readview,来保证数据的一致性也就是可重复读。

要素 生成规则 作用
m_ids 快照时刻活跃事务ID列表 过滤未提交事务
m_up_limit_id 最小活跃事务ID 快速判断老事务
m_low_limit_id 分配的下个事务ID 识别新事务

然后我们创建一个ReadView的时候,就会检测当前未提交的事务这些事务被称为活跃事务,然后把这些活跃事务写入到m_ids列表中,然后m_up_limit_id记录的是当前最小的活动事务ID,m_low_limit_id这个是当前最大事务ID+1,表示的就是下次事务分配的id就是这个数。

ReadView读取流程

第一,我们读取一行数据的时候,不管这个数据加没加锁都会生成一个ReadView,然后记录当前活跃的事务数组。

第二,找到要读取的这行数据的事务ID,然后按照以下的方式进行读取。

python 复制代码
def is_visible(trx_id, read_view):
    # 第一阶:低水位线检查
    if trx_id < read_view.m_up_limit_id:
        return True  # 已提交的老事务
    
    # 第二阶:高水位线检查
    if trx_id >= read_view.m_low_limit_id:
        return False  # 未来事务或当前事务自身修改
    
    # 第三阶:活跃事务检查
    if trx_id in read_view.m_ids:
        return False  # 未提交的活跃事务
    
    return True  # 已提交的中间事务

先检查最小事务id也就是低水位线,如果小于那么表示这个数据早就提交了,是干净的数据,返回当前行的数据。

如果不满足就检查是否大于等于最高事务ID也就是高水位线,如果是则进行下一个循环也就是找到undolog的下一个版本,为什么不能大于等于最高水位线?是因为这个是未来的事务id,你不知道他是否已经提交,所以不能读取万一是脏读呢?

然后就是检查是否在我们的活跃事务id中,如果再的话也不能读取,因为活跃事务数组中的事务都是没有提交的,是脏数据,所以就会通过undolog去找上一个版本,然后再这样循环,直到找到可以访问的数据,如果没有那么就表示,这个数据是未来事务新增的,或者是活跃事务数组中的某一个事务新增的,所以就查询为空了,不会产生幻读的现象。

然后当一个事务消亡那么对应的ReadView也会消亡。好了这下知道Mysql是怎么解决幻读、脏读了吧,AICD原则也都很清楚了。

日志分类

我们之前已经知道了有undo log喝redo log,那mysql是不是还有其他的日志文件呢?那肯定是有的呀,他们可以分为三大类分别是事务日志、复制日志、操作日志。需要注意的是只有操作日志才是文本日志,其他的都是二进制日志,文本日志可以直接使用vim查看,其他的日志想查看就得借助一些工具了。

我们今天先来简单的过一下他们吧。

事务日志

1. Redo Log(重做日志)

这个我们之前讲过了这里就不说了

特性 描述 生产价值
作用 崩溃恢复、保证事务持久性 数据安全基石
存储 ib_logfile0,1(循环写入) 性能提升5-10倍
内容 物理日志(页修改) 精确恢复
刷盘策略 innodb_flush_log_at_trx_commit 之前有说过默认是1,有三种行为

2. Undo Log(回滚日志)

特性 描述 生产价值
作用 事务回滚、MVCC多版本控制 高并发基础
存储 undo表空间(8.0+独立文件) 隔离性保障
内容 逻辑日志(行前镜像) 版本链管理
清理机制 Purge线程异步清理 空间自动回收

复制日志

1. Binlog(二进制日志)

这一部分我们留到下篇说吧。

特性 描述 生产价值
作用 主从复制、数据恢复 高可用核心
存储 mysql-bin.000001(序列文件) 跨实例同步
格式 Statement/Row/Mixed 灵活选择
应用场景 数据恢复、异构同步 业务连续性

2. Relay Log(中继日志)

特性 描述 生产价值
作用 从库接收主库binlog的临时存储 复制缓冲
存储 relay-bin.000001(类binlog) 解耦主从
生命周期 SQL线程应用后删除 空间自动管理
监控重点 延迟检测、文件堆积 复制健康度

操作日志

1. Error Log(错误日志)

这个日志是mysql默认就打开的而且是无法关闭的,我们可以通过 show variables查看相关的属性,这个日志会记录整个mysql服务器的错误,默认名字是mysqld.log在linux中。

特性 描述 生产价值
作用 记录启动/运行/停止错误信息 故障诊断
存储 hostname.err(文本格式) 问题定位
级别 Note/Warning/Error 分级告警
关键信息 崩溃堆栈、参数错误 快速恢复

这个是相关的一些系统变量:

参数 默认值 推荐值 作用说明
log_error hostname.err 自定义路径 日志文件位置
log_error_verbosity 2 (5.7+) 3 详细级别
log_error_services 8.0+ log_filter_internal; log_sink_json 日志服务组件
log_timestamps UTC SYSTEM 时间戳格式
log_warnings 2 2 警告级别

这个是错误级别:

Verbosity 记录内容 生产建议
1 ERROR级错误 生产环境最低要求
2 ERROR+WARNING 默认推荐配置
3 ERROR+WARNING+NOTE 开发/调试环境

2. Slow Query Log(慢查询日志)

这个大家应该也多多少少有所耳闻了,这个默认也是关闭的,查看的相关属性的命令还是 show variabels like命令,设置同样也是set globle 。

特性 描述 生产价值
作用 记录执行超时的SQL语句 性能优化
存储 hostname-slow.log(文本/CSV) SQL调优
阈值 long_query_time(默认10秒) 可配置
分析工具 mysqldumpslow、pt-query-digest 自动化分析

这个是慢sql日志的一些系统变量和其作用:

参数 默认值 推荐值 作用说明
slow_query_log OFF ON 总开关
slow_query_log_file hostname-slow.log 自定义路径 日志文件位置
long_query_time 10秒 0.5-2秒 慢查询阈值
log_queries_not_using_indexes OFF ON 记录无索引查询
log_slow_admin_statements OFF ON 记录管理命令
log_slow_slave_statements OFF ON(主从架构) 从库慢查询
log_output FILE FILE/TABLE 输出格式

这个是日志大概的样子:这个文本怎么看相信就不需要主包来解释了,学习了执行计划再来看这种日子文件还是蛮简单的。

php 复制代码
# Time: 2023-08-15T10:23:45.123456Z
# User@Host: root[root] @ localhost [127.0.0.1]
# Query_time: 3.141592  Lock_time: 0.000123 Rows_sent: 100  Rows_examined: 1000000
SET timestamp=1692095025;
SELECT * FROM orders WHERE create_date > '2023-01-01' AND status='PENDING';

3. General Query Log(通用查询日志)

这个其实也是操作日志,但是默认是关闭的,因为他记录的是所以执行的sql,可想而知是非常大的,根本没有这么多的内存可以供他一直开启,所以一般都是用在调试或者查询问题的时候短暂开启的,还是一样使用 show variables like进行查看系统变量,设置值的话还是set globle 进行设置。

参数 默认值 取值范围 作用说明
general_log OFF ON/OFF 总开关
general_log_file host_name.log 合法路径 日志文件位置
log_output FILE FILE/TABLE/NONE 输出目标
log_timestamps UTC SYSTEM/UTC 时间戳格式

日志的内容大概就是下面这个样子:

php 复制代码
2023-08-15T10:23:45.123456Z	   42 Connect	root@localhost on employees
2023-08-15T10:23:46.234567Z	   42 Query	SELECT * FROM salaries WHERE emp_no=10001
2023-08-15T10:23:47.345678Z	   42 Query	UPDATE salaries SET salary=salary*1.1 WHERE emp_no=10001
2023-08-15T10:23:48.456789Z	   42 Quit

总结

本篇主要讲了什么是MVCC和MVCC有什么作用、实现的流程,以及介绍了日志分类。

相关推荐
岁岁种桃花儿18 分钟前
MySQL从入门到精通系列:InnoDB记录存储结构
数据库·mysql
jiunian_cn2 小时前
【Redis】hash数据类型相关指令
数据库·redis·哈希算法
冉冰学姐2 小时前
SSM在线影评网站平台82ap4(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面
数据库·ssm框架·在线影评平台·影片分类
Exquisite.3 小时前
企业高性能web服务器(4)
运维·服务器·前端·网络·mysql
知识分享小能手3 小时前
SQL Server 2019入门学习教程,从入门到精通,SQL Server 2019数据库的操作(2)
数据库·学习·sqlserver
踩坑小念4 小时前
秒杀场景下如何处理redis扣除状态不一致问题
数据库·redis·分布式·缓存·秒杀
萧曵 丶4 小时前
MySQL 语句书写顺序与执行顺序对比速记表
数据库·mysql
Wiktok5 小时前
MySQL的常用数据类型
数据库·mysql
曹牧5 小时前
Oracle 表闪回(Flashback Table)
数据库·oracle
J_liaty6 小时前
Redis 超详细入门教程:从零基础到实战精通
数据库·redis·缓存