一.事务的四大特性(ACID)
mysql存储引擎分为myisam和innodb两种,想要使用事务就要用innodb引擎,事务一共有四种包括(原子性(atomicity),一致性(consistency),隔离性(isolation),持久性(durability))简称ACID!
原子性(Atomicity):事务是最小的执行单元,不允许分割。原子性保证了动作要么全完成,要么全不完成。
一致性(Consistency):事务执行成功后,数据库从一个正确的状态变化到另一个正确的状态。
隔离型(Isolation):并发访问数据库时,一个事务不被其他事务所干扰,各并发事务之间数据独立。
持久性(Durability):一个事务提交之后,对数据库的改变是持久的
那ACID靠什么保证的呢?
事务的隔离性是通过数据库锁的机制实现的。
事务的一致性由undo log来保证:undolog是逻辑日志,记录了事务的insert、update、deltete操作,回滚的时候做相反的delete、update、insert操作来恢复数据。
事务的原子性和持久性由redolog来保证:redolog被称作重做日志,是物理日志,事务提交的时候,必须先将事务的所有日志写入redolog持久化,到事务的提交操作才算完成。1.Mysql怎么保证一致性的?
(1)从数据库层面,数据库通过原子性、隔离性、持久性来保证一致性。也就是说ACID四大特性之中,C(一致性)是目的,A(原子性)、I(隔离性)、D(持久性)是手段,是为了保证一致性。数据库必须要实现AID三大特性,才有可能实现一致性。
(2)从应用层面,通过代码判断数据库数据是否有效,然后决定回滚还是提交数据!2.Mysql怎么保证原子性的?
利用Innodb的undo log:(undolog名为回滚日志,是实现原子性的关键,当事务回滚时能够撤销所有已经成功执行的sql语句,他需要记录你要回滚的相应日志信息。)3.Mysql怎么保证持久性的?
利用Innodb的redo log:Mysql是先把磁盘上的数据加载到内存中,在内存中对数据进行修改,再刷回磁盘上。如果此时突然宕机,内存中的数据就会丢失。使用redo log,当做数据修改的时候,不仅在内存中操作,还会在redo log中记录这次操作。当事务提交的时候,会将redo log日志进行刷盘(redo log一部分在内存中,一部分在磁盘上)。当数据库宕机重启的时候,会将redo log中的内容恢复到数据库中,再根据undo log和binlog内容决定回滚数据还是提交数据。
好处: 将redo log进行刷盘比对数据页刷盘效率高:redo log体积小,刷盘快。一直往末尾进行追加,属于顺序IO。效率显然比随机IO来的快。4.Mysql怎么保证隔离性的?
利用的是锁和MVCC机制:至于MVCC,即多版本并发控制(Multi Version Concurrency Control),一个行记录数据有多个版本对快照数据,这些快照数据在undo log中。
如果一个事务读取的行正在做DELELE或者UPDATE操作,读取操作不会等行上的锁释放,而是读取该行的快照版本。
MVCC机制在可重复读(Repeateable Read)和读已提交(Read Commited)的MVCC表现形式不同:
(1)在事务隔离级别为读已提交(ReadCommited)时,一个事务能够读到另一个事务已经提交的数据,是不满足隔离性的。
(2)但是当事务隔离级别为可重复读(RepeateableRead)中,是满足隔离性的。
二.事务四种隔离级别(默认事务级别为可重复读)
每个事务都有一个隔离级别(isolation level),它规定了并发运行的两个事务之间是否允许发生上面的问题。隔离级别越高,并发性能也就越低。
读取未提交RU(Read Uncommitted):最低的隔离级别,允许读取尚未提交的数据,可能导致赃读、幻读、不可重复读。
读取已提交RC(Read Committed):允许读取已经提交的事务,可以阻止赃读,可能导致幻读和不可重复读
可重复读RR(RepeatableRead):对同一字段多次的读取结果都是一致的,除非数据是当前事务本身修改的。可以阻止赃读、不可重复读,可能导致幻读和
可串行(Serializable):所有事务依次逐个执行。最高的隔离级别,完全服从ACID的隔离界别。
以上四种隔离级别最高的是Serializable级别,最低的是Read uncommitted级别,当然级别越高,执行效率就越低。Serializable这样的级别,就是以锁表的方式(类似于Java多线程中的锁)使得其他的线程只能在锁外等待,所以平时选用何种隔离级别应该根据实际情况。在MySQL数据库中默认的隔离级别为Repeatable read (可重复读)
事务的各个隔离级别都是如何实现的?
读未提交: 采取的是读不加锁原理。 事务读不加锁,不阻塞其他事务的读和写 事务写阻塞其他事务写,但不阻塞其他事务读;
读取已提交&可重复读: 读取已提交和可重复读级别利用了ReadView和MVCC,也就是每个事务只能读取它能看到的版本(ReadView)。READ COMMITTED:每次读取数据前都生成一个ReadView。 REPEATABLE READ:在第一次读取数据时生成一个ReadView
串行化: 串行化的实现采用的是读写都加锁的原理。串行化的情况下,对于同一行事务,写会加写锁,读会加读锁。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。
不同的隔离级别,在并发事务下可能会发生的问题:
三.并发事务的四个问题
只有root用户commit提交数据到磁盘wangsh用户才能查询到数据。这就是不允许脏读。
并发事务的四个问题:脏读,不可重复读,幻读,丢失更新
脏读:一个事务读到了另一个事务未提交的数据
不可重复读:在一个事务里,同样的条件,读过在读–读到不同的数据
幻读:在同一个事务中,同样的条件,第1次和第2次读出来的记录数不一样
丢失更新:一个事务的修改覆盖了另一个事务所做的修改
不可重复读和幻读的区别:
两者都表现为两次读取的结果不一致。区别是:不可重复读的重点是修改,幻读的重点在于新增或者删除
四.多版本并发控制(MVCC)
MVCC全程Multi-Version Concurrency Control
MVCC 在 MySQL InnoDB 中用来处理读写冲突的手段,目的在于提高数据库高并发场景下的吞吐性能。做到即使有读写冲突时,也能做到不加锁,非阻塞并发读。
什么是当前读和快照读?
当前读 :像 select lock in share mode (共享锁), select for update; update; insert; delete(排他锁)这些操作都是一种当前读,为什么叫当前读?就是它读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁。
快照读 :像不加锁的 select操作就是快照读,即不加锁的非阻塞读;快照读的前提是隔离级别不是串行级别,串行级别下的快照读会退化成当前读;快照读的实现是基于多版本并发控制,即 MVCC ,可以认为 MVCC是行锁的一个变种,但它在很多情况下,避免了加锁操作,降低了开销;既然是基于多版本,即快照读可能读到的并不一定是数据的最新版本,而有可能是之前的历史版本。
说白了 MVCC 就是为了实现读-写冲突不加锁,而这个读指的就是快照读, 而非当前读,当前读实际上是一种加锁的操作,是悲观锁的实现
MVCC的实现原理
主要是依赖每一行记录中两个隐藏字段,undo log,ReadView
undo log:可以理解成回滚日志,它存储的是老版本数据
ReadView:是事务在进行快照读的时候生成的记录快照,可以帮助我们解决可见性问题的
实现隔离机制的方法主要有两种:读写锁。一致性快照读,即MVCC。
本质上,隔离级别是一种在并发性能和并发产生的副作用间的妥协,通常数据库均倾向于采用 Weak Isolation InnoDB中的MVCC
mysql中InnoDB支持MVCC。 应对高并发事务,mvcc比单纯的行锁更有效,开销更小。 MVCC在RC与RR隔离级别下生效。
MVCC实现可以基于乐观所或者悲观锁均可。 MVCC通过隐藏两个字段(没有主键时为3个
MVCC能否解决幻读?
MVCC在快照读的情况下可以解决幻读问题,但是在当前读的情况下是不能解决幻读的
MVCC能解决不可重复读问题,但是不能解决幻读问题,不论是快照读和当前读都不能解决。RR级别解决幻读靠的是锁机制,而不是MVCC机制。
MVCC 能解决什么问题,好处是?
数据库并发场景有三种,分别为:
读-读:不存在任何问题,也不需要并发控制
读-写:有线程安全问题,可能会造成事务隔离性问题,可能遇到脏读,幻读,不可重复读
写-写:有线程安全问题,可能会存在更新丢失问题,比如第一类更新丢失,第二类更新丢失
MVCC 带来的好处是?
多版本并发控制(MVCC)是一种用来解决读-写冲突的无锁并发控制,也就是为事务分配单向增长的时间戳,为每个修改保存一个版本,版本与事务时间戳关联,读操作只读该事务开始前的数据库的快照。所以 MVCC 可以为数据库解决以下问题
1.在并发读写数据库时,可以做到在读操作时不用阻塞写操作,写操作也不用阻塞读操作,提高了数据库并发读写的性能
2.同时还可以解决脏读,幻读,不可重复读等事务隔离问题,但不能解决更新丢失问题