一文带你领略MySQL事务的隔离级别的强大

文章目录

    • 一、隔离级别理论
      • 1.概念
      • 2.读取方式
        • 脏读(Dirty Read)
        • 不可重复读(nonrepeatable)
        • 幻读(Phantom)
        • 加锁读
      • 3.隔离性种类
        • READ UNCOMMITTED(未提交读)
        • READ COMMITTED(提交读)
        • REPEATABLE READ(可重复读)
        • SERIALIZABLE(可串行化)
    • 二、实现隔离级别
      • 实验环境
      • 1.READ UNCOMMITTED(未提交读)
      • 2.READ COMMITTED(提交读)
      • 3.REPEATABLE READ(可重复读)
      • 4.SERIALIZABLE(可串行化)
    • 三、死锁

一、隔离级别理论

1.概念

在事务的ACID特性(ACID详解)中的隔离性(isolation)通常来说是一个事务所做的修改在最终提交之前是不可见的,也就是一个用户在事务开启的时候修改文件的内容其他用户在查看该文件时是不可见的。那么如果用户需要修改实时更新呢该怎么办,其实隔离性不只是一种,这里提出的隔离性只是其中的一种,而隔离性的读取方式也是不同的,隔离性是根据读取数据的方式来进行划分的。

2.读取方式

脏读(Dirty Read)

开启两个事务,在一个事务中修改的值,第二个事务也可以看得到

不可重复读(nonrepeatable)

开启两个事务,在一个事务中修改的值并提交后,第二个事务也能看得到,不提交只修改是看不到的

幻读(Phantom)

开启两个事务,在一个事务中修改值并提交,而第二个终端在事务中还是保留原来的值,不会改变称为幻读

加锁读

当一个事务修改值时,另一个事务不可以同时进行操作,只可以等待

3.隔离性种类

READ UNCOMMITTED(未提交读)

支持脏读
支持不可重复读
支持幻读

READ COMMITTED(提交读)

支持不可重复读
支持幻读

REPEATABLE READ(可重复读)

支持幻读

SERIALIZABLE(可串行化)

支持加锁读

二、实现隔离级别

实验环境

开启两个终端分别是(杰克)与(露丝),后面将以杰克和露丝来命名终端名称。

create database test;
create table list1(id int primary key, name char(10));
insert into list1(id,name) values(1,'成龙'),(2,'李小龙');
#以上内容复制即可搭建成功实验环境

mysql> select * from list1;
+----+--------+
| id | name   |
+----+--------+
|  1 | 成龙   |
|  2 | 李小龙 |
+----+--------+
2 rows in set (0.00 sec)
mysql> desc list1;
+-------+----------+------+-----+---------+-------+
| Field | Type     | Null | Key | Default | Extra |
+-------+----------+------+-----+---------+-------+
| id    | int      | NO   | PRI | NULL    |       |
| name  | char(10) | YES  |     | NULL    |       |
+-------+----------+------+-----+---------+-------+
2 rows in set (0.00 sec)

1.READ UNCOMMITTED(未提交读)

1)杰克:修改隔离级别并查看;开启事务

mysql> set session transaction isolation level read uncommitted;
mysql> select @@transaction_isolation ;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| READ-UNCOMMITTED        |
+-------------------------+
1 row in set (0.00 sec)
mysql> start transaction;

2)露丝:开启事务

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

3)杰克:插入数据

mysql> insert into list1 values(3,'李连杰');
#插入数据

3)露丝:查看表内数据

#查看现在表内的内容
mysql> select * from list1;
+----+--------+
| id | name   |
+----+--------+
|  1 | 成龙   |
|  2 | 李小龙 |
|  3 | 李连杰 |
+----+--------+
3 rows in set (0.00 sec)

4)杰克:将事务回滚

mysql> rollback;
#回滚事务

5)露丝

#查看现在的数据已经回滚到没有修改之前的数据了
mysql> select * from list1;
+----+--------+
| id | name   |
+----+--------+
|  1 | 成龙   |
|  2 | 李小龙 |
+----+--------+
2 rows in set (0.00 sec)

由上得知:在杰克终端中开启事务并插入数据后,在露丝端开启事务也可以查看到,这就称为脏读。

2.READ COMMITTED(提交读)

1)杰克:修改隔离级别并查看;开启事务

mysql> set session transaction isolation level read committed;
mysql> select @@transaction_isolation ;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| READ-COMMITTED          |
+-------------------------+
1 row in set (0.00 sec)
mysql> start transaction;

2)露丝:开启事务更换隔离级别

mysql> set session transaction isolation level read committed;
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

3)杰克:插入数据

