在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
前言 最近在面试,有被问到,MySQL的InnoDB引擎是如何实现事务的,又或者说是如何实现ACID这几个特性的,当时没有答好,所以自己总结出来,记录一下。 事务的四大特性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中的记录,将数据回滚到之前的样子。 持久性(D) 持久性是指事务一旦提交,对数据库的操作就是永久性的,接下来的其他操作和异常故障不应该对它有任何影响。 所以InnoDB为MySQL提供了缓冲池(Buffer Pool),Buffer Pool中包含了磁盘中部分数据页的映射。 虽然Buffer Pool为MySQL的读写提高了效率,但是却也带来了新的问题,那就是如果数据刚更新到Buffer Pool中还没来得及刷新到磁盘中时,MySQL突然宕机了,这就会导致数据丢失,造成事务的持久性无法保证了。 因为在事务提交时会把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 在默认情况下是在事务提交时,进行刷盘的;可以通过参数:innodb_flush_log_at_trx_commit 来改变策略,可以不用等到事务提交时才进行刷盘。 隔离性(I) 原子性和持久性都是基于单个事务内部的措施,而隔离性是只多个事务之间相互隔离,互不影响的特性。
锁机制 MySQL中的锁主要有 按照功能分:读锁和写锁;按照作用范围分:表级锁和行级锁; 读锁:又称“共享锁”,是指多个事务可以共享一把锁,都只能访问数据,并不能修改。 写锁:又称“排他锁”,是不能和其他事务共享数据的,如果一个事务获取到了一个数据的排他锁,那么其他事务就不能再获取该行的其他锁,包括共享锁和排他锁。 表级锁:是指会将整个表进行锁定,性能较差,不同存储引擎支持的锁的粒度不同,MyISAM引擎支持表级锁,InnoDB引擎支持表级锁也支持行级锁。 行级锁:会将需要操作的相应行进行锁定,性能好。 意向锁:意向锁是表级锁,如果在一个事务已经对一个表中的某个数据加上了排他锁或共享锁,那么就可以加上意向锁,这样当下一个事务来进行锁表的时候发现已经存在意向锁了,就会先被阻塞,如果不加意向锁的话,第二个事务来锁表的时候需要一行一行的遍历查看是否有数据已经被锁住了。 间隙锁:间隙锁是为了防止产生幻读而加的锁,加在不存在的空闲空间,可以是两个索引记录之间,也可能是第一个索引记录之前或最后一个索引之后的空间(但是并不包含当前记录)。这样就保证了在间隙锁执行的时候,新增的数据会阻塞,保证了一个事务中的两次查询获得的记录数都是一致的。 Next-Key Lock:Next-Key Lock是行级锁和间隙锁的结合产生的锁,因为间隙锁是不会锁住当前记录的而Next-Key Lock是会将当前记录也锁住的。 例如:如果一个表中有三条数据分别是:
那么在执行SQL:select * from table where number = 17 for update 时间隙锁会锁住,number的区间是(16,17),(17,20),但是Next-Key Lock的锁住的是: 锁机制保障了多个事务间的写操作的隔离,而多个事务间的读和写操作的保证是需要通过MVCC机制来保证的。 MVCC机制
MVCC的主要是靠在每行记录上增加隐藏列和使用undo log来实现的,隐藏列主要包括,改行数据创建的版本号(递增的),删除时间,指向undo log的指针等。 那么MVCC是如何保证读写隔离的呢?主要是通过快照读和当前读两个操作。
MVCC为了保证并发的效率,在进行读取数据的时候是不加锁的,在执行select的时候(不带锁的普通select),会先读取当前数据的版本号,如果在select还没返回结果时,有事务将此行数据进行了修改,那么版本号就会比执行select的时候的大,所以为了保证select读取数据的一致性,就只会读取小于或等于当前版本的数据,这个历史版本的数据就是从undo log中获取到的。
当执行insert、update、delete的时候,是读取的当前最新的版本数据,并且会给当前记录加上锁,用来保证在操作的时候不会被别的事务将版本号进行修改。 像普通的select就是快照读即读取的有可能就是数据的历史版本。 insert、update、delete、select ... lock in share mode 和select ... for update 读取的就是当前读,即读取的都是数据的最新版本。 其实将隔离级别设置为Serializable也是可以实现读写隔离的,但是并发效率会比低很多,所以一般用的很少,但是MVCC是读不加锁的,只有在写的时候才会加锁,从而提高的并发的效率。 通过MVCC机制保证了多个事务间的读写隔离,从而实现了事务的隔离性。 一致性(C) 一致性是指在事务执行前后,数据的一致性,事务前后数据完整性没有破坏,并且都是合法的数据状态。
索引的完整(唯一索引,不重复等),数据列的完成(字段类型,长度,大小符合要求),外键约束等。
保证原子性,持久性,隔离性,如果这些特性都无法保证,那么一致性就也无法保证了。从数据库层面来看,除了前面那几个特性的保证外,对字段的一致性是有保证措施的,例如整型的字符不能传入,字符串、时间等格式,字符串的长度不能超过列的限制。但是在应用层面也是需要开发者自己来保证的, 另外,MySQL还通过两阶段提交事务,保证了redo log和binlog之间的数据一致性问题。 通过上面介绍持久性的时候解释了,redo log和binlog的区别了,在区别中的第三条有说到,在默认情况下,事务提交时,既写redo log 有写binlog那么他们是如何协调一致性的呢?事务提交成功以写入哪个日志为准呢?
将redo log提交到磁盘,并将状态改为prepare状态,binlog不做任何操作。
1、生成事务操作的binlog,并将binlog写入到磁盘中。 2、调用引擎的提交事务接口,将redo log的状态从prepare改为commit,事务提交完成。 总结 MySQL事务的ACID,一致性是最终目的。
以上就是MySQL如何实现事务的ACID的详细内容,更多关于MySQL实现事务的ACID的资料请关注极客世界其它相关文章! |
请发表评论