首页 > 数据库 >MySQL MVCC

MySQL MVCC

时间:2023-03-24 23:12:06浏览次数:45  
标签:事务 快照 Read MVCC MySQL ID View

目录

前言

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

MVCC在MySQL InnoDB中的实现主要是为了提高数据库并发性能,用更好的方式去处理读-写冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读。

共享锁和排它锁

  • 共享锁(S锁)

    用于不更改不更新数据的操作(只读操作),例如:SELECT 语句。

    如果事务 T 仅对数据 A 进行读取,那么会对数据 A 加上共享锁,之后则其他事务如果要读取数据 A 的话可以对其继续加共享锁,但是不能加排他锁(也就是无法修改数据)。获准共享锁的事务只能读数据,不能修改数据。

  • 排他锁(X锁)

    用于数据修改操作,例如:INSERT、UPDATE 或 DELETE 语句。确保不会同时同一资源进行多重更新。

    如果事务 T 对数据 A 要进行修改,则需要对其添加排它锁,加上排他锁后,则其他事务不能再对 A 加任任何类型的封锁。获准排他锁的事务既能读数据,又能修改数据。

什么是当前读和快照读?

当前读

当前读:每次读取的都是数据库记录的最新版本,会对当前读取的数据进行加锁,防止其他事务修改数据,是悲观锁的一种操作。

像select xx from t_xx lock in share mode(共享锁)、select for update、update、insert、delete(排他锁)这些操作都是一种当前读。

因为,它读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,因此,会对读取的记录进行加锁。

以下几种情况都是当前读:

  • select 语句加锁

    # 共享锁
    select name from t_user where id = 1 lock in share mode;
    # 排他锁
    select name from t_user where id = 1 for update;
    
  • update、insert、delete 语句

    # 排他锁
    update t_user set a = a + 1 where id = 1;
    

快照读

快照读:读写不冲突,每次读取的都是快照数据

像不加锁的select操作就是快照读,即不加锁的非阻塞读。快照读的前提是隔离级别不是串行级别,串行级别下的快照读会退化成当前读。

之所以出现快照读的情况,是基于提高并发性能的考虑,快照读的实现是基于多版本并发控制,即MVCC,可以认为MVCC是行锁的一个变种,但它在很多情况下,避免了加锁操作,降低了开销。

因为它是基于多版本实现,因此,快照读可能读到的并不一定是数据的最新版本,而有可能是之前的历史版本,简而言之,MVCC 就是为了实现读-写冲突不加锁,而这个读指的就是快照读。

以下几种情况都是快照读:

  • 在RR隔离级别下,不加锁的select语句:
    # select 语句
    select name from t_user where id = 1;
    

注意:

  • 隔离级别为 Repeatable Read 时:有可能读取的不是最新的数据;
  • 隔离级别为 Read Committed 时:快照读和当前读读取的数据是一样的,都是最新的。

MVCC

多版本并发控制(multiversion concurrency control,MVCC)是一种用来解决读-写冲突的无锁并发控制技术。它在RC和RR隔离级别下,为事务分配单向增长的时间戳,为每个修改保存一个版本,版本与事务时间戳关联,读操作只读该事务开始前的数据库的快照。

所以,MVCC可以为数据库解决以下问题在并发读写数据库时,可以做到在读操作时不用阻塞写操作,写操作也不用阻塞读操作,提高了数据库并发读写的性能 同时还可以解决脏读,幻读,不可重复读等事务隔离问题,但不能解决更新丢失问题。

解决问题如下:

  • 并发读-写时:可以做到读操作不阻塞写操作,同时写操作也不会阻塞读操作。

  • 解决脏读、幻读、不可重复读等事务隔离问题,但不能解决写-写更新丢失问题。因此需要下面提高并发性能的组合:

    • MVCC + 悲观锁:MVCC解决读写冲突,悲观锁解决写写冲突
    • MVCC + 乐观锁:MVCC解决读写冲突,乐观锁解决写写冲突

InnoDB引擎每行除了包含我们的定义的字段外,还会额外包含三个隐式字段:DB_TRX_IDDB_ROLL_PTRDB_ROW_ID

字段 名称 长度 功能
DB_TRX_ID 事务ID 6字节 插入或更新该行的最后一个事务的事务标识符。此外,删除在内部被视为更新,其中行中的特殊位被设置为将其标记为已删除。
DB_ROLL_PTR 回滚指针 7字节 回滚指针指向一条写入回滚段的撤销日志记录。如果该行已更新,则撤消日志记录包含在更新之前重建该行内容所需的信息。
DB_ROW_ID 隐藏自增主键 6字节 如果数据表没有主键,InnoDB会自动以DB_ROW_ID产生一个聚簇索引

官方文档的原文如下:

