数据库的读写分离技术MVCC

本文纯理论学习,无知之处请给与包涵! 写作不易,觉得好,麻烦请点广告支持下 分享一下!
作者不支持读者的任何抽象行为,阅读本文产生的任何后果,作者概不负责

MVCC 英文全称叫多版本并发控制协议. 以前做ORACLE DBA时候没有听说过.后来转到MYSQL DBA就听说了. 另外还有个WAL,这个是POSTGRE SQL的叫法. 这些概念不就是早就实现过得呀! ORACLE REDO,先写日志,后写数据.凡是都要记录日志.ORACLE的日志量可大了, REDO要保护系统表空间,还有辅助表空间,用户表空间,以及UNDO表空间的数据改变前的向量.

ORACLE UNDO 没有听人说 UNDO实现了MVCC.老盖出了那么多书,好像也没有提过.也许我人老了记忆性差.
 

MVCC 包含两方面:

  1. Multi-Versioning,MV:生成多版本的数据内容,使得不同请求(读写)可以获取响应版本数据。

  2. Concurrency Control,CC:并发控制,使得并行执行的内容能保持串行化结果。

    并发控制,我怎么感觉不出来呢? 是在说读的并发控制吗? 读也需要并发控制吗? 我以为是用MUTE,LATCH等内存锁.
    难道是写的并发控制?  这有可能

    行的元组

    数据库中每个数据的数据头(Header)会包含数据的 metadata 信息,这些信息被称为行的元组

    图片

txn-id: 数据的TID,用以实现写锁。begin-ts & end-ts:标识该元组版本的生命起止时间。pointer:指向同一行数据相邻(新/旧)版的指针,依靠指针,版本数据可以形成一个单向链表

上面4个字段,只是意思意思下, ORACLE只有两个 一个锁标志,另外一个是回滚指针. MYSQL会多了些字段.完成是否可见性(ReadView) 查询活跃事务列表,判断本事务ID..


并发控制协议

并发控制协议解决的是不同事务间执行顺序和结果的问题(而不是多版本数据),也就是通过或者时间顺序保持事务读写数据的串行性 说白了就是修改事务,ORACLE的 DML语句 写的并发控制.论文介绍了四种协议:Timestamp OrderingTwo-phase Locking Concurrency Control Serialization Certifier 我在这里不介绍论文,太理论,太烧脑. 我们知道ORACLE直接在行头用锁控制

图片

ORACLE 通过行锁标志+块的事务槽完成DML事务的并发控制.

图片

多版本存储 读写分离核心

多版本存储技术决定了各种不同DB 读写分离实现细节
 
 1  表内存储和表外存储
 2  整行存储,修改列增量存储

ORACLE和MYSQL 采用 表外+列增量存储
SQL SERVER        采用 表外+整行存储
POSTGRE SQL     采用 表内+整行存储


表内+整行存储:

图片


即使数据行中只有少量字段发生了变更。另外,如果表中带有非内联数据,如BLOB、TEXT字段,即使事务没有修改它,也会导致引入大量的重复,导致表数据膨胀。Postgres表膨胀这样也是原因之一

   SQL SERVER        采用 表外+整行存储

图片

既然行版本控制,是通过在读写并发时,让读会话去读取历史行版本数据,从而避免阻塞等待,那么这些历史行数据,存储在什么地方?
答案是: tempdb 数据库!

ORACLE和MYSQL 采用 表外+列增量存储

图片

这种方案对于更新操作很理想,因为减少了内存的分配。但是对于偏向与读的场景负担大。当进行获取一个元组的多个属性的读操作时,数据库需要遍历版本链,必须从各个字段的版本数据链中获取到对应版本的字段值,再进行拼凑,这就带来了一定的额外开销。我们拿ORACLE 来说, 看图回滚指针存在块的头部,也就是说更新某行的某个列,就要修改块头的UNDO地址, 那么数据块有很多行,很多列.那么这个UNDO地址就修改很频繁. 这数据块的UNDO历史版本就很长. 当某个SELECT想要读取它需要的数据时候,需要遍历这个数据块的历史链条.越过很多不是该行的修改版本.

图片

为此这表外+列增量方式 还需要从当前块或者行来逆历史版本共同构造出CR块或者CR行

其实最佳的应该是SQLSERVER 表外+整行存储. 如果对TEXT,BLOB做下优化就完美了.如果没有修改TEXT,BLOB字段,那么就复制基本行去历史区域.
如果修改了就一起复制过去历史区域. 第一次做一下FULL PAGE.或者其它什么方式进行优化 或者在TEXT,BLOB页增加版本控制的字段.

最后就是 回滚覆盖
  
