
事务隔离性理论
- 理解隔离性
MySQL服务可能会同时被多个客户端进程(线程)访问,访问的方式以事务方式进行 - 一个事务可能由多条SQL构成,也就意味着,任何一个事务,都有执行前,执行中,执行后的阶段。而所谓的原子性,其实就是让用户层,要么看到执行前,要么看到执行后。执行中出现问题,可以随时回滚。所以单个事务,对用户表现出来的特性,就是原子性。
- 但,毕竟所有事务都要有个执行过程,那么在多个事务各自执行多个SQL的时候,就还是有可能会出现互相影响的情况。比如:多个事务同时访问同一张表,甚至同一行数据。
- 数据库中,为了保证事务执行过程中尽量不受干扰,就有了一个重要特征:隔离性
- 数据库中,允许事务受不同程度的干扰,就有了一种重要特征:隔离级别
理解隔离性
- sql操作都是具有原子性的,update和select,谁先来谁就先跑
- 每一个人都只能看到自己活着时候世界所对应的样子,所以我们时间线一直在延展的时候,每个人看到的世界都应该是不一样的,这才符合自然规律,人与人之间具有隔离性,因此让每一个事务都看到最新的数据是不合理的,而是应该让每一个事务在他到来时看到他应该看到的数据。这就是隔离性的体现。
- 隔离性具体要隔离到什么程度,我们就有了隔离级别的概念
- 隔离性就是你可以看到你出生后的时间线的内容,不能看到你出生前的内容
- 隔离级别是你试衣服的时候要进试衣间,试鞋子就不用进试衣间
隔离级别
- 读未提交:在该隔离级别,所有的事务都可以看到其他事务没有提交的执行结果。(实际公司不会用这种),这种相当于没有隔离级别
- 读提交:一个事务commit之后,另一个事务可以看到这个事务的提交结果
- 可重复读:相当于一个事务做完操作后,另一个事务也做完操作后,两个事务都关闭后,重新启动事务才能看到结果,在操作的途中是看不到结果的,类比于两个小孩在浑浊的水中潜水,一个小孩要确认另一个小孩是否还在潜水要浮上水面才能知道结果,这是MySQL默认的隔离级别,在操作的途中看到的结果是相同的
- 串行化:事务最高的隔离级别,一个mysql执行完成后另一个mysql才能执行,会影响效率
- 隔离,基本都是通过锁实现的,不同的隔离级别,锁的使用是不同的
上面四种隔离级别关注的场景都是当有一个人在进行写的时候另一个人来读 (读写并发),读读的场景都用的是串行化,一个任务进行完才能进行下一个任务
事务隔离级别的设置和查看
1. 只影响当前事务的隔离级别,mysql会默认使用session的隔离级别
2. 全局的隔离级别对所有的事务都影响,全局的改了,当前会话的和另一个事务的都会改变(在重新登入后更改)
SELECT @@session.tx_isolation;
--查看会话(当前)全局隔级别(默认用全局的初始化)
set global transaction isolation level READ UNCOMMITTED;
-- 查看
mysql> SELECT @@global.tx_isolation;
--查看全局隔级别
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| REPEATABLE-READ |
+-----------------------+
1 row in set, 1 warning (0.00 sec)
mysql> SELECT @@session.tx_isolation;
--查看会话(当前)全局隔级别(默认用全局的初始化)
+------------------------+
| @@session.tx_isolation |
+------------------------+
| REPEATABLE-READ |
+------------------------+
1 row in set, 1 warning (0.00 sec)
mysql> SELECT @@tx_isolation;
--默认同上,跟上面的一个是同一个意思
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set, 1 warning (0.00 sec)
--设置
-- 设置当前会话 or 全局隔离级别语法
// transaction 事务
// isolation 隔离
SET [SESSION | GLOBAL] transaction isolation level
// 读可不可提交,读可提交,可重复读,串行化
{READ UNCOMMITTED | READ COMMITTED |
REPEATABLE READ | SERIALIZABLE}
--设置当前会话隔离性,另起一个会话,看不多,只影响当前会话
mysql> set session transaction isolation level serializable; -- 串行化
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @@global.tx_isolation; --全局隔离性还是RR
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| REPEATABLE-READ |
+-----------------------+
1 row in set, 1 warning (0.00 sec)
mysql> SELECT @@session.tx_isolation; --会话隔离性成为串行化
+------------------------+
| @@session.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 global transaction isolation level READ UNCOMMITTED;
Query OK, 0 rows affected (0.00 sec)
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> SELECT @@tx_isolation;
+------------------+
| @@tx_isolation |
+------------------+
| READ-UNCOMMITTED |
+------------------+
1 row in set, 1 warning (0.00 sec)
-- 注意,如果没有现象,关闭mysql客户端,重新连接。
设置当前事务的隔离级别为读提交
事务隔离级别
读未提交
- 脏读:一个事务在执行中,读到另一个执行中事务的更新(或其他操作)但是未commit的数据,两个事务并发执行
- 几乎没有加锁,虽然效率高,但是问题太多,严重不建议采用
--终端A
-- 设置隔离级别为 读未提交
mysql> set global 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> select * from account;
+----+--------+----------+
| id | name | blance |
+----+--------+----------+
| 1 | 张三 | 100.00 |
| 2 | 李四 | 10000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)
mysql> begin; --开启事务
Query OK, 0 rows affected (0.00 sec)
mysql> update account set blance=123.0 where id=1; --更新指定行
Query OK, 1 row affected (0.05 sec)
Rows matched: 1 Changed: 1 Warnings: 0
--没有commit哦!!!
--终端B mysql> begin;
mysql> select * from account;
+----+--------+----------+
| id | name | blance |
+----+--------+----------+
| 1 | 张三 | 123.00 | --读到终端A更新但是未commit的数据[insert,delete同样]
| 2 | 李四 | 10000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)
读提交(不可重复读)
-- 终端A
mysql> set global transaction isolation level read committed;
Query OK, 0 rows affected (0.00 sec)
--重启客户端
mysql> select * from account; --查看当前数据
+----+--------+----------+
| id | name | blance |
+----+--------+----------+
| 1 | 张三 | 123.00 |
| 2 | 李四 | 10000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)
mysql> begin; --手动开启事务,同步的开始终端B事务
Query OK, 0 rows affected (0.00 sec)
mysql> update account set blance=321.0 where id=1; --更新张三数据
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
--切换终端到终端B,查看数据。
mysql> commit; --commit提交!
Query OK, 0 rows affected (0.01 sec)
--切换终端到终端B,再次查看数据。
--终端B
mysql> begin; --手动开启事务,和终端A一前一后
Query OK, 0 rows affected (0.00 sec)
mysql> select * from account; --终端A commit之前,查看不到
+----+--------+----------+
| id | name | blance |
+----+--------+----------+
| 1 | 张三 | 123.00 | --老的值
| 2 | 李四 | 10000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)
--终端A commit之后,看到了!
--but,此时还在当前事务中,并未commit,那么就造成了,同一个事务内,同样的读取,在不同的时间段(依旧还在事务操作中!),读取到了不同的值,这种现象叫做不可重复读(non reapeatable read)!!(这个是问题吗??)
mysql> select *from account;
+----+--------+----------+
| id | name | blance |
+----+--------+----------+
| 1 | 张三 | 321.00 | --新的值
| 2 | 李四 | 10000.00 |
+----+--------+----------+
2 rows in set (0.00 sec)
不可重复读会出现什么问题呢?
两个事务都同时启动,一个事务提交了(关闭了),另一个运行的事务(查询)看到第一个事务的提交结果是不合理的,两个事务都应该是原子的
不可重复读会造成问题吗?会造成什么后果?
比如说小王要把tom的工资重3200改到4500,而小张负责工资表发放奖品的任务,小张要进行查询的任务,当事务运行到3000到4000奖品的查询时,查到了tom,之后小王提交了事务,在4000到5000奖品的查询时,又查到了tom,这在公司就出现了问题
所以我们不应该让我们的用户在实际操作的时候,两个正在执行的事务一方能够读到另一方的提交。所以虽然读提交比读未提交隔离级别高,但是我们也不推荐!因为会导致上述的不可重复读的问题!!