Internally, InnoDB adds three fields to each row stored in the database:

  • A 6-byte DB_TRX_ID field indicates the transaction identifier for the last transaction that inserted or updated the row. Also, a deletion is treated internally as an update where a special bit in the row is set to mark it as deleted.

  • A 7-byte DB_ROLL_PTR field called the roll pointer. The roll pointer points to an undo log record written to the rollback segment. If the row was updated, the undo log record contains the information necessary to rebuild the content of the row before it was updated.

  • A 6-byte DB_ROW_ID field contains a row ID that increases monotonically as new rows are inserted. If InnoDB generates a clustered index automatically, the index contains row ID values. Otherwise, the DB_ROW_ID column does not appear in any index.

以下面的例子为例:

alt MVCC

Read View

Read View 就是事务进行快照读操作的时候生产的读视图,在该事务执行的快照读的那一刻,会生成数据库系统当前的一个快照,记录并维护系统当前活跃事务的 ID (当每个事务开启时,都会被分配一个 ID , 这个 ID 是递增的,所以最新的事务,ID 值越大)。

Read View 主要是用来做可见性判断的, 即当我们某个事务执行快照读的时候,对该记录创建一个 Read View 读视图,把它比作条件用来判断当前事务能够看到哪个版本的数据,即可能是当前最新的数据,也有可能是该行记录的 undo log 里面的某个版本的数据。

对于使用 read-committed 和 repeatable-read 隔离级别的事务来说,都必须保证读到已经提交了的事务修改过的记录,也就是说假如另一个事务已经修改了记录但是尚未提交,是不能直接读取最新版本的记录的。

我们可以把Read View简单的理解成有三个全局属性:

  • trx_list:未提交事务ID列表,用来维护Read View生成时刻系统正活跃的事务ID;
  • up_limit_id:记录trx_list列表中事务ID最小的ID;
  • low_limit_id:Read View生成时刻系统尚未分配的下一个事务ID,也就是目前已出现过的事务ID的最大值+1。

流程

Read Veiw的生成流程如下:

alt Read View

以下面的例子,来说明:

RR、RC生成Read View的时机

RR、RC生成Read View的时机:

  • RC隔离级别下,每个快照读都会生成并获取最新的Read View,因此可能出现在同一个事务中两次查询的结果不一致的情况;
  • RR隔离级别下,则是同一个事务中的第一个快照读才会创建Read View,之后的快照读获取的都是同一个Read View,之后的查询就不会重复生成了,所以同一一个事务的查询结果每次都是一样的。

总结

参考:

标签:事务,快照,Read,MVCC,MySQL,ID,View
From: https://www.cnblogs.com/larry1024/p/17244802.html

相关文章

  • MySQL主从复制
    原理   主从复制相关线程主节点:dumpThread:为每个Slave的I/OThread启动一个dump线程,用于向其发送binarylogevents从节点:I/OThread:向Master请求二进制日......
  • MySQL数据库备份与恢复
    一,备份,恢复为什么要备份灾难恢复:硬件故障、软件故障、自然灾害、黑客攻击、误操作测试等数据丢失场景参考链接:https://www.toutiao.com/a6939518201961251359/ ......
  • mysql如何优雅删除大表? 看这篇就够了
    MySQL大表删除有次线上用droptablexxx删除200G的大表,导致MySQL连接数暴涨,业务出现大量5XX,“喜提”一个事故报告。看来还是忽略了一行命令背后产生的“蝴蝶效应”,现在让......
  • 力扣570(MySQL)-至少有5名直接下属的经理(简单)
    题目:Employee表包含所有员工和他们的经理。每个员工都有一个Id,并且还有一列是经理的Id。 给定Employee表,请编写一个SQL查询来查找至少有5名直接下属的经理。对于......
  • Winform/Csharp中连接Mysql并执行指定sql,查询结果通过反射映射为对象的List
    场景Winform中连接Mysql8并查询表中数据进行显示:https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/120395988在上面连接Mysql的基础上,如果需要连接Mysql并执......
  • mysql 订单产品表,一个订单对应多个产品,查询只包含产品A001,A002,A003的订单
    假设订单产品表的表名为order_product,包含字段如下:order_id:订单IDproduct_id:产品ID下面是查询只包含产品A001,A002,A003的订单的SQL语句:SELECTorder_idFROMorder......
  • 在docker上安装MySQL和Redis
    1.通过docker命令下载mysql5.7镜像mysql5.7前期准备2.通过docker命令下载mysql8.0镜像mysql8.0前期准备3.通过docker命令下载redis镜像redis前期准备本文永......
  • MYSQL注入流程
    一、判断注入点(与数据库有交互的地方)浏览网站各个地方-->查找有可控参数的地方(特别注意url)并观察网站页面变化情况  根据参数传输方式传输的不同,可以把SQL注入分为GE......
  • python stata转mysql
    importnumpyasnpimportpyreadstataspyreadstatimportjson,re,random,pymysql,configparser,sysimportpandasaspdfromduconfigimportread_inidefdujieg......
  • Centos7-tar包自定义安装mysql -ERROR 2002_ERROR 1045_ERROR 1054_ERROR 1290_ERROR
    @目录1.自定义安装mysql参考链接ERROR2002/ERROR1045/ERROR1054/ERROR12901.1、ERROR2002报错解决方法:1.2、ERROR1045报错解决方法:2.关于登录mys......