事务的隔离级别

目录

如何理解隔离级别?

隔离级别

查看隔离级别

设置隔离级别

读未提交

读提交

可重复读

串行化

总结


如何理解隔离级别?

  • mysql 服务可能同时被多个客户端进程访问,访问的方式以事务进行。

  • 一个事务可能由多条 sql 组成,那么就说明事务就三个状态,执行前、执行中、执行后。而所谓的原子性就是让用户看到要么是执行前,要么是执行后,不会看到执行者的过程,而执行中出现问题可以随时回滚。所以单个事务表现出来就是原子的。

  • 但是事务都是由执行过程的,那么多事务在执行的时候都是有可能会互相影响的,比如多个事务同时访问一张表,甚至是一行记录。

  • 就比如说现在你要去学习,你界定要么就学到最好,要么干脆都别学,而中间的过程并不关心,而你怎么样学习别人并不关心,只要知道你学习到最好就可以了,但是你在学习的过程中容易受到打扰,所以此时你学习的时候就去一个别人不会轻易打扰到你的地方,而这就是隔离性,所以隔离性也是有差别的,如果你去图书馆,那么隔离性就好一点,如果去大街上,那么就几乎没有任何隔离性。

  • 数据库中为了保证事务在执行过程中不受到干扰,就有一个特性——隔离性。

  • 而数据库允许书屋收到不同级别的干扰,也就是隔离级别。

隔离级别

  • 读未提交【Read Uncommitted】: 在该隔离级别,所有的事务都可以看到其他事务没有提交的执行结果。(实际生产中不可能使用这种隔离级别的),但是相当于没有任何隔离性,也会有很多并发问题,如脏读,幻读,不可重复读等。

  • 读提交【Read Committed】 :该隔离级别是大多数数据库的默认的隔离级别(不是 MySQL 默认的)。它满足了隔离的简单定义:一个事务只能看到其他的已经提交的事务所做的改变。这种隔离级别会引起不可重复读,即一个事务执行时,如果多次 select, 可能得到不同的结果。

  • 可重复读【Repeatable Read】: 这是 MySQL 默认的隔离级别,它确保同一个事务,在执行中,多次读取操作数据时,会看到同样的数据行。但是会有幻读问题。

  • 串行化【Serializable】: 这是事务的最高隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决了幻读的问题。它在每个读的数据行上面加上共享锁,。但是可能会导致超时和锁竞争(这种隔离级别太极端,实际生产基本不使用)

查看隔离级别

查看隔离级别的方法有很多下面一一介绍

  • 查看全局的隔离级别

  • mysql> select @@global.tx_isolation;
    +-----------------------+
    | @@global.tx_isolation |
    +-----------------------+
    | READ-UNCOMMITTED      |
    +-----------------------+
    1 row in set, 1 warning (0.00 sec)

    我们前面设置了全局的隔离级别是读未提交,所以现在查询到还是读未提交

  • 查看会话的隔离级别

  • mysql> select @@session.tx_isolation;
    +------------------------+
    | @@session.tx_isolation |
    +------------------------+
    | READ-UNCOMMITTED       |
    +------------------------+
    1 row in set, 1 warning (0.00 sec)

    一般会话的隔离级别会根据全局的隔离基本来初始化,所以如果设置了全局的隔离级别,如果想让会话的隔离级别也生效需要重启一下客户端mysql。

  • 查看会话的隔离级别(上面是全写,下面是缩写)

  • mysql> select @@tx_isolation;
    +------------------+
    | @@tx_isolation   |
    +------------------+
    | READ-UNCOMMITTED |
    +------------------+
    1 row in set, 1 warning (0.00 sec)

设置隔离级别

