当多个线程都开启事务操做数据库中的数据时,数据库系统要能进行隔离操做,以保证各个线程获取数据的准确性, 因此, 对于不一样的事务,采用不一样的隔离级别 会 有不一样的结果。数据库
若是不考虑事务的隔离性, 那么 会发生 下表所示的 3 种问题:并发
不可重复读 是因为 事务并发修改 同一条 记录 致使的 ,要避免这种状况,最简单的方法就是对要修改的记录加锁,这 会 致使锁竞争加重 ,影响性能。另外一种方法是经过 MVCC 能够在无锁的状况下,避免不可重复读。oracle
幻读是因为并发事务增长记录致使的,这个不能像不可重复读经过记录加锁解决,由于对于新增的记录根本没法加锁。须要将事务串行化,才能避免幻读。ide
在 SQL 标准中定义了 4 种隔离级别,每一种级别都规定了一个事务中所作的修改,哪些是在事务内和事务间可见的,哪些是不可见的。较低级别的隔离一般能够执行更高的并发,系统的开销也更低。 SQL 标准定义的四个隔离级别为: Read Uncommitted ( 未提交读 ) 、 Read Committed (提交读)、 Repeatable Read (可重复读)、 Serializable (可串行化) ,下面分别介绍。性能
不一样的隔离级别有不一样的现象,并有不一样的锁 和 并发机制,隔离级别越高,数据库的并发性 能 就越差, 4 种事 隔离级别与并发性能的关系:线程
若是没有事务隔离,会出现什么样的状况?3d
假设咱们如今有这样一张表(T),里面记录了不少牛人的名字,咱们不进行事务的隔离看看会发生什么呢?blog
第一天,事务A访问了数据库,它干了一件事情,往数据库里加上了新来的牛人的名字,可是没有提交事务。索引
insert into T values (4, '牛D');
这时,来了另外一个事务B,他要查询全部牛人的名字。事务
select Name from T;
这时,若是没有事务之间没有有效隔离,那么事务B返回的结果中就会出现“牛D”的名字。这就是“脏读(dirty read)”。
次日,事务A访问了数据库,他要查看ID是1的牛人的名字,因而执行了
select Name from T where ID = 1;
这时,事务B来了,由于ID是1的牛人更名字了,因此要更新一下,而后提交了事务。
update T set Name = '不牛' where ID = 1;
接着,事务A还想再看看ID是1的牛人的名字,因而又执行了
select Name from T where ID = 1;
结果,两次读出来的ID是1的牛人名字居然不相同,这就是不可重复读(unrepeatable read)。
第三天,事务A访问了数据库,他想要看看数据库的牛人都有哪些,因而执行了
select * from T;
这时候,事务B来了,往数据库加入了一个新的牛人。
insert into T values(4, '牛D');
这时候,事务A忘了刚才的牛人都有哪些了,因而又执行了。
select * from T;
结果,第一次有三个牛人,第二次有四个牛人。
相信这个时候事务A就蒙了,刚才发生了什么?这种状况就叫“幻读(phantom problem)”。
为了防止出现脏读、不可重复读、幻读等状况,咱们就须要根据咱们的实际需求来设置数据库的隔离级别。下面介绍下这方面内容。
数据库事务隔离级别
数据库事务隔离级别分为四种(级别递减):
一、Serializable (串行化):最严格的级别,事务串行执行,资源消耗最大;
二、REPEATABLE READ(重复读) :保证了一个事务不会修改已经由另外一个事务读取但未提交(回滚)的数据。避免了“脏读取”和“不可重复读取”的状况,但不能避免“幻读”,可是带来了更多的性能损失。
三、READ COMMITTED (提交读):大多数主流数据库的默认事务等级,保证了一个事务不会读到另外一个并行事务已修改但未提交的数据,避免了“脏读取”,但不能避免“幻读”和“不可重复读取”。该级别适用于大多数系统。
四、Read Uncommitted(未提交读) :事务中的修改,即便没有提交,其余事务也能够看获得,会致使“脏读”、“幻读”和“不可重复读取”。
脏读、不可重复读、幻读:
一个数据库可能拥有多个访问客户端,这些客户端并发访问数据库时,若没有采起必要的隔离措施,存在如下问题,这些问题分为5类,包括3类数据读问题:脏读、不可重复读和幻读。两类数据更新问题:第一类丢失更新、第二类丢失更新。
一、脏读
A事务读取B事务还没有提交的更改数据,并在这个数据的基础上进行操做,这时候若是事务B回滚,那么A事务读到的数据是不被认可的。例如常见的取款事务和转帐事务:
二、不可重复读
不可重复读是指A事务读取了B事务已经提交的更改数据。假如A在取款事务的过程当中,B往该帐户转帐100,A两次读取的余额发生不一致。
三、幻读
A事务读取B事务提交的新增数据,会引起幻读问题。幻读通常发生在计算统计数据的事务中,例如银行系统在同一个事务中两次统计存款帐户的总金额,在两次统计中,恰好新增了一个存款帐户,存入了100,这时候两次统计的总金额不一致。
注意:不可重复读和幻读的区别是:前者是指读到了已经提交的事务的更改数据(修改或删除),后者是指读到了其余已经提交事务的新增数据。对于这两种问题解决采用不一样的办法,防止读到更改数据,只需对操做的数据添加行级锁,防止操做中的数据发生变化;二防止读到新增数据,每每须要添加表级锁,将整张表锁定,防止新增数据(oracle采用多版本数据的方式实现)。
四、通俗解释:
脏读:所谓的脏读,其实就是读到了别的事务回滚前的脏数据。好比事务B执行过程当中修改了数据X,在未提交前,事务A读取了X,而事务B却回滚了,这样事务A就造成了脏读。
也就是说,当前事务读到的数据是别的事务想要修改为为的可是没有修改为功的数据。
不可重复读:事务A首先读取了一条数据,而后执行逻辑的时候,事务B将这条数据改变了,而后事务A再次读取的时候,发现数据不匹配了,就是所谓的不可重复读了。
也就是说,当前事务先进行了一次数据读取,而后再次读取到的数据是别的事务修改为功的数据,致使两次读取到的数据不匹配,也就照应了不可重复读的语义。
幻读:事务A首先根据条件索引获得N条数据,而后事务B改变了这N条数据以外的M条或者增添了M条符合事务A搜索条件的数据,致使事务A再次搜索发现有N+M条数据了,就产生了幻读。
也就是说,当前事务读第一次取到的数据比后来读取到数据条目少。
不可重复读和幻读比较:
二者有些类似,可是前者针对的是update或delete,后者针对的insert。
为何会出现“脏读”?由于没有“select”操做没有规矩。
为何会出现“不可重复读”?由于“update”操做没有规矩。
为何会出现“幻读”?由于“insert”和“delete”操做没有规矩。
“读未提(Read Uncommitted)”能预防啥?啥都预防不了。
“读提交(Read Committed)”能预防啥?使用“快照读(Snapshot Read)”,避免“脏读”,可是可能出现“不可重复读”和“幻读”。
“可重复读(Repeated Red)”能预防啥?使用“快照读(Snapshot Read)”,锁住被读取记录,避免出现“脏读”、“不可重复读”,可是可能出现“幻读”。
“串行化(Serializable)”能预防啥?排排坐,吃果果,有效避免“脏读”、“不可重复读”、“幻读”,不过效果谁用谁知道。