数据库之 事务

Posted flyaway2013

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据库之 事务相关的知识,希望对你有一定的参考价值。

什么是事务,一个事务是一个只包含所有读/写操作成功的集合。通常可以认为 事务包含了多个 写操作 sql 。

事务的四个特点

一个事务本质上有四个特点,也就是ACID:

  1. Atomicity原子性,事务中所有的操作要么全部成功,要么全部失败。 这个容易理解。
  2. Consistency一致性,事务结束后系统状态是一致的;
  3. Isolation隔离性,并发执行的事务彼此要 进行读写隔离(具体如何隔离, 按情况而定);其实这是个不确定状态。应该具体到四个层次中进行讨论。
  4. Durability持久性,事务完成后所做的改动都会被持久化,即使发生灾难性的失败。通过日志和同步备份可以在故障发生后重建数据。也就是被固化。这个容易理解。

 

这里的一致性,有点不好理解。 其实可以这么理解:

一致性是指事务必须使数据库从一个一致性状态   变换到   另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。
拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。

 

重点说说 隔离性

隔离性,虽然按我理解是 一种 不是特性的特性,更像是特点, 但普遍认为它是一种 重要数据库特性, 那就这样认为吧。它当然是很重要的,它内涵十分丰富的。

它其实就是说隔离级别。数据库事务的隔离级别有4个,由低到高依次为Read uncommitted 、Read committed 、Repeatable read 、Serializable ,这四个级别可以逐个解决脏读 、不可重复读 、幻读 这几类问题。 友好度:  序列化读 > 可重复读 > 提交读 > 提交读 > 不支持

  脏读 不可重复读 幻读
Read uncommitted
Read committed ×
Repeatable read × ×
Serializable × × ×

注意:我们讨论隔离级别的场景,主要是在多个事务并发 的情况下。

JDK 的API :

技术分享图片

 大多数数据库的默认级别就是Read committed,比如Sql Server , Oracle。 Mysql的默认隔离级别就是Repeatable read。

 

再论 脏读、幻读、不可重复读

1.脏读(Phantom Read):
脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。

2.不可重复读(Unrepeatable Read):
是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。(即不能读到相同的数据内容)
例如,一个编辑人员两次读取同一文档,但在两次读取之间,作者重写了该文档。当编辑人员第二次读取文档时,文档已更改。原始读取不可重复。如果只有在作者全部完成编写后编辑人员才可以读取文档,则可以避免该问题。

3.幻读(Dirty Read):
是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象
发生了幻觉一样。
例如,一个编辑人员更改作者提交的文档,但当生产部门将其更改内容合并到该文档的主复本时,发现作者已将未编辑的新材料添加到该文档中。如果在编辑人员和生产部门完成对原始文档的处理之前,任何人都不能将新材料添加到文档中,则可以避免该问题。 

出至于 https://blog.csdn.net/JIESA/article/details/51317164 

 

脏读容易理解,但是 不可重复读 有些奇怪。事务t1 两次读数据d1, 为什么会不一样,因为其他的事务(比如t2)在两次读操作之间修改了 d1,那么,为什么t2 可以修改d1,d1 不是处于事务之中吗? 事务之中的资源不是应该被上锁吗? 其他的事务为什么可以访问? 需要说明的是,虽然我们一般认为事务就会给资源加锁,防止任何其他线程的访问,但是, 我们需要明白事务并不一定意味着加锁,加锁确实是一种行为,但是事务依据隔离级别而定,还有各种 行为呢, 比如 脏读、未提交读。  —— 另外,锁可以防止其他线程的访问,但是没说是防止读还是防止写, 如果仅仅是防止读, 那么其他线程的事务 有可能在 当前事务的 两次读 之间 进行读的?。。。

 

脏读、不可重复读读 是 难以被接受的, 因为会出现一些混乱,以及难以理解的情况。

(注意, 未提交读本身不是个问题,它只是个隔离级别。它确实会引起一些的问题)

幻读 更加的难以理解。不可重复读和幻读很相近。一定需要注意的是,不可重复读强调的是记录的更新(update),幻读强调的是插入和删除(insesrt和delete)

 

如何实现的?

要理解 事务的隔离级别,我们需要大概知道 它是如何实现的,以mysql为例。 我大胆猜测一下。

脏读,就是说,t1未提交,t2 就可以读到t1的修改了。 可以想象,如果t1、t2操作的数据 是共享的,那么即使 未提交,却相互可见,那么就出现了 脏读。从锁的角度来看, 可以认为 脏读是 根本没有加锁的 结果。

 

不可重复读,就是说,假设依次发生 t1开始,xxx,t1 读操作r1,t2更新操作w1,t1读操作r2,xxx, t1 提交,那么r2,读到的是 w1的结果。可以想象,如果r1 的读操作没有排斥 w1的写操作的话,那么就会出现这个问题。就是说,读操作r1 应该在读的那一刻,锁住数据,不让其他线程的事务进行 写,但允许他们读。 这个可能是没有加独占锁的结果, 比如只是加了个共享锁, 然而并没有什么卵用。

我们通过对r1 增加 独占锁,可以解决这个问题。 另外, 我们要区分 是读整个表呢? 还是某一行, 或是某几行呢?  如果是 读某一行, 我们当然也可以 对整个表启用 独占锁, 但是这样的话,成本有点高,性能有所下降, 这个时候我们可以启用 行的 独占锁 ; 如果是读整个表呢? 那当然需要  对整个表启用 独占锁; 如果是某个范围呢? 我们需要 范围锁(有这个锁吗?)。

 

