1、数据库事务的隔离级别有4个,由低到高依次为Read uncommitted、Read committed、Repeatable read、Serializable,这四个级别可以逐个解决脏读、不可重复读、幻读这几类问题。
隔离级别 | 脏读 | 不可重复读 | 幻读 | 更新丢失 | 并发模型 | |
Read uncommitted | 是 | 是 | 是 | 否 | 悲观锁 | |
Read committed | 否 | 是 | 是 | 是 | 悲观锁 | |
Repeatable read | 否 | 否 | 是 | 是 | 悲观锁 | |
Serializable | 否 | 否 | 否 | 否 | 悲观锁 |
(1). ISOLATION_READ_UNCOMMITTED:这是事务最低的隔离级别,它允许令外一个事务可以看到这个事务未提交的数据。
这种隔离级别会产生脏读,不可重复读和幻像读。
(2). ISOLATION_READ_COMMITTED:保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据
(3). ISOLATION_REPEATABLE_READ:这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。
它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了避免下面的情况产生(不可重复读)。
(4). ISOLATION_SERIALIZABLE:这是花费最高代价但是最可靠的事务隔离级别。事务被处理为顺序执行。
除了防止脏读,不可重复读外,还避免了幻像读。
-----------------------------------------------------------------------------------------
用MySQL数据库来介绍这几种隔离机制:
2、Read uncommitted(读取未提交内容)
2.1 所有事物可以读取其他未提交的事务,但是不能进行写操作。
2.2 避免了丢失更新的问题,却可能出现脏读的问题:一个事务A读取了另一个未提交的事务B,可能根据这个结果,出现了某些错误。
2.3 这种隔离机制一般很少有,性能上跟其他的隔离机制比起来,没那么好。
-------------------------------代码
2.3.1先创建一个表
create table user(
id int(10),
name varchar(10),
age int(2),
sex varchar(10)
);
查看一下现在的隔离级别
select @@tx_isolation;
我的隔离级别
mysql> select @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set
修改隔离级别
set tx_isolation = 'READ-UNCOMMITTED';
查看修改之后的隔离级别
mysql> select @@tx_isolation;
+------------------+
| @@tx_isolation |
+------------------+
| READ-UNCOMMITTED |
+------------------+
1 row in set
开启事务A
start transaction;
查看现在的表user,
select * from user;
开启事务B
start transaction;
事务B:添加一条记录
insert into user(id,name,sex,age) values(1,'xx',2,'男');
事务A:查看现在user表的内容
select * from user;
mysql> select * from user;
+----+------+-----+-----+
| id | name | age | sex |
+----+------+-----+-----+
| 1 | xx | 2 | ? |
+----+------+-----+-----+
1 row in set
事务B回滚,未提交
rollback;
查看user表;
mysql> select * from user;
Empty set
事务A:查看user表
mysql> select * from user;
Empty set
通过以上可以看到:
脏读意味着在事务A中,可以读到事务B未提交的事务,一旦数据发生变化,事务A都可以看到,因此,如果根据这个数据进行解析,一旦事务B回滚,那么数据肯定不正确。
3、Read Committed(读取提交内容)
3.1 大部分数据库默认的隔离级别(MySQL默认级别为
Repeatable read)
3.2 读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。
3.3 避免了脏读,但是会出现不可重复读。一个事务A读取了一组数据,事务B修改了这组数据,事务A又读取了一遍。结果两次读取的数据不同。出现了交叉的情况。
-------------------------------代码
先修改一下隔离级别
set tx_isolation='READ-COMMITTED';
查看一下修改过后的级别
select @@tx_isolation;
开启一个事务A
start stansaction;
查询一下表user;
mysql> select * from user;
Empty set
开启事务B(这两个事务交叉了)
start transaction;
添加一行数据,但是并不提交
insert into user(id,name,sex,age) values(1,'xx',2,'男');
然后在查看一下事务A,看是否能看见表user
mysql> select * from user;
Empty set
结果没有。
然后我们提交事务B
commit;
在查看一下事务A。这次看能否看见表user的内容
mysql> select * from user;
+----+------+-----+-----+
| id | name | age | sex |
+----+------+-----+-----+
| 1 | xx | 0 | 2 |
+----+------+-----+-----+
1 row in set
如上结果,我们发现
这样子,在两个事务交叉的期间,事务B提交了事务以后,两次查询的select语句查出的结果不同。这就出现了不可重复读的问题。
4、Repeatable read(可重复读)
4.1 这是MySQL默认的隔离级别。
4.2 可以保证并发环境下,多个事务并发读取到相同的数据。
4.3 解决了脏读、不可重复读,但是有可能出现幻读。
-------------------代码
更改隔离级别
set tx_isolation='REPEATABLE-READ';
开启事务A,但是不提交事务:
start transaction;
查询一下表user现在的数据
mysql> select * from user;
+----+------+-----+-----+
| id | name | age | sex |
+----+------+-----+-----+
| 1 | xx | 0 | 2 |
+----+------+-----+-----+
1 row in set
开启事务B
start transaction;
插入一行数据
insert into user(id,name,age,sex) values(2,'xxx',3,'女');
事务B:查询一下表user;
mysql> select * from user;
+----+------+-----+-----+
| id | name | age | sex |
+----+------+-----+-----+
| 1 | xx | 0 | 2 |
| 2 | xxx | 3 | ? |
+----+------+-----+-----+
2 rows in set
事务A;查询一下现在的表user的数据
mysql> select * from user;
+----+------+-----+-----+
| id | name | age | sex |
+----+------+-----+-----+
| 1 | xx | 0 | 2 |
+----+------+-----+-----+
1 row in set
这表明了事务A在事务B未提交之前是无法看见事务B修改的数据的,解决了不可重复读的问题。
事务B:提交
commit;
事务A:查看一下表user
mysql> select * from user;
+----+------+-----+-----+
| id | name | age | sex |
+----+------+-----+-----+
| 1 | xx | 0 | 2 |
| 2 | xxx | 3 | ? |
+----+------+-----+-----+
2 rows in set
在这里,可以看见repeatable read解决了事务之间的不可重复读的问题,但是会出现幻读的问题。
5、Serializable(序列化)
5.1 最高级别的隔离机制
5.2 提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行。
5.3 性能最低,耗价最高,用的很少
5.4 会导致大量的超时现象和锁竞争
----------------------代码
修改隔离级别
set tx_isolation='Serializable';
开启事务A
start transaction;
事务A:查询一下表user;
mysql> select * from user;
+----+------+-----+-----+
| id | name | age | sex |
+----+------+-----+-----+
| 1 | xx | 0 | 2 |
| 2 | xxx | 3 | ? |
+----+------+-----+-----+
2 rows in set
开启事务B
start transaction;
事务B:向表user中插入一组数据。
mysql> insert into user(id,name,age,sex) values(3,'xxx',4,'女');
1205 - Lock wait timeout exceeded; try restarting transaction
可以看见,在事务A未提交之前,事务B是无法进行表user的更改的。