mysql 事务级别

测试的mysql的版本号:

mysql> select version();

+-----------+
| version() |
+-----------+
| 8.0.11    |
+-----------+
1 row in set (0.02 sec)

随便创建一个表:比如就是my_test,字段有三个,id(int),name(varchar),age(int)

CREATE TABLE `my_test` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `age` int(11) DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

 

mysql 默认的事务级别为repeatable read (可以重复读)

查看当前mysql全局的事务级别:

select @@global.TRANSACTION_isolation  

mysql> select @@global.TRANSACTION_isolation;

+--------------------------------+
| @@global.TRANSACTION_isolation |
+--------------------------------+
| REPEATABLE-READ                |
+--------------------------------+
1 row in set (0.00 sec)

 

修改全局的事务级别:

set GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED


关闭事务的主动提交: set global autocommit=off

 

记住global的设置后,重新开启的终端才起作用

 

此时查看:

mysql> SET GLOBAL TRANSACTION ISOLATION LEVEL read uncommitted;
Query OK, 0 rows affected (0.00 sec)

// 记住global的设置后,重新开启的终端才起作用,查看隔离级别
mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| READ-UNCOMMITTED        |
+-------------------------+
1 row in set (0.00 sec)

mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| READ-UNCOMMITTED        |
+-------------------------+
1 row in set (0.00 sec)

 

查看表中的数据:

mysql> select * from my_test;

+----+----------+------+
| id | name     | age  |
+----+----------+------+
|  1 | jeffchan |   20 |
|  2 | caraliu  |   18 |
+----+----------+------+
2 rows in set (0.00 sec)

 

1、当事务级别是 READ UNCOMMITTED (有脏读,幻读,不可重复读)

现在开启mysql,然后打开两个操作界面:   

那么就有两个需要自己手动提交的事务了:记录为A , B

两个终端同时开启事务,命令上面的数字为执行的顺序:

A事务:

1
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

4
mysql> update my_test set name="jeffchan1" where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

5
mysql> select * from my_test;
+----+-----------+------+
| id | name      | age  |
+----+-----------+------+
|  1 | jeffchan1 |   20 |
|  2 | caraliu   |   18 |
+----+-----------+------+
2 rows in set (0.00 sec)

=========================================================================================

B事务:

2
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

3
mysql> select * from my_test;
+----+-----------+------+
| id | name      | age  |
+----+-----------+------+
|  1 | jeffchan  |   20 |
|  2 | caraliu   |   18 |
+----+-----------+------+
2 rows in set (0.00 sec)

6
mysql> select * from my_test;
+----+-----------+------+
| id | name      | age  |
+----+-----------+------+
|  1 | jeffchan1 |   20 |
|  2 | caraliu   |   18 |
+----+-----------+------+
2 rows in set (0.00 sec)

A: 现在在事务A中修改表的记录:update my_test set name="jeffchan1" where id = 1 

B:  此时起始事务还是没有提交,但是现在在B事务中:select * from my_test 会发现id=1的那条记录的name的值给改了,为

jeffchan1,这就是所谓的脏读(因为有可能事务A失败回滚了,此时事务B这个值其实时无效的,就是错误的值)

 

因为读未提交是最低级别的隔离,所以脏读(读取到未提交的数据)会存在(因为这个级别是基本不用的),不可重复读也存在,幻读也会存在。

 

 

 2、当事务级别时: READ COMMITTED (解决了脏读问题,幻读和不可重复读还是有)

set GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED   

设置完后,一定记得要重启开启新的终端。

A终端 :

1
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

3
mysql> select * from my_test;
+----+----------+------+
| id | name     | age  |
+----+----------+------+
|  1 | jeffchan |   20 |
|  2 | caraliu  |   18 |
+----+----------+------+
2 rows in set (0.00 sec)

mysql> update my_test set name="jeffchan2" where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

4
mysql> select * from my_test;
+----+-----------+------+
| id | name      | age  |
+----+-----------+------+
|  1 | jeffchan2 |   20 |
|  2 | caraliu   |   18 |
+----+-----------+------+
2 rows in set (0.00 sec)

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

=========================================================================================

B终端:

2
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

5
mysql> select * from my_test;
+----+----------+------+
| id | name     | age  |
+----+----------+------+
|  1 | jeffchan |   20 |
|  2 | caraliu  |   18 |
+----+----------+------+
2 rows in set (0.00 sec)

A终端提交事务:此时查看发现修改了
6
mysql> select * from my_test;
+----+-----------+------+
| id | name      | age  |
+----+-----------+------+
|  1 | jeffchan2 |   20 |
|  2 | caraliu   |   18 |
+----+-----------+------+
2 rows in set (0.00 sec)

 

 事务A:  update my_test set name="jaychou1" where id = 2 ,不提交事务

 事务B:  select * from my_test 发现id=2的数据没有被修改,所以已经没有脏读了,但是此时你将事务A提交,此时

 再进行 select * from my_test 发现id=2的数据被修改了。也是同一个事务中两次读的数据是不一样的,这个叫不可以重复读,不  可以在一个事务中重复读某个记录,可能会出现不一致现象),此时幻读存在。

 