版本太长了是个问题, 不需要的旧版本应该清理掉. ORACLE 经典的错误

   Oracle ORA-01555
这在ORACLE 11G 以前是个头痛的事情,后来ORACLE根据查询最长时间的SELECT,指定回滚段保留时间,这又导致UNDO的膨胀.
它有三种状态来管理生命周期,分别是ACTIVE  UNEXPIRED EXPIRED,
活跃事务的段总是ACTIVE状态;
已完成事务的,但是在UNDO_RETENTION周期内的是UNEXPIRED状态;
已完成事务的UNDO,但已经过了UNDO_RETENTION保留周期的是EXPIRED状态。

图片

ORACLE 不用专门的程序去清理历史版本,而是采用覆盖重用的方式.

MYSQL 需要专门的线程去清理,它采用事务级别盯着活跃事务列表

图片

事务记录它读写数据行的集合,DBMS需要监控集合中的版本数据是否都过时了。当某个事务的版本数据集合对所有的活跃事务都不可见时,就可以将这个集合中的版本数据都进行清理。优点是数据库可以立即回收对应事务的所有存储空间,缺点是数据库要跟踪每个时期的事务读/写集

MySQL的 MVCC 主要依赖于:数据行中的隐式字段、undo log,consistent read view

undo log 中主要保存了数据的基本信息,比如说日志开始的位置、结束的位置,主键的长度、表id,日志编号、日志类型

图片

InnoDB存储引擎中的每行记录都包含一些隐藏字段,如DB_TRX_ID最后修改该行的事务ID、DB_ROLL_POINT 回滚指针

图片

当事务执行读写分离时,会产生一个ReadView,ReadView中包含了系统中活跃的事务ID列表。
通过比较ReadView与历史版本链中的DB_TRX_ID,可以确定哪个历史版本的数据对当前事务可见。数据库每次数据修改时,都会在undo log中留下快照,用于读取旧版本信息。

ReadView 其实就是一个保存事务ID的list列表。记录的是本事务执行时,MySQL还有哪些事务在执行,且还没有提交。

它主要包含这样几部分:

  • m_ids,当前有哪些事务正在执行,且还没有提交,这些事务的 id 就会存在这里;

  • min_trx_id,是指 m_ids 里最小的值;

  • max_trx_id,是指下一个要生成的事务 id。下一个要生成的事务 id 肯定比现在所有事务的 id 都大;

  • creator_trx_id,每开启一个事务都会生成一个 ReadView,而 creator_trx_id 就是这个开启的事务的 id。

这样在访问某条记录时,只需要按照下边的步骤判断该记录在版本链中的某个版本(trx_id)是否可见:
        1、trx_id < m_ids列表中最小的事务id
        表明生成该版本的事务在生成ReadView前已经提交,所以该版本可以被当前事务访问。
        2、trx_id > m_ids列表中最大的事务id
        表明生成该版本的事务在生成ReadView 后才生成,所以该版本不可以被当前事务访问。
        3、m_ids列表中最小的事务id ≤ trx_id ≤ m_ids列表中最大的事务id(可以取等于,感谢网友指出)
        此处比如m_ids为[5,6,7,9,10]
        ①、若trx_id在m_ids中,比如是6,可以分成2种情况

                a. creator_trx_id也是6,说明这条trx_id记录是当前事务产生的,可以被访问

                a. creator_trx_id不是6,说明这条trx_id记录不是当前事务产生的,并且创建 ReadView 时生成该版本的事务还是活跃的,该版本不可以被访问。
        ②、若trx_id不在m_ids中,比如是8:说明创建 ReadView 时生成该版本的事务已经被提交,该版本可以被访问。

一句话说:当trx_id在m_ids中且creator_trx_id != trx_id,或者大于m_ids列表中最大的事务id的时候,这个版本就不能被访问。

如果某个版本的数据对当前事务不可见的话,那就顺着版本链找到下一个版本的数据,继续按照上边的步骤判断可见性,依此类推,直到版本链中的最后一个版本,如果最后一个版本也不可见的话,那么就意味着该条记录对该事务不可见,查询结果就不包含该记录。

图片

图片

图片

MYSQL 依靠锁链完成事务的并发控制

图片

图片

图片

因此 MVCC 应该说是多版本读写分离

当然也可以这样说 MVCC 是多版本读写分离提升读并发的协议

基于MYSQL的JAVA初级优化措施
MYSQL RR隔离下加锁初步理解
MYSQL RC下加锁的初步理解
不会C/C++的不是好DBA,一个解析MYBAITS的脚本
架构DBA
终于搞明白64位LINUX页表问题
一段直接路径读取文件LINUX C代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值