事务的隔离级别
事务的隔离级别:read uncommitted,RU,读未提交。一个事务中,可以读取到其他事务未提交的变更。该级别属于脏读。read committed,RC,读已提交。一个事务中,可以读取到其他事务已经提交的变更。该级别允许幻读,不可重复读的发生。repetable read,RR,可重复读。一个事务中,直到事务结束前,都可以反复读取到事务一开始看到底数据,不会发生变化。该级别可保证事务一致性。serializable,SR,串行。即每次读都需要获得表级共享锁,每次写都加表级排它锁,两个会话间读写会相互阻塞。该级别会导致innodb表的并发特性丧失,变成myisam一样。如果没有事务控制的话,那么并发读写数据库会有什么隐患?脏读:一个事务按相同的查询条件重新读取以前检索过的数据,发现其他事务更新后达到了满足其查询条件的旧数据(此时它还未被提交),这种现象就称为“脏读”。简单讲就是一个会话读取到了另一个会话还未提交的事务。这种现象一定要避免发生,涉及到的事务隔离级别为RU。不可重复读:一个事务按相同的查询条件重新读取以前检索过的数据,发现其他事务更新后达到了满足其查询条件的旧数据(此时它已被提交),这种现象称为“不可重复读”。幻读:一个事务按相同的查询条件重新读取以前检索过的数据,发现其他事务插入了满足其查询条件的新数据(此时它已被提交),这种现象就称为“幻读”。
修改事务隔离级别
需要执行两条命令:mysql> show variables like '%iso%';
+---------------+-----------------+
| Variable_name | Value |
+---------------+-----------------+
| tx_isolation | REPEATABLE-READ |
+---------------+-----------------+
1 row in set (0.00 sec)
mysql> set global transaction isolation level read uncommitted;
Query OK, 0 rows affected (0.00 sec)
--隔离级别可以为:read uncommitted;read committed;repeatable read;serializable;
mysql> set session transaction isolation level read uncommitted;
Query OK, 0 rows affected (0.00 sec)
确认结果:mysql> show variables like '%iso%';
+---------------+------------------+
| Variable_name | Value |
+---------------+------------------+
| tx_isolation | READ-UNCOMMITTED |
+---------------+------------------+
1 row in set (0.00 sec)
脏读
在讲之前,先了解脏页的概念。脏页:指的是在缓冲池中已经被修改的页,但是还没有刷新到磁盘,即数据库实例内存中的页和磁盘中的页数据是不一致的。当然在刷新到磁盘之前,日志已经被写入到日志文件中。而所谓的脏数据,是在缓冲池中被修改的数据,并且还没有被提交的。对于脏页的读取,是非常正常的,脏页是因为数据库实例和磁盘的异步同步造成的,这并不影响数据的一致性,并且因为是异步的,因此可以带来性能的提高。而脏数据却不同,脏数据是指未提交的数据,如果读到了脏数据,即一个事务可以读到另一个事务未提交的数据,这显然违反了数据库的隔离性。读未提交(RU)隔离级别会出现脏读现象,示例:修改数据库的事务隔离级别:
mysql> set global transaction isolation level read uncommitted;
Query OK, 0 rows affected (0.00 sec)
mysql> set session transaction isolation level read uncommitted;
Query OK, 0 rows affected (0.00 sec)
mysql> show variables like '%iso%';
+---------------+------------------+
| Variable_name | Value |
+---------------+------------------+
| tx_isolation | READ-UNCOMMITTED |
+---------------+------------------+
1 row in set (0.00 sec)
会话1开启事务,插入数据,先不提交:mysql> use test;
Database changed
mysql> select * from t;
+------+------+
| id | name |
+------+------+
| 1 | aa |
| 2 | b |
| 3 | c |
| 4 | d |
+------+------+
4 rows in set (0.00 sec)
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into t values(1,'read-uncommited');
Query OK, 1 row affected (0.00 sec)
会话2连接到数据库,查看表t的数据,可以查询到未提交的数据:mysql> use test;
Database changed
mysql> select * from t;
+------+-----------------+
| id | name |
+------+-----------------+
| 1 | aa |
| 2 | b |
| 3 | c |
| 4 | d |
| 1 | read-uncommited |
+------+-----------------+
5 rows in set (0.00 sec)
脏读在生产环境是不允许出现的情况,所以一般情况下,事务隔离级别不选择RU。不可重复读
不可重复读:一个事务按相同的查询条件重新读取以前检索过的数据,发现其他事务更新后达到了满足其查询条件的旧数据(此时它已被提交),这种现象称为“不可重复读”。详细解释:在一个事务内多次读同一数据,在这个事务还没有结束时,另外一个事务也访问该同一数据。那么在第一个事务的两次读数据之间,由于第二个事务的修改,第一个事务两次读到的数据可能是不一样的,这样就发生了一个事务内两次读到的数据是不一样的,因此称为不可重复读。不可重复读和脏读的区别:脏读是读到未提交的数据,而不可重复读是读取到已经提交的数据,但是违反了数据库事务一致性的要求。在读已提交(RC)隔离级别,会出现不可重复读和幻读现象,对于事务一致性要求不高的,互联网类的公司,可以使用。实验,修改事务隔离级别:
mysql> set global transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)
mysql> set session transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)
mysql> show variables like '%iso%';
+---------------+----------------+
| Variable_name | Value |
+---------------+----------------+
| tx_isolation | READ-COMMITTED |
+---------------+----------------+
1 row in set (0.00 sec)
会话1开启事务,并执行更新操作:mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> update t set name='read-commited' where id=1;
Query OK, 2 rows affected (0.07 sec)
Rows matched: 2 Changed: 2 Warnings: 0
会话2开启事务,查询表t:mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from t;
+------+-----------------+
| id | name |
+------+-----------------+
| 1 | aa |
| 2 | b |
| 3 | c |
| 4 | d |
| 1 | read-uncommited |
+------+-----------------+
5 rows in set (0.00 sec)
并没有查看到更新数据,此时将会话1进行提交,会话2再次查询:mysql> commit;
Query OK, 0 rows affected (0.01 sec)
mysql> select * from t;
+------+---------------+
| id | name |
+------+---------------+
| 1 | read-commited |
| 2 | b |
| 3 | c |
| 4 | d |
| 1 | read-commited |
+------+---------------+
5 rows in set (0.00 sec)
结果数据提交后可以查看到提交之后的数据。总结:不可重复读的问题是可以接受的,因为读到的是已经提交的数据,本身不会带来很大的问题。因此很多数据库厂商(oracle、MSS)将其数据库事务隔离级别默认设置为read committed,这种隔离级别下允许不可重复读的现象。innodb存储引擎中,通过使用next-key lock算法来避免不可重复度的问题,在mysql官方文档中,将不可重复读定义为幻象问题。innodb默认的存储引擎的默认事务隔离级别是read repeatable,采用next-key lock算法,就避免了不可重复读现象。
幻读
幻读:一个事务按相同的查询条件重新读取以前检索过的数据,发现其他事务插入了满足其查询条件的新数据(此时它已被提交),这种现象就称为“幻读”。幻读概念(另外一个session读取到了新增数据的信息)。在RC隔离级别下,会话1开启事务,并新增数据:mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into t values(5,'huandu');
Query OK, 1 row affected (0.00 sec)
会话2查询表t:mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from t;
+------+---------------+
| id | name |
+------+---------------+
| 1 | read-commited |
| 2 | b |
| 3 | c |
| 4 | d |
| 1 | read-commited |
+------+---------------+
5 rows in set (0.00 sec)
会话1提交,会话2查询表t数据:mysql> commit;
Query OK, 0 rows affected (0.01 sec)
mysql> select * from t;
+------+---------------+
| id | name |
+------+---------------+
| 1 | read-commited |
| 2 | b |
| 3 | c |
| 4 | d |
| 1 | read-commited |
| 5 | huandu |
+------+---------------+
6 rows in set (0.00 sec)
查询到了新增并且提交的数据。这就是幻读。可重复读
MySQL默认的隔离级别,不允许出现幻读,脏读,不可重复读现象。在线交易类的系统,支付,很多都使用RR这个隔离级别。修改MySQL隔离级别为RR:mysql> set global transaction isolation level repeatable read;
Query OK, 0 rows affected (0.00 sec)
mysql> set session transaction isolation level repeatable read;
Query OK, 0 rows affected (0.00 sec)
mysql> show variables like '%iso%';
+---------------+-----------------+
| Variable_name | Value |
+---------------+-----------------+
| tx_isolation | REPEATABLE-READ |
+---------------+-----------------+
1 row in set (0.00 sec)
会话1开启事务,并插入数据:mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> insert into t values(6,'RR');
Query OK, 1 row affected (0.00 sec)
会话2开启事务,并查询数据:mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from t;
+------+---------------+
| id | name |
+------+---------------+
| 1 | read-commited |
| 2 | b |
| 3 | c |
| 4 | d |
| 1 | read-commited |
| 5 | huandu |
+------+---------------+
会话1提交事务,会话2查询数据:mysql> commit;
Query OK, 0 rows affected (0.01 sec)
mysql> select * from t;
+------+---------------+
| id | name |
+------+---------------+
| 1 | read-commited |
| 2 | b |
| 3 | c |
| 4 | d |
| 1 | read-commited |
| 5 | huandu |
+------+---------------+
6 rows in set (0.00 sec)
会话1提交以后,会话2没有查询到新增的数据,读取的数据还是原来的数据,这种现象称为可重复读。如果在会话2查看最新的数据,需要加for update选项:mysql> select * from t for update;
+------+---------------+
| id | name |
+------+---------------+
| 1 | read-commited |
| 2 | b |
| 3 | c |
| 4 | d |
| 1 | read-commited |
| 5 | huandu |
| 6 | RR |
+------+---------------+
7 rows in set (0.00 sec)
---如果不加for update,还是查看的原来的数据
mysql> select * from t;
+------+---------------+
| id | name |
+------+---------------+
| 1 | read-commited |
| 2 | b |
| 3 | c |
| 4 | d |
| 1 | read-commited |
| 5 | huandu |
+------+---------------+
6 rows in set (0.00 sec)
串行读,serializable,SR,串行。即每次读都需要获得表级共享锁,每次写都加表级排它锁,两个会话间读写会相互阻塞。该级别会导致innodb表的并发特性丧失,变成myisam一样。所以基本上不使用这种事务隔离级别。来自为知笔记(Wiz)