3、当事务级别时: REPEATABLE READ (解决了脏读问题和不可重复读问题,幻读还是存在)

set GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ

设置完后,一定记得要重启开启新的终端。

 

A事务:

1
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

3
mysql> update my_test set name="jeffchan3" where id = 1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

4
mysql> select * from my_test;
+----+-----------+------+
| id | name      | age  |
+----+-----------+------+
|  1 | jeffchan3 |   20 |
|  2 | caraliu   |   18 |
|  3 | hello     |   12 |
+----+-----------+------+
3 rows in set (0.00 sec)


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

=========================================================================================


B事务:

2
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

3
mysql> select * from my_test;
+----+-----------+------+
| id | name      | age  |
+----+-----------+------+
|  1 | jeffchan2 |   20 |
|  2 | caraliu   |   18 |
|  3 | hello     |   12 |
+----+-----------+------+
3 rows in set (0.00 sec)


6
mysql> select * from my_test;
+----+-----------+------+
| id | name      | age  |
+----+-----------+------+
|  1 | jeffchan2 |   20 |
|  2 | caraliu   |   18 |
|  3 | hello     |   12 |
+----+-----------+------+
3 rows in set (0.00 sec)

 

很明显A事务修改(我试了下,插入和修改也是一样的结果)记录的时候,不管是否提交事务,B事务都不会读到A事务中修改的记录,所以不管B事务在事务中重复多少次读取记录,都是一样的,所以叫可以重复读,在读取数据的层面,不会出现幻读的情况。

但是update记录时,不做条件限制的话,会update到别的事务新插入的其他记录,当前事务查看时,没看到那条记录,其实它已经存在了,然后做范围修改的时候,会修改到它,好像出现幻觉一样。每一个命令上面加一个执行的顺序。

 

A事务:

1
mysql> begin; 
Query OK, 0 rows affected (0.00 sec)

3
mysql> select * from my_test;
+----+-----------+------+
| id | name      | age  |
+----+-----------+------+
|  1 | jeffchan3 |   20 |
|  2 | caraliu   |   18 |
|  3 | hello     |   13 |
+----+-----------+------+
3 rows in set (0.00 sec)

6
mysql> select * from my_test;
+----+-----------+------+
| id | name      | age  |
+----+-----------+------+
|  1 | jeffchan3 |   20 |
|  2 | caraliu   |   18 |
|  3 | hello     |   13 |
+----+-----------+------+
3 rows in set (0.00 sec)

8
mysql> update my_test set age = 40;
Query OK, 4 rows affected (0.00 sec)
Rows matched: 4  Changed: 4  Warnings: 0

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

10
mysql> select * from my_test;
+----+-----------+------+
| id | name      | age  |
+----+-----------+------+
|  1 | jeffchan3 |   40 |
|  2 | caraliu   |   40 |
|  3 | hello     |   40 |
|  4 | hi        |   40 |
+----+-----------+------+
4 rows in set (0.00 sec)

=============================================================================================

B事务:

2
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

4
mysql> select * from my_test;
+----+-----------+------+
| id | name      | age  |
+----+-----------+------+
|  1 | jeffchan3 |   20 |
|  2 | caraliu   |   18 |
|  3 | hello     |   13 |
+----+-----------+------+
3 rows in set (0.00 sec)

5
mysql> insert into my_test values(4,"hi",30);
Query OK, 1 row affected (0.00 sec)

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


可以看到当执行到第10步的时候,查出来的结果,事务B新插入的记录也被修改了。所以幻读依然存在。

 

4、当事务级别时: SERIALIZABLE (不存在并发问题,但是效率很低)

set GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE

设置完后,一定记得要重启开启新的终端。

 

A事务:

1
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

5 这一步会阻塞
mysql> select * from my_test;

ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

==========================================================================================

B事务:

2
mysql> begin;
Query OK, 0 rows affected (0.00 sec)

3
mysql> select * from my_test;
+----+-----------+------+
| id | name      | age  |
+----+-----------+------+
|  1 | jeffchan3 |   40 |
|  2 | caraliu   |   40 |
|  3 | hello     |   40 |
|  4 | hi        |   40 |
+----+-----------+------+
4 rows in set (0.00 sec)

4
mysql> insert into my_test values(5,"good",13);
Query OK, 1 row affected (0.00 sec)

发现在第五步的时候,会阻塞查询的指令,所以没有并发问题,但是会很消耗性能,所以也是基本不使用的隔离级别

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章