MySQL事务、日志、锁和MVCC机制

InnoDB中事务的四大特性

原子性:当前事务的操作要么全部成功要么全部失败。

原理:原子性是由undo log来保证的,undolog记录着数据修改之前的值。比如我们insert一条语句,undolog就会记录一条delete语句,我们update一条语句,undolog就会记录一条对应的原来数据的update语句,如果回滚就会利用到undolog日志的内容。

一致性:事务提交前后我们的数据要一一致。

我们使用事务的目的就是为例保证一致性,而原子性、隔离性、持久性都是为了保证一致性的。

隔离性

  • 读未提交:存在脏读的问题,就是读到了其他事务未提交的数据。不可重复读问题。
  • 读已提交:不可重复读。
  • 可重复读:存在幻读。一个事务先根据某些条件查询出一些记录,之后另一个事务又向表中插入了符合这些条件的记录,原先的事务再次按照该条件查询时,能把另一个事务插入的记录也读出来。
  • 串行化:各种问题(脏读、不可重复读、幻读)都不会发生,通过加锁实现(读锁和写锁)。

事务的隔离级别越高,安全性越好,但是性能就会越低。隔离性底层是由锁来实现的,所谓隔离性
只是屏蔽了加锁的细节。

持久性:事务一旦提交了,它对数据库的改变是永久性的,也就是将数据持久化到硬盘当中了

持久性是由redolog来保证的,如果我们需要修改数据,MySQL会先把这个数据所在的页找到,加载到内存当中,将对应的数据修改了,为了防止我们内存刚修改完MySQL服务器就挂了,就无法记录到硬盘当中了,所以MySQL会把操作记录到redolog日志当中,redolog是顺序写入的,写入的速度很快,即使MySQL数据库挂了,也可以通过redolog进行恢复。

日志

change buffer

当需要更新一个数据的时候,如果这个数据页在内存中,就会直接更新,如果不在内存中,InnoDB就会把数据缓存到change buffer中,这样就不用从磁盘读取数据页了,下次查询需要访问这个数据页的时候再把这个change buffer里面的内容写到这个数据页里面,提高效率。但是要注意的是只有普通索引可以使用changebuffer,唯一索引就不能使用changebuffer了。

要注意对于写多读少的情况可以充分利用changebuffer,但是对于写少读多的情况,写了立马就会查询,触发merge过程,这样的话并没有减少io的次数,反而还增加了维护changebuffer的代价。

.二进制日志(bin log)

记录所有更改数据的语句,可用于数据复制。属于物理日志,基于MySQL服务的,所有的存储引擎都有binglog,通过追加的方式记录文件,可以通过max_binlog_size来设置binlog文件的大小,满了会创建新的文件。

虽然binlog记录了所有的操作,但是由于binlog无法判断哪些数据已经刷盘,索引MySQL服务宕机了之后无法通过binlog恢复,

binlog日志有三种形式

Statement:基于sql语句的复制,不需要记录每一行的变化,减少binlog的日志量,节省IO,提高性能。但是需要记录sql上下文相关的信息。

Row:基于行的复制,不需要记录sql语句上下文的信息,仅需要记录每一行数据被修改成了什么,但是可能会导致大量的日志。

Mixed:混合模式复制,前两种的结合。涉及到一些函数的sql,binlog无法记录sql,就需要Row的格式,MySQL根据执行的具体的sql自动选择记录的格式。

参考文字
row格式的binlog日志,记录的不是SQL原文,而是两个event:Table_map 和 Delete_rows。Table_map event说明要操作的表,Delete_rows event用于定义要删除的行为,记录删除的具体行数。row格式的binlog记录的就是要删除的主键ID信息,因此不会出现主从不一致的问题。
但是如果SQL删除10万行数据,使用row格式就会很占空间的,10万条数据都在binlog里面,写binlog的时候也很耗IO。但是statement格式的binlog可能会导致数据不一致,因此设计MySQL的大叔想了一个折中的方案,mixed格式的binlog。所谓的mixed格式其实就是row和statement格式混合使用,当MySQL判断可能数据不一致时,就用row格式,否则使用就用statement格式。

redo log

redo log主要用于MySQL服务器异常重启的情况,可以用来恢复尚未写入磁盘的数据,因为MySQL进行更新操作时候采用了异步写入磁盘的技术,写入内存就返回可能导致数据丢失,这时候redo log就可以起作用了。

