ACID ---- 原子性、一致性、隔离性、持久性
原子性
就是一系列的操作,要么都执行,要么都不执行。当一个事务发生异常的时候,就会通过回滚来保证原子性。在mysql中,原子性是通过回滚日志来实现的,回滚日志就是我们的操作的逆操作。
持久性
事务提交之后一定会存储到数据库中,如果要回滚只能手动执行相反的操作了
隔离性
事务的隔离性会跟并发等相关概念联系的非常密切,因为它主要就是为了保证并行事务处理能够达到“互不干扰”的效果。
我们在一致性中讨论过事务在并发情况下执行时,可能发生的一系列问题:虽然单个事务执行并没有错误,但是它的执行可能会牵连到其他事务的执行,最终导致数据库的整体一致性出现偏差。
谈到这里我们就要看看事务之间的互相干扰都有哪些层级,也就是我们数据库中非常重要的概念:
事务的隔离级别
事务的隔离级别,其实是数据库对数据隔离性能的一种约束,选择不同的隔离级别会影响数据一致性的程度,同时也会影响数据库的操作性能。
标准SQL中定义了以下4种隔离级别:
-
读未提交
使用查询语句不会加锁,可能会读到未提交的行(脏读)
-
读已提交
只对记录加记录锁,而不会在记录之间增加间隙锁,所以允许新的记录被插入到被锁定记录附近,在多次使用查询语句时,可能会得到不同的结果(不可重复读)。例如:事务A读取数据,然后事务B修改数据,之后A再次读取,这是读取到的数据和第一次读取的数据不一致
-
可重复读
多次读取同一范围的数据会返回第一次查询的快照,不会返回不同的数据行,但是可能发生幻读。幻读 : 是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。 同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象 发生了幻觉一样。
-
串行化
隐式地将全部的查询语句都加上了共享锁。从上到下一致性逐渐增强,但是数据库的读写性能也逐渐变差。大部分数据库中使用提交读作为默认的隔离级别,这是出于性能和一致性的平衡,而MySQL中则默认采用可重复读作为配置。对于开发者而言,不必去了解每个隔离级别具体的实现,但要能够根据不同的场景选择最合适的隔离级别。
一致性
http://geyifan.cn/2016/07/17/talk-about-transaction/
状态是不一致的。
举个栗子,张三给李四转账100元。事务要做的是从张三账户上减掉100元,李四账户上加上100元。一致性的含义是其他事务要么看到张三还没有给李四转账的状态,要么张三已经成功转账给李四的状态,而对于张三少了100元,李四还没加上100元这个中间状态是不可见的。
那么反驳的声音来了:
- 要么转账操作全部成功,要么全部失败,这是原子性。从例子上看全部成功,那么一致性就是原子性的一部分咯,为什么还要单独说一致性和原子性?
- 你说的不对。在未提交读的隔离级别下是事务内部操作是可见的,这时候会出现脏读,明显违背了一致性,怎么解释?
好吧,你的疑问很有道理,也很充分,这正说明你对事务的ACID特性理解的很到位。不过,需要注意的是:
- 原子性和一致性的的侧重点不同:原子性关注状态,要么全部成功,要么全部失败,不存在部分成功的状态。而一致性关注数据的可见性,中间状态的数据对外部不可见,只有最初状态和最终状态的数据对外可见。
- 在未提交读的隔离级别下,会造成脏读,这就是因为一个事务读到了另一个事务操作内部的数据。ACID中是的一致性描述的是一个最理想的事务应该怎样的,是一个强一致性状态,如果要做到这点,需要使用排它锁把事务排成一队,即Serializable的隔离级别,这样性能就大大降低了。现实是骨感的,所以使用隔离性的不同隔离级别来破坏一致性,来获取更好的性能。
补充:不可重复读和幻读
很多人容易搞混不可重复读和幻读,确实这两者有些相似。但不可重复读重点在于update和delete,而幻读的重点在于insert。
如果使用锁机制来实现这两种隔离级别,在可重复读中,该sql第一次读取到数据后,就将这些数据加锁,其它事务无法修改这些数据,就可以实现可重复 读了。但这种方法却无法锁住insert的数据,所以当事务A先前读取了数据,或者修改了全部数据,事务B还是可以insert数据提交,这时事务A就会 发现莫名其妙多了一条之前没有的数据,这就是幻读,不能通过行锁来避免。需要Serializable隔离级别 ,读用读锁,写用写锁,读锁和写锁互斥,这么做可以有效的避免幻读、不可重复读、脏读等问题,但会极大的降低数据库的并发能力。