当前位置: 首页 > 科技观察

MySQL多版本并发控制机制(MVCC)源码分析

时间:2023-03-16 13:30:44 科技观察

前言作为一个数据库爱好者,自己写了一个简单的SQL解析器和存储引擎,但还是觉得不够过瘾。<>确实说的很透彻,但是也只能勾勒出大概的轮廓,不能让你把玩真正的数据库。感谢cmake,让我可以在mac上使用xcode调试MySQL,让我领略到它的各种实现细节。笔者一直对数据库的隔离感到好奇。这篇博客是我调试MySQL过程中的偶然收获。(注:本文中的MySQL使用的是MySQL-5.6.35版本)MVCC(多版本并发控制机制)隔离也可以称为并发控制、序列化等,说到并发控制,首先想到的是是锁。MySQL通过使用两级锁来实现更新的序列化。同时,为了加快查询性能,它采用了MVCC(多版本并发控制)机制,实现了无锁使用。获得一致的版本。可重复读MySQL通过MVCC和(Next-KeyLock)实现可重复读(RepeatableRead)。它的思想(MVCC)是记录数据的版本变化,通过精心选择不同的数据版本,将一致的结果呈现给用户。.如下图:上图中,(A=50|B=50)的初始版本为1。1.事务t1选择A时看到的版本为1,即A=502。事务t2对A和B的修改将版本升级为2,即A=0,B=1003。事务t1选择B时看到的版本还是1,即B=50,隔离了版本的影响,A+B一直是100。ReadCommit如果不通过版本控制机制,而是读取最新提交的结果,隔离级别为readcommit,如下图:这种情况下,需要使用锁机制(比如selectforupdate)对A和B记录进行加锁,以获得正确一致的结果,如下图:对齐,并且你不想添加一个消耗大量性能的锁。这时候一致版本的MVCC就有了很大的优势。MVCC(实现机制)这一节开始讲MVCC的实现机制。注意MVCC只对纯select有效(不包括selectforupdate、lockinsharemode、updateinsert等锁操作)。Selectrunningstack首先我们来跟踪下mysql源码中一条普通查询sql的运行过程,sql为(select*fromtest);它的运行栈是:由于mysql默认的隔离级别是repeatable_read(RR),所以read_record被重载为rr_sequential(目前我们不关心按索引扫描然后按条件过滤的选择行的过程)。继续跟踪:我们看一下这个函数的内部:boollock_clust_rec_cons_read_sees(constrec_t*rec/*innodb扫描的一行*/,....){...//从当前扫描的行中获取其***修改trx_id(事务id)的版本trx_id=row_get_rec_trx_id(rec,index,offsets);//通过参数(一致的快照视图和事务id)来确定快照看到的行return(read_view_sees_trx_id(view,trx_id));}read_view在创建过程中,首先关注一致视图的创建过程。我们先看read_view结构体:然后通过调试发现read_view结构体的创建也是在上面的rr_sequential中操作的,继续跟踪调用栈:我们看row_search_for_mysql中的一个分支:row_search_for_mysql://这里只有当select不加锁模式时才会创建一致视图elseif(prebuilt->select_lock_type==LOCK_NONE){//创建一致视图trx_assign_read_view(trx);prebuilt->sql_stat_start=FALSE;}上面的注释是为什么selectfor更新(在共享模型中)将不会遵循MVCC。下面进一步分析trx_assign_read_view函数:OK,终于到了创建read_view的主要阶段,主要流程如下图所示:read_view_sees_trx_id函数:其实上面的函数是二分法。read_view实际上保存了当前活跃事务的所有事务id。如果当前行版本修改的事务id不在当前活动事务中,则返回true,表示当前版本可见,否则不可见。如下所示。继上面lock_clust_rec_cons_read_sees:undolog查找可见版本的过程的返回,我们再来看看row_sel_build_prev_vers_for_mysql函数:row_sel_build_prev_vers_for_mysql|-row_vers_build_for_consistent_read主要调用row_ver_build_for_consistent_read方法返回可见版本:如何恢复undolog对应行整个过程:又是一个复杂的过程,限于篇幅这里不再赘述。read_view创建的时机将再次讨论。在row_search_for_mysql创建一致视图的代码中,trx_assign_read_view中有这么一段代码。因此,将两段代码结合起来,即在一个事务中,只有在第一次运行select(不带锁)的时候。创建一个一致的视图,如下图所示:作者构建了这种场景并进行了模拟,确实如此。MVCC和锁同时作用导致的一些现象MySQL使用MVCC和两级锁(2PL)来兼顾性能和一致性,但是由于MySQL只在select的时候创建一致的view,锁update等操作有时这个操作是没有做,所以会出现一些奇怪的现象。如下图:如果理解update不走一致视图(read_view),而select走一致视图(read_view),这个现象就很好解释了。如下图所示:总结MySQL为了平衡性能和ACID,使用了大量复杂的机制。2PL(两级锁)和MVCC是其实现的典型例子。幸运的是,可以通过xcode等IDE进行方便的调试,从而可以非常准确方便地跟踪各种机制的实现。希望本文能对喜欢研究MySQL源码的读者有所帮助。