redolog可以判断哪些数据已经写入了磁盘,写入之后就会删除redolog里面的日志,所以是可以一直写的。如果redolog写入失败,说明这次的操作失败,事务也就不会提交。redolog内部结构是基于页的,记录了这个页字段的变化。

数据库崩溃了之后如何恢复没有写入磁盘的数据?

如果changebuffer写入了,redolog没有commit,binlog没有记录到磁盘,那么就无法恢复数据。

如果changebuffer写入了,redolog没有commit,binlog记录到了磁盘中,可以从binlog恢复redolog,然后操作你的redolog恢复changebuffer。

如果changebuffer写入了,redolog和binlog都已经写入磁盘,可以直接从redolog恢复数据。

WAL(Write Ahead Log)预写日志

先把对数据的操作写入日志,然后再把数据写入磁盘。由于不需要每次更新都写入磁盘这样的话就能够快速响应sql。

两阶段提交

所谓的两阶段提交就是把redo log的写拆分成prepare和commit两步骤,在两个步骤中间插入binlog的写入,这就是两阶段提交。

两阶段提交的目的是为了保证数据库的高度一致性。
假如我们无论是先写redolog还是binlog,只要其中有一个写完的时候数据库崩溃,就会导致redolog和binlog的数据不一致,导致我们备份数据的时候备份表的数据和当前表的数据不一致。

MySQL中的锁

InnoDB存储引擎下,可以分为表锁和行锁
如果我们的sql语句命中了索引,这时候锁住的就是一行,当我们需要更新一张较大表的大部分甚至全表的数据时,如果我们不小心用索引作为检索条件,一不小心开启了行锁(没毛病啊!保证数据的一致性!)。可MySQL却认为大量对一张表使用行锁,会导致事务执行效率低,从而可能造成其他事务长时间锁等待和更多的锁冲突问题,性能严重下降。所以MySQL会将行锁升级为表锁,即实际上并没有使用索引。当我们一次操作事务涉及多个表的时候,可能造成死锁的情况下我们应该主动使用表锁。InnoDB的行锁是基于索引实现的,如果不通过索引访问数据,InnoDB会使用表锁。

间隙锁

当我们用范围条件而不是相等条件检索数据,并请求共享或排他锁时,InnoDB会给符合条件的已有数据的索引项加锁;对于键值在条件范围内但并不存在的记录,叫做“间隙(GAP)”,InnoDB也会对这个“间隙”加锁,这种锁机制就是所谓的间隙锁(Next-Key锁)。间隙锁只有在可重复读隔离界别之下才会生效。

意向锁

假如我们事务A锁住了表的一行,这一行就会只能读不能写,然后事务B申请获取这个表的表锁,如果申请成功意思就是说可以对这个表的数据进行写操作,这就存在矛盾,所以事务B首先会判断这个表里面有没有行锁,怎么判断呢?遍历的话肯定太慢了,所以就引入了意向锁。

在引入意向锁的情况下:
事务A要想获取行锁首先要获取这个表的意向互斥锁,获得了之后就可以对某一行上锁。而事务B发现这个表被上了意向互斥锁之后就只能等待锁被释放才能进行添加表锁的操作,这就比较高效地解决了行锁和表锁冲突的问题。

MVCC多版本并发控制

在没有mvcc的情况下我们读写都要加锁,效率低下,而MVCC就是为了解决这个问题,做到读写不加锁。

MVCC通过生成快照来提供一定的隔离级别,在读已提交的隔离级别下,它生成的就是语句快照读,MVCC在每个操作上面添加了事务id,事务版本号以及回滚指针。在读取的时候会读取commit之后的id的版本号修改的数据,通过 版本的概念解决脏读的问题。同时MVCC也可以根据版本号做到可重复读,就是只读取当前事务对应的版本的数据信息,也就是快照读。

MVCC的本质就是对比版本,通过记录在undolog里面的隐式字段:事务id,尚未提交的事务id的最小值以及回滚指针来实现的。

在RC隔离级别下,快照读每次都会重新生成一个快照。
在RR隔离级别下,快照读只在当前事务第一次读取的才会生成快照,后面都是读取这个快照。

这里要注意的是,开启了事务之后要对数据库进行操作,不然就是没开启的,进行操作了之后才会有后面的段版本并发控制记录一些东西,如果只是开启了事务,没有进行其他的操作,这个时候另一个事务开启了然后进行了操作,这个时候我第一次首先开启的那个事务再进行操作的时候,相当于是再第二次事务开启之后才开启的事务,要注意这个问题。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北海冥鱼未眠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值