mysql mvcc limit_MySQL事务与MVCC

本文记录下了作者整理MySQL事务相关知识点时的一些思考和总结,厘清了过去一些模棱两可的知识点。希望本文对大家理解MySQL有所帮助。

MySQL事务隔离级别

隔离程度从弱到强依次是:读未提交(read uncommitted) < 读已提交(read committed) < 可重复读(repeatable read) < 串行读(serializable read)

更强的隔离级别能在低级别的隔离级别基础上,提供更强的隔离保证。而不是说这种隔离级别提供这种能力,那种隔离级别提供与之相对的能力。

SQL标准中定义,可重复读隔离级别,幻读问题仍可能存在。但是在InnoDB引擎Repeatable Read级别中,通过next key locking解决了幻读问题。

幻读(Phantom read)

定义:幻读是指一个事务在前后两次查询同一个范围的时候,后一次查询看到了前一次查询没有看到的行。

幻读说明:1)幻读特指看到了新增的行,如果是看到了更新之后的数据不叫幻读;2)在可重复读的事务隔离级别下,普通的查询是快照读,是不会看到别的事务插入的数据的,因此幻读只在当前读下才会发生;

幻读带来的问题:1)可重复读的语义被破坏了;2)数据一致性会有问题,也就是数据库中的数据和基于该数据库操作产生的binlog重放在备份库的数据会和原主库不一致;

幻读产生的原因:在并发操作时,数据库中的行锁只能锁住行,但是新插入记录这个动作是要更新记录之间的“间隙”;

间隙锁(Gap lock)

为了解决幻读问题,InnoDB引入了间隙锁(Gap lock),顾名思义,就是锁的两个值之间的间隙。

间隙锁锁定的区域:根据检索条件向左寻找最靠近检索条件的记录A,向右寻找最靠近检索条件的记录B,即锁定的间隙为(A,B);

InnoDB自动使用间隙锁的条件:1)必须在Repeatable Read隔离级别下;2)检索条件必须有索引(没有索引的话,MySQL会全表扫描,那样会锁定整张表所有记录,包括不存在的记录,此时其它事务不能修改不能删除不能添加)

多版本并发控制(MVCC)

多版本并发控制是一种技术概念,并没有统一的实现标准,其核心理念是不同的事务访问不同版本的数据快照,从而实现不同的事务隔离级别。当用户读取某条记录被其它事务占用时,当前事务可以通过Undo日志读取到该行之前的数据,以此实现非锁定读取。

MVCC只在Repeatable Read 和Read Committed两个事务隔离级别下工作。因为Read Uncommitted 总是读取数据行的最新值,而Serializable 则会对读取的数据行加锁。

从本质上讲,Read Committed隔离级别违背了事务原则中的“隔离性(I)”,因此是一种隔离程度比较弱的事务隔离级别。

Read Committed与Repeatable Read 的区别是:在开启事务后进行快照读时,Read Committed能读到最新提交的事务所做的修改;而Repeatable Read 只能读到在事务开启后第一次SELECT读操作之前提交的修改,因此是可重复读;

MySQL的InnoDB MVCC 的实现依赖:隐藏字段、Read View、Undo log

隐藏字段

InnoDB存储引擎在每行数据的后面添加了三个隐藏字段DB_TRX_ID(6字节):表示最近一次对本记录行作修改(insert | update)的事务ID。对于delete操作,InnoDB认为是一个update操作,会额外更新一个另外的删除位,将行表示为deleted,并非真正物理删除。

DB_ROLL_PTR(7字节):回滚指针,指向当前记录行的undo log信息

DB_ROW_ID(6字节):随着新行插入而单调递增的行ID。理解:当表没有主键或唯一非空索引时,innodb就会使用这个行ID自动产生聚簇索引。如果表有主键或唯一非空索引,聚簇索引就不会包含这个行ID了。这个DB_ROW_ID跟MVCC关系不大。

Read view

InnoDB支持的RC(Read Committed)和RR(Repeatable Read)隔离级别是利用consistent read view(一致读视图)方式支持的。 所谓consistent read view就是在某一时刻给事务系统trx_sys打snapshot(快照),把当时trx_sys状态(包括活跃读写事务数组)记下来,之后的所有读操作根据其事务ID(即trx_id)与snapshot中的trx_sys的状态作比较,以此判断read view对于事务的可见性。

Read view中保存的trx_sys状态主要包括low_limit_id:目前出现过的最大的事务ID+1,即下一个将被分配的事务ID。high water mark,大于等于view->low_limit_id的事务对于view都是不可见的。

