MySQL如何实现事务的ACID

amber 4年前 2,440次点击 来自 mysql

事务的四大特性ACID
事务的四大特性ACID分别是,A-原子性(Atomicity),C-一致性(Consistency),I-隔离性(Isolation),D-持久性(Durability)。一致性是最终目的,原子性、隔离性、持久性是为了保证一致性所做的措施。所以我写的顺序并不是按照ACID来写的,将一致性放到了最后,顺序就变成了,ADIC。

原子性(A)

原子性是指一个事务就是一个不可分割的工作单位,要么全部都执行成功,要么全部都执行失败,没有中间状态或是只执行一部分。

MySQL的InnoDB引擎是靠undo log(回滚日志)来实现的,undo log能够保证在事务回滚时,能够撤销所有已经执行成功的SQL。

undo log 属于逻辑日志,它记录的是SQL执行相关的信息。当事务对数据库进行修改时,InnoDB会生成与之对应的undo log。如果事务执行失败或者调用的rollback,导致事务需要回滚,InnoDB引擎会根据undo log中的记录,将数据回滚到之前的样子。
例如在执行insert语句时会生成相关的delete语句的undo log。反之执行delete语句也会生成相关的insert语句的undo log。执行update语句时也是如此,不过update语句在执行undo log回滚时有可能会涉及到MVCC。主要是为了保证在执行undo log的时候的select能看到哪个版本的数据。

持久性(D)

持久性是指事务一旦提交,对数据库的操作就是永久性的,接下来的其他操作和异常故障不应该对它有任何影响。
我们都知道MySQL的数据最终是存放在磁盘中的,所以才会有磁盘的容量大小决定数据容量的大小。但是如果对MySQL的操作都是通过读写磁盘来进行的话,那么光是磁盘的I/O就够把效率大大的拉低了。

所以InnoDB为MySQL提供了缓冲池(Buffer Pool),Buffer Pool中包含了磁盘中部分数据页的映射。
当从数据库读取数据时,会先从Buffer Pool中读取数据,如果Buffer Pool中没有,则从磁盘读取后放入到Buffer Pool中。
当向数据库写入数据时,会先写入到Buffer Pool中,Buffer Pool中更新的数据会定期刷新到磁盘中(此过程称为刷脏)。

虽然Buffer Pool为MySQL的读写提高了效率,但是却也带来了新的问题,那就是如果数据刚更新到Buffer Pool中还没来得及刷新到磁盘中时,MySQL突然宕机了,这就会导致数据丢失,造成事务的持久性无法保证了。
为了解决这个缓存的一致性问题,redo log就出现了。在对Buffer Pool中的数据进行修改的时候通过redo log记录这次操作,当事务提交时会通过fsync接口对redo log进行刷盘。

因为在事务提交时会把redo log是同步在磁盘中的,所以当MySQL出现宕机时,可以从磁盘中读取redo log进行数据的恢复,从而保证了事务的持久性。

redo log 采用的预写的方式记录日志,即先记录日志,再更新Buffer Pool,这样就强行的保证了,数据只要保存在了redo log中就一定会存储到磁盘中了。

这要解释一下,redo log 也是写磁盘,刷脏也是写磁盘,为啥要先记录redo log而不是直接刷脏?

主要原因就是redo log比刷脏快很多。

第一点是,redo log是追加操作日志,是顺序IO;而刷脏是随机IO,因为每次更新的数据不一定是挨着的,也就是随机的。

第二点是,刷脏是以数据页(Page)为单位的(即每次最少从磁盘中读取一页数据到内存,或者最少刷一页数据到磁盘),MySQL默认页大小是16KB,对一个页上的修改,都要整个页都刷到磁盘中;而redo log只包含真正的需要写入磁盘的操作日志。

MySQL还有一个记录操作的日志,叫binlog ,那么redo log和binlog又有什么区别呢?

  • 第一点作用上的区别:
    redo log是用来记录更新缓存的,为了保证MySQL就算宕机也不会影响事务的持久性;binlog是用来记录什么时间操作了什么,主要有时间点,可以保证将数据恢复到某个时间点,也有用于主从同步数据的。

  • 第二点层次上的区别:
    redo log是存储引擎InnoDB实现的(MyISAM就没有redo log),而binlog是在MySQL服务器层面存在的任何其他存储引擎也有binlog。
    存储内容上,redo log是物理日志,基于磁盘的数据页,binlog是逻辑日志,存储的一条执行SQL。

  • 第三点写入时机的区别:
    redo log 在默认情况下是在事务提交时,进行刷盘的;可以通过参数:innodb_flush_log_at_trx_commit 来改变策略,可以不用等到事务提交时才进行刷盘。
    如:可以设置成每秒提交一次。
    binlog是在事务提交时写入。

隔离性(I)
原子性和持久性都是基于单个事务内部的措施,而隔离性是只多个事务之间相互隔离,互不影响的特性。
我们都知道事务的隔离级别中最严谨的是串行化(Serializable),但是隔离性越高,性能就越低,所以一般不使用串行化这个隔离级别。
对于隔离性的,我们要分两种情况进行讨论:

  • 一个事务中的写操作对另一个事务中的写操作的影响;

  • 一个事务中的写操作对另一个事务中的读操作的影响;
    首先,事务间的写操作其实是靠MySQL的锁机制来实现隔离的,而事务间的写和读操作是靠MVCC机制来实现的。

目前暂无回复

致力于搭建一个分享发现好玩东西的社区京ICP备15052899号-3

本站总访问量 | 本文总阅读量 | 本站总访客数