MySQL并发利器多版本控制器MVCC

Wesley13
• 阅读 642

    MySQl大多数事务性存储引擎实现的都不是简单的行级锁。基于高性能考虑,他们一般都同时是想了多版本并发控制器(MVCC)。不仅仅MySQL,包括Oracle、PostgreSQL等其他数据库系统也都实现了MVCC,但各自实现机制不尽相同,因为MVCC没有一个统一的实现标准。MVCC可以说是行级锁的一个变种,但是他在多数情况下避免了加锁操作,因此开销更低。虽然实现机制有所不同,但大都实现了非阻塞的读操作,写操作也只锁定必要的行。

MVCC只存在于MySQL的Read Commit和Read Repeatable的隔离级别下。

事务日志

    事务日志可以帮助提高事务的效率。使用事务日志,存储引擎在修改表的数据时只需要修改其内存拷贝,再把该修改行为记录到持久在硬盘上的事务日志中,而不用每次都将修改的数据本身持久到磁盘。事务日志采用的是追加的方式,因此写日志的操作是磁盘上一小块区域内的顺序 I/O,而不像随机 I/O需要在次盘的多个地方移动磁头,所以采用事务日志的方式相对来说要快得多。事务日志持久以后,内存中被修改的数据在后台可以慢慢的刷回到磁盘。目前大多数存储引擎都是这样实现的,我们通常称之为预写式日志(Write-Ahead Logging),修改数据需要写两次磁盘。
    redo log 是InnoDB存储引擎层的日志,主要用于记录事务的日志信息,开启一个事务时,会记录一个日志序列号,当事务执行时会向日志缓冲(redo buffer)插入事务日志,并且在事务提交前会把redo buffer中的日志信息记录到磁盘中。如数据库掉电,InnoDB存储引擎会使用redo log恢复到掉电前的时刻,以此来保证数据的完整性。
    undo log 是InnoDB存储引擎层的日志,主要用于记录数据被修改之前的日志,在表信息修改之前先会把数据拷贝到undo log 里,当事务进行回滚时可以通过undo log 里的日志进行数据还原。
    undo log用于MVCC快照读的数据,在MVCC多版本控制中,通过读取undo log的历史版本数据可以实现不同事务版本号都拥有自己独立的快照数据版本。有时候应用到行版本控制的时候,也是通过undo log来实现的:当读取的某一行被其他事务锁定时,它可以从undo log中分析出该行记录以前的数据是什么,从而提供该行版本信息,让用户实现非锁定一致性读取。

InnoDB是一个多版本的存储引擎:它保存有关已更改行的旧版本的信息,以支持并发和回滚等事务功能 。此信息存储在表空间中称为回滚段的数据结构中(在Oracle中的类似数据结构之后)。InnoDB 使用回滚段中的信息来执行事务回滚中所需的撤消操作。它还使用该信息构建行的早期版本以进行一致读取。
    在内部,InnoDB为数据库中存储的每一行添加三个字段。6字节DB_TRX_ID字段指示插入或更新行的最后一个事务的事务标识符。此外,删除在内部被视为更新,其中行中的特殊位被设置为将其标记为已删除。每行还包含一个DB_ROLL_PTR称为滚动指针的7字节 字段。roll指针指向写入回滚段的undo log记录。如果更新了行,则undo log记录包含在更新行之前重建行内容所需的信息。一个6字节的DB_ROW_ID字段包含一个行ID,当插入新行时,该行ID会单调增加。如果 InnoDB自动生成聚簇索引,索引包含行ID值。否则,该 DB_ROW_ID列不会出现在任何索引中。
    撤消段中的undo log分为插入和更新undo log。只在事务回滚中才需要插入undo log,并且可以在事务提交后立即丢弃。更新undo log也用于一致性读取,但只有在没有事务InnoDB已分配快照的情况下才能丢弃它们 ,在一致读取中可能需要更新undo log中的信息来构建数据库的早期版本行。 在InnoDB多版本控制方案中,使用SQL语句删除行时,不会立即从数据库中物理删除该行。InnoDB只有在丢弃为删除写入的更新undo log记录时,才会物理删除相应的行及其索引记录。此删除操作称为purge,并且速度非常快,通常与执行删除的SQL语句的时间顺序相同。

  • DB_TRX_ID:记录操作该数据事务的事务ID;
  • DB_ROLL_PTR:指向上一个版本数据在undo log 里的位置指针;
  • DB_ROW_ID: 隐藏ID ,当创建表没有合适的索引作为聚集索引时,会用该隐藏ID创建聚集索引;

read view
    read view 其实就是一个保存事务ID的list列表。记录的是本事务执行时,MySQL还有哪些事务在执行。
    Read Repeatable 对应的是在每个事务启动的时候创建一个read view。
    Read Commit 对应的是每次执行SQL statement时候创建一个read view。

  • Read View结构

    struct read_view_t{ // 由于是逆序排列,所以low/up有所颠倒 // 能看到当前行版本的高水位标识,> low_limit_id皆不能看见 trx_id_t low_limit_id; // 能看到当前行版本的低水位标识,< up_limit_id皆能看见 trx_id_t up_limit_id; // 当前活跃事务(即未提交的事务)的数量 ulint n_trx_ids; // 以逆序排列的当前获取活跃事务id的数组 // 其up_limit_id<tx_id<low_limit_id trx_id_t* trx_ids;
    // 创建当前视图的事务id trx_id_t creator_trx_id; // 事务系统中的一致性视图链表 UT_LIST_NODE_T(read_view_t) view_list; };

  • 版本可见性

read view其实保存的是当前活跃事务的所有事务id,如果当前行版本对应修改的事务id不在当前活跃事务里面的话,表示当前版本可见,否则就是不可见。也就是看不到read view创建以后启动的事务,看不到read view创建时活跃的事务。Read View不可见的话,就从undo log中读取。

只有在非锁select下才会创建read view。

当前读和快照读

  • 当前读

    当前读是读取的数据库最新的数据,当前读和快照读不同,因为要读取最新的数据而且要保证事务的隔离性,所以当前读是需要对数据进行加锁的 (Update、    delete、    insert、   select ....lock in share mode、   select for update 为当前读)

  • 快照读

    快照读是指读取数据时不是读取最新版本的数据,而是基于历史版本读取的一个快照信息(mysql读取undo log历史版本) ;
快照读可以使普通的SELECT 读取数据时不用对表数据进行加锁,从而解决了因为对数据库表的加锁而导致的两个如下问题:

  1.     解决了因加锁导致的修改数据时无法对数据读取问题;
  2.     解决了因加锁导致读取数据时无法对数据进行修改的问题;
点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
2年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
Python进阶者 Python进阶者
4个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这