幻读,就是说,依然假设依次发生 t1.r1,t2.w1, t1.r2, 假设r1 是读整个表 或者某部分的 部分( 不是 按唯一id 查询),假设我们已经 对r1 启用的 整表独占锁 或者该锁住的 部分。 按理来说,r2 应该会排斥w1 才对,也就是说,按理来说,r2 应该发生在w1 之前,但是呢。 整表独占锁 也可以细分为 很多的情况, 比如我们可以锁住整个所有的数据(或者整个索引文件), 这样这些数据是不能够修改的,那么就不会出现幻读。 如果 这个时候 表新增一行或删除了其他行的数据? 那么, 假设 整表独占锁 允许新增,也就是没有锁住新增的,或者允许删除,那么就出现了 一个事务内 两次相同的 读操作, 读到不同的 结果了, 也就是出现了幻读了!!

 

特别说明,我认为r1 应该是r2是 同一个sql, 也就是开始r1是 读表操作, 后面的r2也是读表操作。而不是说 r1 读行, r2 读表,如果是这样,会出现更多的问题, 但我认为这个是另外的话题了。我认为 幻读 应该的是 r1,r2 读表 引起的,而不是 按id 读行。

所谓 读表操作 sql ,可能是 没有where 条件的查询, 也可以是 有条件的sql, 总之不是 根据 唯一索引查询 的 sql。

知其所以然

通过上面的表,我们知道了各个隔离级别 可以引起的问题。

 Read uncommitted 可以引起脏读,不可重复读,幻读,why,因为 它可能 根本没有加锁,t1的写,没有对t2的读隔离,所有,那么t1的写 对 t2是立即可见的, 脏读都防止不了,其他各种问题 更加防止不了。

Read committed 可以引起不可重复读,幻读, 这个可能是没有加独占锁,那么 t1 还是不能限制t2 的写,自然会引起 不可重复读, 幻读就更加不要说了。

Repeatable read 可以引起幻读, 这个可能是没有 “真正的独占的”  “锁住整个表或者该锁住的 部分”, 虽然表的已经存在的 数据是 锁住了,但是新增的呢? 删除的呢?

 

前面假设t1、t2 使用的相同的隔离级别, 如果他们不同,那么情况可能更加复杂。(同一个时间,t1、t2 使用的不同的隔离级别吗? 存疑)

 

ACID与CAP、BASE

ACID是传统数据库常用的设计理念,追求强一致性模型。BASE支持的是大型分布式系统,提出通过牺牲强一致性获得高可用性。

ACID和BASE代表了两种截然相反的设计哲学

在分布式系统设计的场景中,系统组件对一致性要求是不同的,因此ACID和BASE又会结合使用。

 

BASE的BA 基本可用是指分布式系统在出现不可预知故障的时候,允许损失部分可用性----注意,这绝不等价于系统不可用。比如:

(1)响应时间上的损失。正常情况下,一个在线搜索引擎需要在0.5秒之内返回给用户相应的查询结果,但由于出现故障,查询结果的响应时间增加了1~2秒

(2)系统功能上的损失:正常情况下,在一个电子商务网站上进行购物的时候,消费者几乎能够顺利完成每一笔订单,但是在一些节日大促购物高峰的时候,由于消费者的购物行为激增,为了保护购物系统的稳定性,部分消费者可能会被引导到一个降级页面

BASE的S 是说, 分布式系统多个节点,只保证部分而不是 全部 节点 拥有 最新的数据备份, 是一个中间状态。并认为该中间状态的存在不会影响系统的整体可用性,即允许系统在不同节点的数据副本之间进行数据同步的过程存在延时。

BASE的E 是说,分布式系统的所有节点,经过一些延时之后, 最终都拥有了 最新的数据备份

 

ACID和BASE 这两个术语都好记有余而精确不足,出现较晚的BASE硬凑的感觉更明显,它是“Basically Available, Softstate, Eventually consistent(基本可用、软状态、最终一致性)”的首字母缩写。其中的软状态和最终一致性这两种技巧擅于对付存在分区的场合,并因此提高了可用性。

CAP与ACID的关系更复杂一些,也因此引起更多误解。其中一个原因是ACID的C和A字母所代表的概念 完全不同于CAP的C和A。还有一个原因是选择可用性只部分地影响ACID约束。

总的来说,BASE理论面向的是大型高可用可扩展的分布式系统,和传统的事物ACID特性是相反的,它完全不同于ACID的强一致性模型,而是通过牺牲强一致性来获得可用性,并允许数据在一段时间内是不一致的,但最终达到一致状态。但同时,在实际的分布式场景中,不同业务单元和组件对数据一致性的要求是不同的,因此在具体的分布式系统架构设计过程中,ACID特性和BASE理论往往又会结合在一起

ACID 是面向单机的事务特性,如果mysql 数据库做了集群呢?ACID  是根本没有考虑网络问题的,而一旦涉及集群或者分布式系统,那么,P相关问题不可避免,那么我们可能需要结合 ACID ,CAP,甚至BASE。

 

当我们把事务的ACID 特性和 分布式理论的CAP 统一起来的时候,其实就是 所谓的 分布式事务 !!!

 

参考:

https://blog.csdn.net/JIESA/article/details/51317164
https://www.jianshu.com/p/f432665d523f
https://blog.csdn.net/zh521zh/article/details/69400053
https://blog.csdn.net/wireless_com/article/details/79153643 
 

以上是关于数据库之 事务的主要内容,如果未能解决你的问题,请参考以下文章

如何绕过将数据模型传递给片段参数以避免事务太大异常?

回栈事务后如何持久化分片数据?

片段事务分离和附加后ListView不工作?

理解片段事务期间片段的生命周期方法调用

提交带有全屏片段的片段事务

使用 OnItemClickListener 列出视图片段到片段事务