1.事务的特性:ACID
如果一个数据库声称支持事务的操作,那么该数据库必须要具备以下四个特性:
(1)原子性( Atomicity):事物作为单个逻辑单元执行的一组操作, 事务中的所有操作要么全部执行成功,要么全部执行失败。要么全都执行,要么全都不执行。
(2)一致性(Consistency):事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于同一个状态。
拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。
如果数据库系统在运行中发生故障,有些事务尚未完成就被迫中断,这些未完成事务对数据库所做的修改有一部分已写入物理数据库,有一部分没有写入,这时数据库的状态就处于一种不正确的状态,或者说是不一致的状态。
如果事务没有原子性的保证,那么在发生系统故障的情况下,数据库就有可能处于不一致的状态。
(3)隔离性(Isolation):一个事务的执行不能对其它的事务造成干扰。即一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰。
比如当多个用户并发访问数据库操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
即要达到这么一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。
关于事务的隔离性数据库提供了多种隔离级别,第3点会讲到。
(4)持久性(Durability):也称永久性,指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的,即便是在数据库系统遇到故障的情况下也不会出现丢失提交事务的操作。接下来的其它操作或故障不应该对其执行结果有任何影响。
2.事务并发操作可能会引发的问题
当多个线程都开启事务操作数据库中的数据时,数据库系统要能进行隔离操作,以保证各个线程获取数据的准确性。如果不考虑事务的隔离性,会发生的以下几种问题:
(1)脏读:就是读到的数据是还没有被提交的数据。
比如:指在访问数据库中,事务T1将某一值修改,但是未提交,然后事务T2读取该值,T1由于某种原因撤销对该值的修改,就会导致T2所读取到的数据是无效的,就会出现脏读。
(2)不可重复读:是指在对于数据库中的某个数据,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询间隔,要查询的数据被另一个事务修改并提交了。
例如事务T1在读取某一数据,而事务T2立马修改了这个数据并且提交事务给数据库,事务T1再次读取该数据就得到了不同的结果,这就发生了不可重复读。
在某些情况下,不可重复读并不是问题,比如我们多次查询某个数据当然以最后查询得到的结果为主。但在另一些情况下就有可能发生问题,例如对于同一个数据A和B依次查询就可能不同,A和B就可能打起来了……
不可重复读比如:singo拿着工资卡去消费,系统读取到卡里确实有2000元,而此时她的老婆也正好在网上转账,把singo工资卡的2000元转到另一账户,并在 singo之前提交了事务,当singo扣款时,系统检查到singo的工资卡已经没有钱,扣款失败,singo十分纳闷,明明卡里有钱,为何......
脏读和不可重复读的区别是:脏读是某一事务读取了另一个事务未提交的脏数据,而不可重复读则是读取了一个事务前后提交的数据。
(3)幻读:幻读是事务非独立执行时发生的一种现象。例如事务T1第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。而操作事务T1的用户如果再查看刚刚修改的数据,会发现还存在没有修改的数据行,就好象发生了幻觉一样。出现这种情况的原因是两个事物同时对一个表执行操作的后果。
又或者:第一次读没有做更新操作,读之前没有插入记录,第一次读之后第二次读之前插入了一条数据,第二次读之后发现了插入的记录,导致数据不一致。
不可重复读和幻读都是读取了另一条已经提交的事务(这点就脏读不同),所不同的是不可重复读查询的都是同一个数据项,有一个事务对该数据项进行操作,而幻读针对的是一批数据整体(比如数据的个数),可能包含对多个数据项的操作。
3.事务的隔离级别
数据库事务的隔离级别有4个,由低到高依次为Read uncommitted 、Read committed 、Repeatable read 、Serializable ,这四个级别可以逐个解决脏读 、不可重复读 、幻读这几类问题。
√: 可能出现 ×: 不会出现
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
Read uncommitted | √ | √ | √ |
Read committed | × | √ | √ |
Repeatable read | × | × | √ |
Serializable | × | × | × |
(1)Read uncommitted
如果就是读到的数据是还没有被提交的,会出现脏读。
当隔离级别设置为Read uncommitted时,就可能出现脏读,避免脏读就是把事物的隔离级别设置为下一个隔离级别。
(2)Read commited
当隔离级别设置为Read committed时,一个事务要等另一个事务提交后才能读取数据,每个事务读到的数据是已经被写入的,避免了脏读,但是可能会造成不可重复读。
不可重复读就是读一个数据时有事物进行更改数据操作时必须先等更新操作完成,那再读的时候就不能读取到原来的了。不可重复读对应的问题是对一个数据的update。
大多数数据库的默认级别就是Read committed,比如Sql Server , Oracle。避免不可重复读就是把事物的隔离级别设置为下一个隔离级别。
(3)Repeatable read
当隔离级别设置为Repeatable read时,读取数据的过程中就不允许修改操作。可重复读对应的问题是insert幻读。
幻读就是:第一次读之前没有插入记录,第一次读之后第二次读之前插入了一条数据,导致数据不一致。
解决幻读问题就是进行序列化,使用事务隔离级别的下一个隔离级别。
Mysql的默认隔离级别就是Repeatable read。
(4)Serializable
Serializable是事物的最高隔离级别,在该级别下,事物串行化顺序执行,避免脏读,不可重复读和幻读,但是效率低,消耗性能,所以一般不使用。