兰姆达从集合中找出另一个集合中id一致的数据_数据库中的引擎、事务、锁、MVCC(三)...

我的参考网站很少使用卡片,每次使用很建议大家读一读。

另外由于文章写完之后是从有道云笔记贴过来的,格式样式都有点错乱,现在附上有道云的链接地址,带格式的。

https://note.youdao.com/share/?id=8d2aafa40a1126948c839d97ec81e0f7&type=notebook#/B305B041ACF54495B161BC9B35FC53CA​note.youdao.com

本章咱们介绍MVCC,数据库的并发访问。

一、定义

MVCC:Multi-Version Concurrency Control 多版本并发控制,MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问;在编程语言中实现事务内存。

二、介绍:

大多数的MySQL事务型存储引擎,如InnoDB,Falcon以及PBXT都在使用一种简单的行锁机制。事实上,他们都和另外一种用来增加并发性的被称为“多版本并发控制(MVCC)”的机制来一起使用。MVCC不只使用在MySQL中,Oracle、PostgreSQL,以及其他一些数据库系统也同样使用它。

MVCC看成行级别锁的一种妥协,它在许多情况下避免了使用锁,同时可以提供更小的开销。根据实现的不同,它可以允许非阻塞式读,在写操作进行时只锁定必要的记录。

MVCC的实现通俗的讲就是MVCC通过保存数据的历史版本,根据比较版本号来处理数据的是否显示,从而达到读取数据的时候不需要加锁就可以保证事务隔离性的效果。

三、MVCC的实现

了解MVCC实现之前,需要了解一下数据库行结构、以及ReadView结构,这对了解MVCC很重要。

813f2a4d06ae33fcd0232a4362647527.png

1、数据库行结构

数据库中的表除了自己建表时候的字段,还隐藏了三个隐藏字段。

InnoDB的内部实现中为每一行数据增加了三个隐藏列用于实现MVCC。

0a92937288032b139e67305066b76098.png

2、Read VIew

在innodb 中每个SQL语句执行前都会得到一个read_view副本,该结副本主要保存了当前数据库系统中正处于活跃(没有commit)事务的ID号,其实就是说这个副本中保存的是系统中当前不应该被本事务看到的其他活跃事务id列表

readView几个重要属性:

  • trx_ids:当前系统活跃事务版本号集合。
  • low_limit_id:创建read view 时当前系统活跃的事务最大版本号。
  • up_limit_id:创建read view 时当前系统活跃的事务最小版本号。
  • creator_trx_id:创建当前read view的事务版本号。

Read view 返回数据条件:

  • 数据事务ID小于up_limit_id(活跃事务的最小ID),则说明该数据是在所有活跃事务开启之前就已经存在的,可以显示。
  • 数据事务ID大于low_limit_id(活跃事务的最大ID),则说明该数据是在所有活跃事务开始之后才创建的,所以数据不予显示。
  • 数据事务ID 大于up_limit_id 并且小于low_limit_id 时,用数据事务ID 与trx_ids 集合中的事务ID进行匹配,如果事务ID不存在于活跃事务ID集合,或者数据事务ID等于creator_trx_id(这说明该数据就是当前事务修改的),满足上面的条件则可以显示。
  • 当数据的事务ID不满足read view条件时候,就会从undo log里面获取数据的历史版本,然后数据历史版本事务号回头再来和read view 条件匹配 ,直到找到一条满足条件的历史数据,或者找不到则返回空结果;

3、MVCC的名词

mysql的MVCC是通过保存两个隐藏的版本字段数据创建版本号、数据删除版号来区分数据历史版本和数据的可见性的。

MVCC里面有几个名词需要知道:

3.1 Undo log

undo log是回滚日志,其作用:提供回滚和多个行版本控制(MVCC)。

undo log用来回滚行记录到某个版本。undo log一般是逻辑日志,根据每行记录进行记录。

在数据修改的时候,不仅记录了redo,还记录了相对应的undo,如果因为某些原因导致事务失败或回滚了,可以借助该undo进行回滚。

