Transaction 也就是所谓的事务了,通俗理解就是一件事情。从小,父母就教育咱们,作事情要善始善终,不能半途而废。 事务也是这样,不能作一半就不作了,要么作完,要么就不作。也就是说,事务必须是一个不可分割的总体,就像咱们在化学课里学到的原子,原子是构成物质的最小单位。因而,人们就概括出事务的第一个特性:原子性(Atomicity)。我靠,一点都不神秘嘛。mysql
特别是在数据库领域,事务是一个很是重要的概念,除了原子性之外,它还有一个极其重要的特性,那就是:一致性(Consistency)。也就是说,执行完数据库操做后,数据不会被破坏。打个比方,若是从 A 帐户转帐到 B 帐户,不可能由于 A 帐户扣了钱,而 B 帐户没有加钱吧。若是出现了这类事情,您必定会很是气愤,什么 diao 银行啊!spring
当咱们编写了一条 update 语句,提交到数据库的一刹那间,有可能别人也提交了一条 delete 语句到数据库中。也许咱们都是对同一条记录进行操做,能够想象,若是不稍加控制,就会出大麻烦来。咱们必须保证数据库操做之间是“隔离”的(线程之间有时也要作到隔离),彼此之间没有任何干扰。这就是:隔离性(Isolation)。要想真正的作到操做之间彻底没有任何干扰是很难的,因而乎,天天上班打酱油的数据库专家们,开始动脑筋了,“咱们要制定一个规范,让各个数据库厂商都支持咱们的规范!”,这个规范就是:事务隔离级别(Transaction Isolation Level)。能定义出这样牛逼的规范真的挺不容易的,其实说白了就四个级别:sql
1.READ_UNCOMMITTED数据库
2.READ_COMMITTED安全
3.REPEATABLE_READ并发
4.SERIALIZABLE高并发
千万不要去翻译,那只是一个代号而已。从上往下,级别愈来愈高,并发性愈来愈差,安全性愈来愈高,反之则反。工具
当咱们执行一条 insert 语句后,数据库必需要保证有一条数据永久地存放在磁盘中,这个也算事务的一条特性, 它就是:持久性(Durability)。性能
概括一下,以上一共提到了事务的 4 条特性,把它们的英文单词首字母合起来就是:ACID,这个就是传说中的“事务 ACID 特性”!spa
真的是很是牛逼的特性啊!这 4 条特性,是事务管理的基石,必定要透彻理解。此外还要明确,这四个家伙当中,谁才是老大?
其实想一想也就清楚了:原子性是基础,隔离性是手段,持久性是目的,真正的老大就是一致性。数据不一致了,就至关于“江湖乱套了”。因此说,这三个小弟都是跟着“一致性”这个老大混,为他全心全意服务。
这四个家伙当中,其实最难理解的反倒不是一致性,而是隔离性。由于它是保证一致性的重要手段,是工具,使用它不能有半点差池,不然后果自负!怪不得数据库行业专家们都要来研究所谓的事务隔离级别了。其实,定义这四个级别就是为了解决数据在高并发下所产生的问题,那又有哪些问题呢?
三类数据读问题
1.Dirty Read(脏读)
2.Unrepeatable Read(不可重复读)
3.Phantom Read(幻读)
两类数据更新问题
1.第一类丢失更新
2.第二类丢失更新
首先看看“脏读”,看到“脏”这个字,我就想到了恶心、肮脏。数据怎么可能脏呢?其实也就是咱们常常说的“垃圾数据”了。好比说,有两个事务,它们在并发执行(也就是竞争)。看看如下这个表格,您必定会明白我在说什么:
余额应该为 1100 元才对!请看 T6 时间点,事务 A 此时查询余额为 900 元,这个数据就是脏数据,它是事务 A 形成的,明显事务没有进行隔离,渗过来了,乱套了。
因此脏读这件事情是很是要不得的,必定要解决掉!让事务之间隔离起来才是硬道理。
不可重复读又怎么解释呢?仍是用相似的例子来讲明:
事务 A 其实除了查询了两次之外,其余什么事情都没有作,结果钱就从 1000 变成 0 了,这就是重复读了。可想而知,这是别人干的,不是我干的。其实这样也是合理的,毕竟事务 B 提交了事务,数据库将结果进行了持久化,因此事务 A 再次读取天然就发生了变化。
这种现象基本上是能够理解的,但在有些变态的场景下倒是不容许的。毕竟这种现象也是事务之间没有隔离所形成的,但咱们对于这种问题,彷佛能够忽略。
幻读。我去!Phantom 这个单词不就是“幽灵、鬼魂”吗?刚看到这个单词时,真的把个人小伙伴们都给惊呆了。怪不得这里要翻译成“幻读”了,总不能翻译成“幽灵读”、“鬼魂读”吧。其实意义就是鬼在读,不是人在读,或者说搞不清楚为何,它就变了,很晕,真的很晕。仍是用一个示例来讲话吧:
银行工做人员,每次统计总存款,都看到不同的结果。不过这也确实也挺正常的,总存款增多了,确定是这个时候有人在存钱。可是若是银行系统真的这样设计,那算是玩完了。这一样也是事务没有隔离所形成的,但对于大多数应用系统而言,这彷佛也是正常的,能够理解,也是容许的。银行里那些恶心的那些系统,要求很是严密,统计的时候,甚至会将全部的其余操做给隔离开,这种隔离级别就算很是高了(估计要到 SERIALIZABLE 级别了)。
第一类丢失更新,A事务撤销时,把已经提交的B事务的更新数据覆盖了。这种错误可能形成很严重的问题,经过下面的帐户取款转帐就能够看出来:
可是,在当前的四种任意隔离级别中,都不会发生该状况,否则绝对乱套,我都没提交事务只是撤销,就把别人的给覆盖了,这也太恐怖了。
第二类丢失更新,B事务覆盖A事务已经提交的数据,形成A事务所作操做丢失
概括一下,以上提到了事务并发所引发的跟读取数据有关的问题,各用一句话来描述一下:
1.脏读:事务 A 读取了事务 B 未提交的数据,并在这个基础上又作了其余操做。
2.不可重复读:事务 A 读取了事务 B 已提交的更改数据。
3.幻读:事务 A 读取了事务 B 已提交的新增数据。
第一条是坚定抵制的,后两条在大多数状况下可不做考虑。
这就是为何必需要有事务隔离级别这个东西了,它就像一面墙同样,隔离不一样的事务。看下面这个表格,您就清楚了不一样的事务隔离级别能处理怎样的事务并发问题:
根据您的实际需求,再参考这张表,最后肯定事务隔离级别,应该再也不是一件难事了。
JDBC 也提供了这四类事务隔离级别,但默认事务隔离级别对不一样数据库产品而言,倒是不同的。咱们熟知的MySQL 数据库的默认事务隔离级别就是 READ_COMMITTED,Oracle、SQL Server、DB2等都有有本身的默认值。我认为 READ_COMMITTED 已经能够解决绝大多数问题了,其余的就具体状况具体分析吧。
提示:在 Java.sql.Connection 类中可查看全部的隔离级别。
咱们知道 JDBC 只是链接 Java 程序与数据库的桥梁而已,那么数据库又是怎样隔离事务的呢?其实它就是“锁”这个东西。当插入数据时,就锁定表,这叫“锁表”;当更新数据时,就锁定行,这叫“锁行”。固然这个已经超出了咱们今天讨论的范围,因此仍是留点空间给咱们的 DBA 同窗吧,省得他没啥好写的了。
除了 JDBC 给咱们提供的事务隔离级别这种解决方案之外,还有哪些解决方案能够完善事务管理功能呢?
不妨看看 spring 的解决方案吧,其实它是对 JDBC 的一个补充或扩展。它提供了一个很是重要的功能,就是:事务传播行为(TransactionPropagation Behavior)。
确实够牛逼的,Spring 一会儿就提供了 7 种事务传播行为,这 7 种行为一出现,真的是亮瞎了!
1.PROPAGATION_REQUIRED
2.RROPAGATION_REQUIRES_NEW
3.PROPAGATION_NESTED
4.PROPAGATION_SUPPORTS
5.PROPAGATION_NOT_SUPPORTED
6.PROPAGATION_NEVER
7.PROPAGATION_MANDATORY
看了 Spring 参考手册以后,更是晕了,这究竟是在干吗?
首先要明确的是,事务是从哪里来?传播到哪里去?答案是,从方法 A 传播到方法 B。Spring 解决的只是方法之间的事务传播,那状况就多了,好比:
这样就是 4 种了,还有 3 种特殊状况。仍是用个人 Style 给你们作一个分析吧:
@Transactional void A(){ } @Transactional void B(){ A(); }
假设事务从方法 A 传播到方法 B,您须要面对方法 B,问本身一个问题:
方法 A 有事务吗?
看到我上面这段解释,小伙伴们是否已经感觉到,被打通任督二脉的感受?多读几遍,体会一下,就是您本身的东西了。
须要注意的是 PROPAGATION_NESTED,不要被它的名字所欺骗,Nested(嵌套),因此凡是在相似方法 A 调用方法 B 的时候,在方法 B 上使用了这种事务传播行为,若是您真的那样作了,那您就错了。由于您错误地觉得 PROPAGATION_NESTED 就是为方法嵌套调用而准备的,其实默认的 PROPAGATION_REQUIRED 就能够帮助您,作您想要作的事情了。
Spring 给咱们带来了事务传播行为,这确实是一个很是强大而又实用的功能。除此之外,也提供了一些小的附加功能,好比:
1.事务超时(Transaction Timeout):为了解决事务时间太长,消耗太多的资源,因此故意给事务设置一个最大时常,若是超过了,就回滚事务。
2.只事务(Readonly Transaction):为了忽略那些不须要事务的方法,好比读取数据,这样能够有效地提升一些性能。
在 Spring 配置文件中使用:
<tx:annotation-driven/>
@Transactional
public void xxx() {
...
}
可在 @Transactional 注解中设置:事务隔离级别、事务传播行为、事务超时时间、是否只读事务。
简直是太完美了,太优雅了!
最后,有必要对本文的内容作一个总结,免费赠送一张高清无码思惟导图: