首页 > 代码库 > mysql事务隔离级别测试

mysql事务隔离级别测试

隔离性
mysql提供了4种不同的隔离级别以支持多版本并发控制(MVCC)
较低级别的隔离通常可以执行更高的并发,系统的开销也更低
read uncommited(未提交读)
read commited(提交读)
repeatable read(可重复读)
serializable(可串行化)
默认repeatable-read
建议最好不要修改默认的隔离级别,修改隔离级别会对mysql复制产生影响

isolation-table.sql

DROP TABLE IF EXISTS transaction_test;
CREATE TABLE transaction_test(
  id   INT UNSIGNED NOT NULL AUTO_INCREMENT,
  val  VARCHAR(20) NOT NULL,
  created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY(id)
) ENGINE=InnoDB DEFAULT CHARSET latin1;

#初始化数据
insert into  transaction_test(val) values (a),(b),(c);

1.REPEATABLE-READ(可重复读)

select @@global.tx_isolation,@@session.tx_isolation;
+-----------------------+------------------------+
| @@global.tx_isolation | @@session.tx_isolation |
+-----------------------+------------------------+
| READ-UNCOMMITTED      | READ-UNCOMMITTED       |
+-----------------------+------------------------+
set @@global.tx_isolation=REPEATABLE-READ;
mysql> select @@global.tx_isolation,@@session.tx_isolation;
+-----------------------+------------------------+
| @@global.tx_isolation | @@session.tx_isolation |
+-----------------------+------------------------+
| REPEATABLE-READ       | REPEATABLE-READ        |
+-----------------------+------------------------+

第一个会话窗口

start transaction;
select * from transaction_test;
select sleep(20);
insert into transaction_test (val) values (@@session.tx_isolation);
select * from transaction_test;
commit;

在第一个会话正在运行时在第二个会话窗口里运行

start transaction;
insert into transaction_test (val) values (x),(y),(z);
select  * from transaction_test;
commit;

均执行结束后

mysql> select  * from transaction_test;
+----+-----------------+---------------------+
| id | val             | created             |
+----+-----------------+---------------------+
|  1 | a               | 2017-01-08 20:38:06 |
|  2 | b               | 2017-01-08 20:38:06 |
|  3 | c               | 2017-01-08 20:38:06 |
|  4 | x               | 2017-01-08 20:38:41 |
|  5 | y               | 2017-01-08 20:38:41 |
|  6 | z               | 2017-01-08 20:38:41 |
|  7 | REPEATABLE-READ | 2017-01-08 20:38:59 |
+----+-----------------+---------------------+
7 rows in set (0.00 sec)

可以看到在第二个会话中,添加了3个新行,并在第一个会话事务完成前提交了数据

会话1里的执行状态

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from transaction_test;
+----+-----+---------------------+
| id | val | created             |
+----+-----+---------------------+
|  1 | a   | 2017-01-08 20:45:48 |
|  2 | b   | 2017-01-08 20:45:48 |
|  3 | c   | 2017-01-08 20:45:48 |
+----+-----+---------------------+
3 rows in set (0.00 sec)

mysql> select sleep(20);
+-----------+
| sleep(20) |
+-----------+
|         0 |
+-----------+
1 row in set (20.00 sec)

mysql> insert into transaction_test (val) values (@@session.tx_isolation);
Query OK, 1 row affected (0.00 sec)

mysql> select * from transaction_test;
+----+-----------------+---------------------+
| id | val             | created             |
+----+-----------------+---------------------+
|  1 | a               | 2017-01-08 20:45:48 |
|  2 | b               | 2017-01-08 20:45:48 |
|  3 | c               | 2017-01-08 20:45:48 |
|  7 | REPEATABLE-READ | 2017-01-08 20:47:16 |
+----+-----------------+---------------------+
4 rows in set (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.06 sec)

第一个会话事务不知道第二个会话事务已完成的结果

2.READ-COMMITTED(读取提交内容)

TRUNCATE transaction_test;
insert into  transaction_test(val) values (a),(b),(c);
set @@global.tx_isolation=READ-COMMITTED;
mysql> select @@global.tx_isolation,@@session.tx_isolation;
+-----------------------+------------------------+
| @@global.tx_isolation | @@session.tx_isolation |
+-----------------------+------------------------+
| READ-COMMITTED        | READ-COMMITTED         |
+-----------------------+------------------------+

在session1中运行

start transaction;
select * from transaction_test;
select sleep(20);
insert into transaction_test (val) values (@@session.tx_isolation);
select * from transaction_test;
commit;

同时在session2中运行

start transaction;
insert into transaction_test (val) values (x),(y),(z);
select  * from transaction_test;
commit;

session1的运行显示

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from transaction_test;
+----+-----+---------------------+
| id | val | created             |
+----+-----+---------------------+
|  1 | a   | 2017-01-08 21:11:26 |
|  2 | b   | 2017-01-08 21:11:26 |
|  3 | c   | 2017-01-08 21:11:26 |
+----+-----+---------------------+
3 rows in set (0.00 sec)

mysql> select sleep(20);
+-----------+
| sleep(20) |
+-----------+
|         0 |
+-----------+
1 row in set (20.00 sec)

mysql> insert into transaction_test (val) values (@@session.tx_isolation);
Query OK, 1 row affected (0.01 sec)

mysql> select * from transaction_test;
+----+----------------+---------------------+
| id | val            | created             |
+----+----------------+---------------------+
|  1 | a              | 2017-01-08 21:11:26 |
|  2 | b              | 2017-01-08 21:11:26 |
|  3 | c              | 2017-01-08 21:11:26 |
|  4 | x              | 2017-01-08 21:11:50 |
|  5 | y              | 2017-01-08 21:11:50 |
|  6 | z              | 2017-01-08 21:11:50 |
|  7 | READ-COMMITTED | 2017-01-08 21:12:02 |
+----+----------------+---------------------+
7 rows in set (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.07 sec)

可以看到,在session1事务期间,事务内的数据结果发生了改变,以反映一个已提交的事务
这仅仅是一个演示,可能应用程序永远不会需要在某个事务期间,多次选择相同的信息
作为隔离级别的默认值,REPEATABLE-READ是最广泛使用和测试的隔离级别,应当优先于READ-COMMITED使用

3.READ-UNCOMMITED(读取未提交内容)

 

set @@global.tx_isolation=READ-UNCOMMITED;

 

运行之前的测试语句
先执行会话1
在执行会话2 不提交事务
等会话1执行完再提交会话2的事务
会发现会话1读到了会话2未提交的事务

4.SERIALIZABLE(序列化,可串行化)

 

此种情况下,在共享模式下对表进行了有效锁定,这会迫使事务阻塞其它事务
按照之前的测试语句
对于第一个会话,输出效果相同 ,造成第二个会话中insert的延迟,该insert将会被阻塞,直到第一个会话完成
set @@global.tx_isolation=‘SERIALIZABLE‘;

 

会话1

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> select * from transaction_test;
+----+-----+---------------------+
| id | val | created             |
+----+-----+---------------------+
|  1 | a   | 2017-01-08 22:34:59 |
|  2 | b   | 2017-01-08 22:34:59 |
|  3 | c   | 2017-01-08 22:34:59 |
+----+-----+---------------------+
3 rows in set (0.00 sec)

mysql> select sleep(20);
+-----------+
| sleep(20) |
+-----------+
|         0 |
+-----------+
1 row in set (20.00 sec)

mysql> insert into transaction_test (val) values (@@session.tx_isolation);
Query OK, 1 row affected (0.00 sec)

mysql> select * from transaction_test;
+----+--------------+---------------------+
| id | val          | created             |
+----+--------------+---------------------+
|  1 | a            | 2017-01-08 22:34:59 |
|  2 | b            | 2017-01-08 22:34:59 |
|  3 | c            | 2017-01-08 22:34:59 |
|  7 | SERIALIZABLE | 2017-01-08 22:36:12 |
+----+--------------+---------------------+
4 rows in set (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.34 sec)

会话2

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into transaction_test (val) values (x),(y),(z);
Query OK, 3 rows affected (13.04 sec)
Records: 3  Duplicates: 0  Warnings: 0

mysql> select  * from transaction_test;
+----+--------------+---------------------+
| id | val          | created             |
+----+--------------+---------------------+
|  1 | a            | 2017-01-08 22:34:59 |
|  2 | b            | 2017-01-08 22:34:59 |
|  3 | c            | 2017-01-08 22:34:59 |
|  4 | x            | 2017-01-08 22:35:59 |
|  5 | y            | 2017-01-08 22:35:59 |
|  6 | z            | 2017-01-08 22:35:59 |
|  7 | SERIALIZABLE | 2017-01-08 22:36:12 |
+----+--------------+---------------------+
7 rows in set (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.39 sec)

值得注意的是
auto_increment 列的顺序
所显示的顺序是实际语句初始化的顺序,
因为auto_increment列的值是作为对该表保持的一个单独的内部全局变量值而进行管理的,在执行期间,可以在内部独占的互斥体内获得该值
它独立于隔离级别而发挥作用,与实际sql数据的serializable相反,即数据直到加锁事务完成insert操作后,才会释放第二个会话的锁定

 

表的外键和级联

 

DROP TABLE IF EXISTS trans_parent;
CREATE TABLE trans_parent (
  id   INT UNSIGNED NOT NULL AUTO_INCREMENT,
  val  VARCHAR(10) NOT NULL,
PRIMARY KEY (id),
UNIQUE KEY (val)
) ENGINE=InnoDB DEFAULT CHARSET latin1;

外键约束
DROP TABLE IF EXISTS trans_child;
CREATE TABLE trans_child (
  id        INT UNSIGNED NOT NULL AUTO_INCREMENT,
  parent_id INT UNSIGNED NOT NULL,
  created   TIMESTAMP NOT NULL,
PRIMARY KEY (id),
INDEX (parent_id),
FOREIGN KEY (parent_id) REFERENCES trans_parent(id)
) ENGINE=InnoDB DEFAULT CHARSET latin1;
看一张表的索引
show index from trans_child;
看一张表的外键
use INFORMATION_SCHEMA;

select TABLE_NAME,COLUMN_NAME,CONSTRAINT_NAME,REFERENCED_TABLE_NAME,REFERENCED_COLUMN_NAME from KEY_COLUMN_USAGE where table_name = trans_child;


+-------------+-------------+--------------------+-----------------------+------------------------+
| TABLE_NAME  | COLUMN_NAME | CONSTRAINT_NAME    | REFERENCED_TABLE_NAME | REFERENCED_COLUMN_NAME |
+-------------+-------------+--------------------+-----------------------+------------------------+
| trans_child | id          | PRIMARY            | NULL                  | NULL                   |
| trans_child | parent_id   | trans_child_ibfk_1 | trans_parent          | id                     |
+-------------+-------------+--------------------+-----------------------+------------------------+
如果删除trans_parent里的一条与trans_child有外键关联的表 会报错
级联约束
alter table trans_child
drop foreign key trans_child_ibfk_1,
add foreign key (parent_id) references trans_parent(id) on delete cascade;

delete from trans_parent where id=1;//trans_child表里parent_id=1的纪录也会被删除

 

mysql事务隔离级别测试