undo log和redo log记录物理日志不一样,它是逻辑日志。可以认为当delete一条记录时,undo log中会记录一条对应的insert记录,反之亦然,当update一条记录时,它记录一条对应相反的update记录

当执行rollback时,就可以从undo log中的逻辑记录读取到相应的内容并进行回滚。有时候应用到行版本控制的时候,也是通过undo log来实现的:当读取的某一行被其他事务锁定时,它可以从undo log中分析出该行记录以前的数据是什么,从而提供该行版本信息,让用户实现非锁定一致性读取。

undo log是采用段(segment)的方式来记录的,每个undo操作在记录的时候占用一个undo log segment。

另外,undo log也会产生redo log,因为undo log也要实现持久性保护。

innodb存储引擎对undo的管理采用段的方式。rollback segment称为回滚段,每个回滚段中有1024个undo log segment。

3.2 Redo log

redo log是前滚日志,提供前滚操作。

redo log通常是物理日志,记录的是数据页的物理修改,而不是某一行或某几行修改成怎样怎样,它用来恢复提交后的物理数据页(恢复数据页,且只能恢复到最后一次提交的位置)。

redo log包括两部分:

    • 一是内存中的日志缓冲(redo log buffer),该部分日志是易失性的;
    • 二是磁盘上的重做日志文件(redo log file),该部分日志是持久的。

[“redo log 跟二进制文件的区别”摘自

详细分析MySQL事务日志(redo log和undo log)”]


redo log不是二进制日志。虽然二进制日志中也记录了innodb表的很多操作,也能实现重做的功能,但是它们之间有很大区别,以下是区别:
1. 二进制日志是在存储引擎的上层产生的,不管是什么存储引擎,对数据库进行了修改都会产生二进制日志。而redo log是innodb层产生的,只记录该存储引擎中表的修改。并且二进制日志先于redo log被记录。具体的见后文group commit小结。
2. 二进制日志记录操作的方法是逻辑性的语句。即便它是基于行格式的记录方式,其本质也还是逻辑的SQL设置,如该行记录的每列的值是多少。而redo log是在物理格式上的日志,它记录的是数据库中每个页的修改。
3. 二进制日志只在每次事务提交的时候一次性写入缓存中的日志"文件"。而redo log在数据准备修改前写入缓存中的redo log中,然后才对缓存中的数据执行修改操作;而且保证在发出事务提交指令时,先向缓存中的redo log写入日志,写入完成后才执行提交动作。
4. 因为二进制日志只在提交的时候一次性写入,所以二进制日志中的记录方式和提交顺序有关,且一次提交对应一次记录。而redo log中是记录的物理页的修改,redo log文件中同一个事务可能多次记录,最后一个提交的事务记录会覆盖所有未提交的事务记录。例如事务T1,可能在redo log中记录了 T1-1,T1-2,T1-3,T1* 共4个操作,其中 T1* 表示最后提交时的日志记录,所以对应的数据页最终状态是 T1* 对应的操作结果。而且redo log是并发写入的,不同事务之间的不同版本的记录会穿插写入到redo log文件中,例如可能redo log的记录方式如下: T1-1,T1-2,T2-1,T2-2,T2*,T1-3,T1* 。
5. 事务日志记录的是物理页的情况,它具有幂等性,因此记录日志的方式极其简练。幂等性的意思是多次操作前后状态是一样的,例如新插入一行后又删除该行,前后状态没有变化。而二进制日志记录的是所有影响数据的操作,记录的内容较多。例如插入一行记录一次,删除该行又记录一次。

redo log 与 undo log的 主要区别:
● undo日志:在恢复时取消未完成的事物的影响,忽略已提交事物。
Redo日志:忽略未完成的事物,重做已提交事物的改变。
● undo日志:先将修改后的数据写到磁盘——写<COMMIT T>到磁盘。
Redo日志:先写<COMMIT T>到磁盘——将修改后的数据写到磁盘。
● 当遵循Undo日志的U1和U2规则,恢复时我们所需要的是数据库的旧值。
使用Redo日志恢复时,我们需要的是新值。

