首页 > 编程知识 正文

mysql使用MVCC来解决幻读,mysql间隙锁与mvcc

时间:2023-05-04 05:28:58 阅读:166589 作者:2574

MVCC

同时控制多个版本。 MVCC是一种并发控制的方法,一般在数据库管理系统中提供对数据库的并发访问。

为什么需要MVCC呢? 数据库通常使用锁进行隔离。 最本土的摇滚。 锁定一个资源将阻止其他线程访问同一资源。 但是,许多APP应用的特征之一是读写较少的场景,许多数据的读取次数远远大于修正次数,不需要在读取数据之间进行排斥。 因此,我们使用的是读/写锁,而不是读/写锁之间的互斥,而是写锁和写锁之间的互斥。 这样可以大大提高系统的并发性。 后来发现同时阅读还不够,提出了避免读写之间发生冲突的方法。 这意味着,通过在读取数据时以类似快照的方式存储数据,读锁定和写锁定之间不会发生冲突,不同的事务session可以看到自己特定版本的数据。 当然,快照是一个概念模型,在不同的数据库中可能以不同的方式工作。

InnoDB和MVCC

MVCC仅在两个隔离级别上运行:读命令和读命令。 其他两个隔离级别与MVCC的兼容性不够。 因为READ UNCOMMITTED总是读取最新的数据行,而不是与当前事务版本相匹配的数据行。 可序列化锁定所有读取的行。

重做log,wjdqc log,还原log

在InnoDB中通过undo log实现数据的多个版本,同时控制通过锁定实现。

还原日志除了实现MVCC外,还用于回滚事务。 MySQL Innodb中存在许多类型的日志,包括错误日志、查询日志以及许多与数据持久性和一致性相关的日志。

wjdqclog是mysql服务器层生成的日志,常用于数据恢复、数据库复制。 常见的mysql主从体系结构由采用slave同步master的wjdqclog实现,并通过分析wjdqclog实现从mysql到其他数据源(如ElasticSearch )的数据复制

重做日志记录数据操作在物理级别的修改。 mysql使用了大量的缓存,缓存存在于内存中。 修改操作时,会直接修改内存,而不是立即修改磁盘。 如果内存和磁盘数据不匹配,则内存中的数据称为脏页。 为了确保数据的安全,在事务的进行过程中会不断发生重做日志,在提交事务时进行一次闪存操作并保存到磁盘。 重做日志按顺序写入,磁盘顺序的读写远远快于随机读写。 当数据库或主机过期并重新启动时,将根据重做日志恢复数据,如果重做日志中存在事务提交,则提交事务并修改数据。 这将确保事务的原子性、一致性和持久性。

除了重做日志外,还原日志:还会在修改数据时记录还原日志。 还原日志用于撤回数据操作,例如,可以插入相应的删除,将相应的修改更改为原始数据,或者还原日志可以回滚事务处理,并可以基于还原日志返回到特定版本的数据

版本链和还原日志

在innodb中将b树作为索引的数据结构,主键所在的索引是ClusterIndex ),在ClusterIndex的叶节点保存着对应的数据内容。 因为表只有一个主键,所以只有一个聚簇索引。 如果表没有定义主键,请选择第一个非空唯一索引作为簇索引,如果尚未存在,请生成隐藏的id列作为簇索引。

Cluster Index以外的索引是Secondary Index。 辅助索引的叶节点保存聚类索引的叶节点的值。

除了上述rowid外,InnoDB行记录还包括trx_id和db_roll_ptr。 trx_id表示最近修改的事务处理的id,而db_roll_ptr指向还原消息的还原日志。

添加事务会增加事务id,trx_id可以表示事务开始的优先级。

Undo log有Insert和Update两种,delete可以看作是在记录上修正删除标记的特殊Update。

update undo log包含数据之前的数据信息,可以通过这些信息恢复到以前版本的状态。

插入操作允许在提交事务后删除生成的Insert undo log。 这是因为其他事务不需要此还原日志。

删除修改操作时,将生成相应的还原日志,并且当前数据记录的db_roll_ptr指向新的还原日志。

读我

还原日志结束后,让我们看看自述。 提交读取与可重复读取的区别在于生成ReadView的策略不同。

ReadView提供了一个列表,其中主要存储系统上当前活动的读/写事务,即begin未提交的事务。 使用此列表可以确定当前事务中是否显示了记录的版本。 主要可见性相关属性如下:

up_limit_id :当前提交的事务编号1,事务编号up_limit_id显示给当前读视图。 了解到,创建读视图时,以前提交的事务对该事务是肯定的

可见的。

low_limit_id:当前最大的事务号 + 1,事务号 >= low_limit_id,对于当前Read View都是不可见的。理解起来就是在创建Read View视图之后创建的事务对于该事务肯定是不可见的。

trx_ids:为活跃事务id列表,即Read View初始化时当前未提交的事务列表。所以当进行RR读的时候,trx_ids中的事务对于本事务是不可见的(除了自身事务,自身事务对于表的修改对于自己当然是可见的)。理解起来就是创建RV时,将当前活跃事务ID记录下来,后续即使他们提交对于本事务也是不可见的。

用一张图更好的理解一下:

最后我们来举个例子让我们更好理解上面的内容。

比如我们有如下表:

现在有一个事务id是60的执行如下语句并提交:

update user set name = 'ssdwt1' where id = 1;

此时undo log存在版本链如下:

提交事务id是60的记录后,接着有一个事务id为100的事务,修改name=ssdwt2,但是事务还没提交。则此时的版本链是:

此时另一个事务发起select语句查询id=1的记录,因为trx_ids当前只有事务id为100的,所以该条记录不可见,继续查询下一条,发现trx_id=60的事务号小于up_limit_id,则可见,直接返回结果ssdwt1。

那这时候我们把事务id为100的事务提交了,并且新建了一个事务id为110也修改id为1的记录name=ssdwt3,并且不提交事务。这时候版本链就是:

这时候之前那个select事务又执行了一次查询,要查询id为1的记录。

如果你是已提交读隔离级别READ_COMMITED,这时候你会重新一个ReadView,那你的活动事务列表中的值就变了,变成了[110]。按照上的说法,你去版本链通过trx_id对比查找到合适的结果就是ssdwt2。

如果你是可重复读隔离级别REPEATABLE_READ,这时候你的ReadView还是第一次select时候生成的ReadView,也就是列表的值还是[100]。所以select的结果是ssdwt1。所以第二次select结果和第一次一样,所以叫可重复读!

也就是说已提交读隔离级别下的事务在每次查询的开始都会生成一个独立的ReadView,而可重复读隔离级别则在第一次读的时候生成一个ReadView,之后的读都复用之前的ReadView。

这就是Mysql的MVCC,通过版本链,实现多版本,可并发读-写,写-读。通过ReadView生成策略的不同实现不同的隔离级别。

参考:

关注公众号获取更多内容,有问题也可在公众号提问哦:

ssdwt叨逼叨

叨逼叨编程、互联网的见解和新鲜事

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。