隔离级别(4个)
读未提交:read uncommitted
读已提交:read committed
可重复读:repeatable read
串行化:serializable
- read uncommitted:
先查询id=1的points,结果是10
在第一个session中查询points,使用单步执行,执行到第二行,也就是仅仅设置隔离等级
use sql_store;
set transaction isolation level read uncommitted;
start transaction;
select points from customers where customer_id = 1;
commit;
然后到另一个session执行到update,也就是更新points但是不提交
use sql_store;
start transaction;
update customers set points = 20 where customer_id = 1;
commit;
回到第一个session,接着往下执行,发现points变成了20,因为隔离等级设置的是read uncommitted,所以能读取到未提交的数据。在现实中,如果第二个人在提交事务时,系统或者数据库出现问题,导致提交事务异常,那么第一个人读取到的就是虚假的数据,这也叫做脏读。要解决这个问题,我们需要更高的隔离等级。
- read committed(oracle默认)
重复上面的操作,但把隔离等级修改成read committed,发现读取到的是原来的10,这是因为只能读取已提交的事务。
解决了“脏读”,但read committed又存在一个问题,就是不能重复读取,下面看例子
如果查询同一个值两次,在第一个查询结束后,结果是20,在另一个session中,修改了points为30并提交,那么第二次查询到的值就是30,这会造成读取不稳定的问题,因此,需要更高的隔离等级
use sql_store;
set transaction isolation level read committed;
start transaction;
//第一个查询
select points from customers where customer_id = 1;
//第二个查询
select points from customers where customer_id = 1;
commit;
-
repeatable read(MySql默认)
重复read committed的操作,但把隔离等级修改成repeatable read
发现第一次查询到的是30,在另一个session中,修改了points为40并提交,但第二次查询到的值还是30,这解决了读取不稳定的问题。这能解决大部分并发问题了,但还存在一个问题,就是“幻读”,什么是“幻读”呢?
在第一个session中,查询,只有一个用户
在第二个session中,把id=1的客户地区修改成‘VA’并提交
use sql_store;
start transaction;
update customers
set state = 'VA'
where customer_id = 1;
commit;
回到第一个session,第二次查询,理论上应该是显示两个客户的,但因为隔离等级是repeatable read,读取到的只有1个用户,造成"幻读"(提交事务,再查询一次,才会出来2个用户)。
因此,需要更高的隔离等级
- serializable
这是最高的隔离等级,可以解决所有的并发问题。
在第一个session中,把隔离等级改成serializable,然后开始事务,查询后,到另一个session里,修改id=3的用户state为’VA’但不提交,这时候回到第一个session,执行查询时发现在转圈圈,也就是他在等待第二个session的事务的提交,这样就能解决“幻读”的问题。
但是这样效率特别低,所以一般仅仅在特定需要的事务上设定这个隔离等级