上面是查看隔离级别,我们发现隔离级别既有全局的也有局部的,所以我们设置隔离级别也有这两种

  • 设置全局隔离级别

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

    上面讲全局的隔离级别设置为了读提交,下面查一下隔离级别看一下

    mysql> select @@global.tx_isolation;
    +-----------------------+
    | @@global.tx_isolation |
    +-----------------------+
    | READ-COMMITTED        |
    +-----------------------+
    1 row in set, 1 warning (0.00 sec)

  • 设置局部隔离级别

  • mysql> select @@tx_isolation;
    +----------------+
    | @@tx_isolation |
    +----------------+
    | SERIALIZABLE   |
    +----------------+
    1 row in set, 1 warning (0.00 sec)

    设置局部隔离级别为串行化,下面查询查看

    mysql> select @@tx_isolation;
    +----------------+
    | @@tx_isolation |
    +----------------+
    | SERIALIZABLE   |
    +----------------+
    1 row in set, 1 warning (0.00 sec)

局部的隔离级别只会影响当前会话,而全局的隔离级别会影响全局的隔离性。

读未提交

下面开始查看读未提交,读未提交是在两个启动的事务中,只要其中一个事务一旦修改或者跟新了数据,那么在另一个事务中也是可以看到的,这种隔离性几乎没有,而在实际中也几乎没有人会采用这种隔离级别。

首先我们先将隔离级别设置为读未提交

mysql> set session transaction isolation level read uncommitted;
Query OK, 0 rows affected (0.00 sec)
​
mysql> select @@tx_isolation;
+------------------+
| @@tx_isolation   |
+------------------+
| READ-UNCOMMITTED |
+------------------+
1 row in set, 1 warning (0.00 sec)

下面我们开启两个事务,然后在其中一个插入数据,另一个里面查看

mysql> insert into account values(1, '张三', 1111.11);
Query OK, 1 row affected (0.00 sec)

插入后查看

mysql> select * from account;
+----+--------+---------+
| id | name   | balance |
+----+--------+---------+
|  1 | 张三   | 1111.11 |
+----+--------+---------+
1 row in set (0.00 sec)

在其中一个正在运行的事务插入了一条数据,结果还未等该事务结束,被另一正在运行的事务就查看到该事务插入的数据了,这显然是一个问题,按照正常情况下,该事务都未结束,正处于执行中的状态,不应该被其他事务看到执行结果。

而一个事务在执行中,读到另一执行中的事务的修改或者跟新,单未commit 的数据,这种现象就叫脏读。

读提交

设置隔离级别为读提交

mysql> set session transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)
​
mysql> select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| READ-COMMITTED |
+----------------+
1 row in set, 1 warning (0.00 sec)

插入数据

mysql> insert into account values(2, '李四', 2222.22);
Query OK, 1 row affected (0.00 sec)
 
  

查看

mysql> select * from account;
+----+--------+---------+
| id | name   | balance |
+----+--------+---------+
|  1 | 张三   | 1111.11 |
+----+--------+---------+
1 row in set (0.00 sec)

这里查看到没有,下面提交一下继续查看

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

查看

mysql> select * from account;
+----+--------+---------+
| id | name   | balance |
+----+--------+---------+
|  1 | 张三   | 1111.11 |
|  2 | 李四   | 2222.22 |
+----+--------+---------+
2 rows in set (0.00 sec)

这里查看到了,负责查看的事务中,查看多次account表,结果发现查询的内容是不一样的,那么这是问题吗?

显然也是一个问题,而该问题就是不可重复读

在同一个事务中,不同的时间段(还在事务的操作中),读取到了不同的值,那么这种就叫做不可重复读。

可重复读

设置隔离级别为可重复读

mysql> set session transaction isolation level repeatable read;
Query OK, 0 rows affected (0.00 sec)
​
mysql> select @@tx_isolation;
+-----------------+
| @@tx_isolation  |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set, 1 warning (0.00 sec)

当前设置可重复读成功

插入数据

mysql> insert into account values(3, '王五', 8892.50);
Query OK, 1 row affected (0.00 sec)