4、事物对某行记录的更改

4.1. 初始数据行

ab36ce40f7729e0fb5615e47d0ed970f.png

F1~F6是某行列的名字,1~6是其对应的数据。后面三个隐含字段分别对应该行的事务号和回滚指针,假如这条数据是刚INSERT的,可以认为ID为1,其他两个字段为空。

4.2.事务1更改该行的各字段的值

706c1a8dbb76f308df7c00643049a3b0.png

当事务1更改该行的值时,会进行如下操作:

  • 用排他锁锁定该行
  • 记录redo log
  • 把该行修改前的值Copy到undo log,即上图中下面的行
  • 修改当前行的值,填写事务编号,使回滚指针指向undo log中的修改前的行

4.3.事务2修改该行的值

37a4a05639c7fe173bbe12bd57f89e8a.png

与事务1相同,此时undo log,中有有两行记录,并且通过回滚指针连在一起。

因此,如果undo log一直不删除,则会通过当前记录的回滚指针回溯到该行创建时的初始内容,所幸的时在Innodb中存在purge线程,它会查询那些比现在最老的活动事务还早的undo log,并删除它们,从而保证undo log文件不至于无限增长。

4.4. 事务提交

当事务正常提交时Innbod只需要更改事务状态为COMMIT即可,不需做其他额外的工作,而Rollback则稍微复杂点,需要根据当前回滚指针从undo log中找出事务修改前的版本,并恢复。如果事务影响的行非常多,回滚则可能会变的效率不高,根据经验值没事务行数在1000~10000之间,Innodb效率还是非常高的。很显然,Innodb是一个COMMIT效率比Rollback高的存储引擎。据说,Postgress的实现恰好与此相反。

4.5. Insert Undo log

上述过程确切地说是描述了UPDATE的事务过程,其实undo log分insert和update undo log,因为insert时,原始的数据并不存在,所以回滚时把insert undo log丢弃即可,而update undo log则必须遵守上述过程。

5、MVCC的工作过程

MVCC只在READ COMMITED 和 REPEATABLE READ 两个隔离级别下工作。READ UNCOMMITTED总是读取最新的数据行,而不是符合当前事务版本的数据行。而SERIALIZABLE 则会对所有读取的行都加锁

  1. 事务开启,获得一个系统的事务版本号。
  2. 获得一个当前事务可视版本read view副本。
  3. 根据SQL查询出符合SQL条件的数据。
  4. 根据数据版本号和read view 中的条件匹配,符合则返回,不符合则从undo log寻找到合适的历史版本数据返回。


注意:

如果根据事务DB_TRX_ID去比较获取事务的话,按道理在一个事务B(在事务A后,但A还没commit)select的话 B.DB_TRX_ID>A.DB_TRX_ID则应该能返回A事务对数据的操作以及修改。那不是和前面矛盾?其实不然。

InnoDB每个事务在开始的时候,会将当前系统中的活跃事务列表(trx_sys->trx_list)创建一个副本(read view),然后一致性读去比较记录的tx id的时候,并不是根据当前事务的tx id,而是根据read view最早一个事务的tx id(read view->up_limit_id)来做比较的,这样就能确保在事务B之前没有提交的所有事务的变更,B事务都是看不到的。当然,这里还有个小问题要处理一下,就是当前事务自身的变更还是需要看到的。

参考网站:

详细分析MySQL事务日志(redo log和undo log)​www.cnblogs.com
42c2eb7b2c8a347467d364b5956add75.png

MySQL中InnoDB的多版本并发控制(MVCC):https://www.jianshu.com/p/a3d49f7507ff

MVCC实现方式:http://www.360doc.com/content/14/0821/09/12904276_403505950.shtml

MySQL的并发控制与加锁分析:https://www.cnblogs.com/yelbosh/p/5813865.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值