mysql>  insert into list1 values(3,'李连杰');

4)露丝:查看数据

mysql> select * from list1;
+----+--------+
| id | name   |
+----+--------+
|  1 | 成龙   |
|  2 | 李小龙 |
+----+--------+
2 rows in set (0.00 sec)

5)杰克:提交数据

mysql> commit;

6)露丝:查看数据

mysql> select * from list1;
+----+--------+
| id | name   |
+----+--------+
|  1 | 成龙   |
|  2 | 李小龙 |
|  3 | 李连杰 |
+----+--------+
3 rows in set (0.00 sec)

由上得知:当杰克开启事务并插入数据后,露丝不能马上看到必须要杰克提交数据后露丝才能查看到数据,这就是提交读,由于一个事务从开始直到提交之前,所有的任何修改对其他事务都是不可见的,又称为不可重复读。

3.REPEATABLE READ(可重复读)

1)杰克:修改隔离级别并查看;开启事务

mysql> set session transaction isolation level REPEATABLE READ;
mysql> select @@transaction_isolation ;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| REPEATABLE-READ         |
+-------------------------+
1 row in set (0.00 sec)
mysql> start transaction;

2)露丝:开启事务更换隔离级别

mysql> set session transaction isolation level REPEATABLE READ;
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

3)杰克:插入数据

mysql> insert into list1 values(4,"周杰伦");

4)露丝:查看数据

mysql> select * from list1;
+----+--------+
| id | name   |
+----+--------+
|  1 | 成龙   |
|  2 | 李小龙 |
|  3 | 李连杰 |
+----+--------+
3 rows in set (0.00 sec)

5)杰克:提交事务

mysql> commit;

6)露丝:提交事务,查看数据

mysql> commit;
mysql> select * from list1;
+----+--------+
| id | name   |
+----+--------+
|  1 | 成龙   |
|  2 | 李小龙 |
|  3 | 李连杰 |
|  4 | 周杰伦 |
+----+--------+
4 rows in set (0.00 sec)

由上得知:当杰克开启事务并插入数据后,开启事务的露丝并没有直接查看到杰克插入的数据,当杰克提交事务后,露丝也没有查看到数据,而是露丝将自己的事务提交后才看到杰克插入的数据,这就说明可重复读了,而将这不能读取最新数据的现象称之为幻读。这种隔离级别是MySQL默认的隔离级别。

4.SERIALIZABLE(可串行化)

1)杰克:修改隔离级别并查看;开启事务

mysql> set session transaction isolation level SERIALIZABLE;
mysql> select @@transaction_isolation ;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| SERIALIZABLE            |
+-------------------------+
1 row in set (0.00 sec)
mysql> start transaction;

2)露丝:开启事务更换隔离级别

mysql> set session transaction isolation level SERIALIZABLE;
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

3)杰克:删除并查看数据

mysql> delete from list1 where id=4;
mysql> select * from list1;
+----+--------+
| id | name   |
+----+--------+
|  1 | 成龙   |
|  2 | 李小龙 |
|  3 | 李连杰 |
+----+--------+
3 rows in set (0.00 sec)

4)露丝:查看数据

#发现一直在等待,说明当杰克在操作这个数据时,其他事务是不可以查看的。
mysql> select * from list1;
^C -- query aborted
ERROR 1317 (70100): Query execution was interrupted

5)杰克:提交数据

mysql> commit;

5)露丝:查看数据

#查看成功,并且为最新数据
mysql> select * from list1;
+----+--------+
| id | name   |
+----+--------+
|  1 | 成龙   |
|  2 | 李小龙 |
|  3 | 李连杰 |
+----+--------+
3 rows in set (0.00 sec)

由上得知:可串行化这一隔离级别是不可以两个事务同时访问同一个数据的,这将导致大量超市的超时和锁争用的问题。只有在非常需要确保数据的一致性而且可以接受没有并发的情况下才考虑使用该级别。

三、死锁

死锁是指两个或者多个事务在同一资源上互相占用,并请求锁定对方占用的资源,从而导致恶性循环的现象。如果在两个事务终端上同时输入下面这两条语句,那么将会陷入死循环,因为两个事务互相都陷入了对方的锁,数据库系统实现了各种死锁检测和死锁超时机制。InnoDB引擎对死锁的做法是将持有排他锁的事务进行回滚
杰克:

mysql> start transaction;
mysql> update list1 set name='程程' where id=3;
mysql> update list1 set name='橙橙' where id=2;

露丝:

mysql> start transaction;
mysql> update list1 set name='城城' where id=2;
mysql> update list1 set name='澄澄' where id=3;

你可能感兴趣的:(mysql)