查询

mysql> select * from account;
+----+--------+---------+
| id | name   | balance |
+----+--------+---------+
|  1 | 张三   | 1111.11 |
|  2 | 李四   | 2222.22 |
+----+--------+---------+
2 rows in set (0.00 sec)

下面我们提交后继续查询

mysql> select * from account;
+----+--------+---------+
| id | name   | balance |
+----+--------+---------+
|  1 | 张三   | 1111.11 |
|  2 | 李四   | 2222.22 |
+----+--------+---------+
2 rows in set (0.00 sec)

提交后查询和我们前面查询到的结果还是相同,这个就是可重复读。

其实可重复读有幻读问题,但是 mysql 已经解决了。

串行化

设置当前隔离级别为串行化

mysql> set session transaction isolation level serializable;
Query OK, 0 rows affected (0.00 sec)
​
mysql> select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| SERIALIZABLE   |
+----------------+
1 row in set, 1 warning (0.00 sec)

串行化,就是所有的修改或者跟新的事务都串行运行,也就是一个事务结束后下一个事务才开始,但是对于只进行select来说一般不会串行化,因为select 不会修改数据

插入数据

mysql> insert into account values(4, '赵六', 5732.00);
Query OK, 1 row affected (0.00 sec)

查询

mysql> select * from account;

上面插入数据后,直接查询就阻塞了,下面马上对插入数据的那个事务进行 commit

mysql> select * from account;
+----+--------+---------+
| id | name   | balance |
+----+--------+---------+
|  1 | 张三   | 1111.11 |
|  2 | 李四   | 2222.22 |
|  3 | 王五   | 8892.50 |
|  4 | 赵六   | 5732.00 |
+----+--------+---------+
4 rows in set (32.96 sec)

commit 后查询结果出来了

下面我们只进行 select

mysql> select * from account;
+----+--------+---------+
| id | name   | balance |
+----+--------+---------+
|  1 | 张三   | 1111.11 |
|  2 | 李四   | 2222.22 |
|  3 | 王五   | 8892.50 |
|  4 | 赵六   | 5732.00 |
+----+--------+---------+
4 rows in set (0.00 sec)
mysql> select * from account;
+----+--------+---------+
| id | name   | balance |
+----+--------+---------+
|  1 | 张三   | 1111.11 |
|  2 | 李四   | 2222.22 |
|  3 | 王五   | 8892.50 |
|  4 | 赵六   | 5732.00 |
+----+--------+---------+
4 rows in set (0.00 sec)

只进行 select 的话,并不会阻塞

下面我们可以试一下 写-写 并发

mysql> update account set name='赵6' where id=4;
Query OK, 1 row affected (31.12 sec)
Rows matched: 1  Changed: 1  Warnings: 0
mysql> insert into account values(5, '田七', 888);
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

当在 serializable 的环境下,同时写-写并发就会中断掉一个事务,让另一个事务进行运行。

总结

下面我们总结一下上面的隔离级别以及他们的问题

隔离级别 脏读 不可重复读 幻读 加锁读
读未提交
读提交
可重复读 是(mysql 没有)
串行化

其中上面的问题里面我们有个不可重复读和幻读的问题,下面解释一下这两个问题

  • 不可重复读:主要表示的是在一个运行的事务中删除或者是跟新并且提交后,另一运行的事务读该数据多次读取结果不同

  • 幻读:主要表示的是插在一个运行的事务中插入数据并且提交后,另一事务读该数据发现结果不同

上面就是隔离级别的问题。

其实在实际中,一般读未提交和串行化基本不会用到,因为读未提交基本没有任何隔离性,但是并行高,而串行化就是直接让所有的事务串行的执行,所以效率太慢,在实际中一般还是用到的是读提交和可重复读,而mysql默认是可重复读,并且mysql还解决了可重复读的幻读问题。

你可能感兴趣的:(mysql,mysql)