up_limit_id:活跃事务列表trx_ids中最小的事务ID,如果trx_ids为空,则up_limit_id 为 low_limit_id。low water mark,小于view->up_limit_id的事务对于view一定是可见的

low_limit_no:trx_no小于view->low_limit_no的undo log对于view是可以purge的

rw_trx_ids:读写事务数组。Read View创建时其他未提交的活跃事务ID列表。意思就是创建Read View时,将当前未提交事务ID记录下来,后续即使它们修改了记录行的值,对于当前事务也是不可见的。注意:Read View中trx_ids的活跃事务,不包括当前事务自己和已提交的事务(正在内存中)

创建/关闭read view需要持有trx_sys->mutex,会降低系统性能,5.7版本对此进行优化,在事务提交时session会cache只读事务的 read view。下次创建read view 时,判断如果是只读事务并且系统的读写事务状态没有发生变化,即trx_sys的max_trx_id没有向前推进,而且没有新的读写事务产生,就可以重用上次的read view。

Undo log

虽然MVCC字面上是说具有多个版本的数据快照,但这并不意味着数据库必须拷贝数据,保存多份数据文件,这样会浪费大量的存储空间。

InnoDB通过事务的undo日志巧妙地实现了多版本的数据快照。 当用户读取某条记录被其它事务占用时,当前事务可以通过Undo日志读取到该行之前的数据,以此实现非锁定读取。

数据库的事务有时需要进行回滚操作,这时就需要对之前的操作进行undo。因此,在对数据进行修改时,InnoDB会产生undo log。当事务需要进行回滚时,InnoDB可以利用这些undo log将数据回滚到修改之前的样子。

根据行为的不同 undo log 分为两种 insert undo log和update undo log。

insert undo log: 是在 insert 操作中产生的 undo log。因为 insert 操作的记录只对事务本身可见,对于其它事务此记录是不可见的,所以 insert undo log 可以在事务提交后直接删除而不需要进行 purge 操作。

update undo log: 是 update 或 delete 操作中产生的 undo log,因为会对已经存在的记录产生影响,为了提供 MVCC机制,因此 update undo log 不能在事务提交时就进行删除,而是将事务提交时放到入 history list 上,等待 purge 线程进行最后的删除操作。

为了保证事务并发操作时,在写各自的undo log时不产生冲突,InnoDB采用回滚段的方式来维护undo log的并发写入和持久化。回滚段实际上是一种 Undo 文件组织方式。

Redo Log

用于保证事务的持久性。InnoDB中通过Force Log at commit机制实现事务的持久性,即当事务提交时,必须将该事务的所有日志写入到redo日志文件进行持久化。

redo 日志基本上是顺序写,在数据库运行时不需要对redo log的文件进行读取操作。而undo log则需要随机读写。

为了确保每次操作日志都写入redo log文件,在每次将redo log缓冲写入redo log文件后,innodb引擎都需要调用一次fsync操作。

Purge线程

为了实现InnoDB的MVCC机制,更新或者删除操作都只是设置一下旧记录的deleted_bit,并不真正将就记录删除。为了节省磁盘空间,InnoDB有专门的purge线程来清理deleted_bit为true的记录。purge线程自己也维护了一个read view,如果某个记录的deleted_bit为true,并且DB_TRX_ID相对于purge线程的read_view可见,那么这条记录一定是可以被安全清楚。

Read view与可见性比较算法

Read Committed与Repeatable Read隔离级别是利用 consistent read view(一致性视图)方式支持的,一致性视图就是在某个时刻给事务系统trx_sys打snapshot(快照),把当时的trx_sys状态(包括活跃读写事务数组)记下来,之后所有的读操作根据其事务ID(即trx_id)与snapshot中的trx_sys

只读事务(Read-Only transaction)

InnoDB通过如下两种方式来判断一个事务是否为只读事务

1)在InnoDB中通过 start transaction read only 命令来开启,只读事务是指在事务中只允许读操作,不允许修改操作。如果在只读事务中尝试对数据库做修改操作会报错,报错后该事务依然是只读事务,'ERROR 1792 (25006): Cannot execute statement in a READ ONLY transaction.'

2)autocommit 开关打开,并且语句是单条语句,并且这条语句是"non-locking" SELECT 语句,也就是不使用 FOR UPDATE/LOCK IN SHARED MODE 的 SELECT 语句。

优势:1)只读事务避免了为事务分配事务ID(TRX_ID域)的开销;2)对于密集读的场景,可以将一组查询请求包裹在只读事务中,既能提高性能,又能保证查询数据的一致性。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值