Java充电社
专辑
博文
联系我
本人继续续收门徒,亲手指导
MySQL教程
-> 事务详解
1、MySQL的一些基础知识
2、MySQL中数据类型介绍
3、MySQL管理员常用的一些命令
4、DDL常见操作汇总
5、DML常见操作
6、select查下基础篇
7、select条件查询
8、排序和分页(order by 、limit)
9、分组查询(group by、having)
10、常用函数汇总
11、深入了解连接查询及原理
12、子查询(本篇非常重要,高手必备)
13、细说NULL导致的神坑,让人防不胜防
14、事务详解
15、视图
16、变量
17、存储过程 & 自定义函数详解
18、流程控制语句介绍
19、游标详解
20、异常捕获及处理详解
21、什么是索引?
22、MySQL索引原理详解
23、MySQL索引管理
24、如何正确的使用索引?
25、sql中的where条件在数据库中提取与应用浅析
26、聊聊如何使用MySQL实现分布式锁
27、MySQL如何确保数据不丢失的?有几点我们可以借鉴
28、MySQL系列测试库脚本
29、win10安装mysql5.7.20解压版
上一篇:细说NULL导致的神坑,让人防不胜防
下一篇:视图
<div style="display:none"></div> 打算提升sql技能的,可以加我微信itsoku,带你成为sql高手。 这是Mysql系列第14篇。 环境:mysql5.7.25,cmd命令中进行演示。 **开发过程中,会经常用到数据库事务,所以本章非常重要。** ## 本篇内容 1. 什么是事务,它有什么用? 2. 事务的几个特性 3. 事务常见操作指令详解 4. 事务的隔离级别详解 5. 脏读、不可重复读、可重复读、幻读详解 6. 演示各种隔离级别产生的现象 7. 关于隔离级别的选择 ## 什么是事务? **数据库中的事务是指对数据库执行一批操作,这些操作最终要么全部执行成功,要么全部失败,不会存在部分成功的情况。** **举个例子** 比如A用户给B用户转账100操作,过程如下: ```java 1.从A账户扣100 2.给B账户加100 ``` 如果在事务的支持下,上面最终只有2种结果: 1. 操作成功:A账户减少100;B账户增加100 2. 操作失败:A、B两个账户都没有发生变化 如果没有事务的支持,可能出现错:A账户减少了100,此时系统挂了,导致B账户没有加上100,而A账户凭空少了100。 ## 事务的几个特性(ACID) ## 原子性(Atomicity) 事务的整个过程如原子操作一样,最终要么全部成功,或者全部失败,这个原子性是从最终结果来看的,从最终结果来看这个过程是不可分割的。 ## 一致性(Consistency) 一个事务必须使数据库从一个一致性状态变换到另一个一致性状态。 首先回顾一下一致性的定义。所谓一致性,指的是数据处于一种有意义的状态,这种状态是**语义上的**而不是**语法上的**。最常见的例子是转帐。例如从帐户A转一笔钱到帐户B上,如果帐户A上的钱减少了,而帐户B上的钱却没有增加,那么我们认为此时数据处于不一致的状态。 **从这段话的理解来看,所谓一致性,即,从实际的业务逻辑上来说,最终结果是对的、是跟程序员的所期望的结果完全符合的** ## 隔离性(Isolation) 一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。 ## 持久性(Durability) 一个事务一旦提交,他对数据库中数据的改变就应该是永久性的。当事务提交之后,数据会持久化到硬盘,修改是永久性的。 ## Mysql中事务操作 mysql中事务默认是隐式事务,执行insert、update、delete操作的时候,数据库自动开启事务、提交或回滚事务。 是否开启隐式事务是由变量`autocommit`控制的。 所以事务分为**隐式事务**和**显式事务**。 ## 隐式事务 > 事务自动开启、提交或回滚,比如insert、update、delete语句,事务的开启、提交或回滚由mysql内部自动控制的。 查看变量`autocommit`是否开启了自动提交 ```java mysql> show variables like 'autocommit'; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | autocommit | ON | +---------------+-------+ 1 row in set, 1 warning (0.00 sec) ``` > `autocommit`为ON表示开启了自动提交。 ## 显式事务 > 事务需要手动开启、提交或回滚,由开发者自己控制。 2种方式手动控制事务: **方式1:** 语法: ```java //设置不自动提交事务 set autocommit=0; //执行事务操作 commit|rollback; ``` 示例1:提交事务操作,如下: ```java mysql> create table test1 (a int); Query OK, 0 rows affected (0.01 sec) mysql> select * from test1; Empty set (0.00 sec) mysql> set autocommit=0; Query OK, 0 rows affected (0.00 sec) mysql> insert into test1 values(1); Query OK, 1 row affected (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.00 sec) mysql> select * from test1; +------+ | a | +------+ | 1 | +------+ 1 row in set (0.00 sec) ``` 示例2:回滚事务操作,如下: ```java mysql> set autocommit=0; Query OK, 0 rows affected (0.00 sec) mysql> insert into test1 values(2); Query OK, 1 row affected (0.00 sec) mysql> rollback; Query OK, 0 rows affected (0.00 sec) mysql> select * from test1; +------+ | a | +------+ | 1 | +------+ 1 row in set (0.00 sec) ``` > 可以看到上面数据回滚了。 我们把`autocommit`还原回去: ```java mysql> set autocommit=1; Query OK, 0 rows affected (0.00 sec) ``` **方式2:** 语法: ```java start transaction;//开启事务 //执行事务操作 commit|rollback; ``` 示例1:提交事务操作,如下: ```java mysql> select * from test1; +------+ | a | +------+ | 1 | +------+ 1 row in set (0.00 sec) mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> insert into test1 values (2); Query OK, 1 row affected (0.00 sec) mysql> insert into test1 values (3); Query OK, 1 row affected (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.00 sec) mysql> select * from test1; +------+ | a | +------+ | 1 | | 2 | | 3 | +------+ 3 rows in set (0.00 sec) ``` > 上面成功插入了2条数据。 示例2:回滚事务操作,如下: ```java mysql> select * from test1; +------+ | a | +------+ | 1 | | 2 | | 3 | +------+ 3 rows in set (0.00 sec) mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> delete from test1; Query OK, 3 rows affected (0.00 sec) mysql> rollback; Query OK, 0 rows affected (0.00 sec) mysql> select * from test1; +------+ | a | +------+ | 1 | | 2 | | 3 | +------+ 3 rows in set (0.00 sec) ``` > 上面事务中我们删除了`test1`的数据,显示删除了3行,最后回滚了事务。 ## savepoint关键字 在事务中我们执行了一大批操作,可能我们只想回滚部分数据,怎么做呢? 我们可以将一大批操作分为几个部分,然后指定回滚某个部分。可以使用`savepoin`来实现,效果如下: 先清除`test1`表数据: ```java mysql> delete from test1; Query OK, 3 rows affected (0.00 sec) mysql> select * from test1; Empty set (0.00 sec) ``` 演示`savepoint`效果,认真看: ```java mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> insert into test1 values (1); Query OK, 1 row affected (0.00 sec) mysql> savepoint part1;//设置一个保存点 Query OK, 0 rows affected (0.00 sec) mysql> insert into test1 values (2); Query OK, 1 row affected (0.00 sec) mysql> rollback to part1;//将savepint = part1的语句到当前语句之间所有的操作回滚 Query OK, 0 rows affected (0.00 sec) mysql> commit;//提交事务 Query OK, 0 rows affected (0.00 sec) mysql> select * from test1; +------+ | a | +------+ | 1 | +------+ 1 row in set (0.00 sec) ``` > 从上面可以看出,执行了2次插入操作,最后只插入了1条数据。 > > `savepoint`需要结合`rollback to sp1`一起使用,可以将保存点`sp1`到`rollback to`之间的操作回滚掉。 ## 只读事务 表示在事务中执行的是一些只读操作,如查询,但是不会做insert、update、delete操作,数据库内部对只读事务可能会有一些性能上的优化。 用法如下: ```java start transaction read only; ``` 示例: ```java mysql> commit; Query OK, 0 rows affected (0.00 sec) mysql> start transaction read only; Query OK, 0 rows affected (0.00 sec) mysql> select * from test1; +------+ | a | +------+ | 1 | | 1 | +------+ 2 rows in set (0.00 sec) mysql> delete from test1; ERROR 1792 (25006): Cannot execute statement in a READ ONLY transaction. mysql> commit; Query OK, 0 rows affected (0.00 sec) mysql> select * from test1; +------+ | a | +------+ | 1 | | 1 | +------+ 2 rows in set (0.00 sec) ``` > 只读事务中执行delete会报错。 ## 事务中的一些问题 这些问题主要是基于数据在多个事务中的可见性来说的。 ## 脏读 一个事务在执行的过程中读取到了其他事务还没有提交的数据。 这个还是比较好理解的。 ## 读已提交 从字面上我们就可以理解,即一个事务操作过程中可以读取到其他事务已经提交的数据。 事务中的每次读取操作,读取到的都是数据库中其他事务已提交的最新的数据(相当于当前读) ## 可重复读 一个事务操作中对于一个读取操作不管多少次,读取到的结果都是一样的。 ## 幻读 脏读、不可重复读、可重复读、幻读,其中最难理解的是幻读 以mysql为例: **幻读在可重复读的模式下才会出现,其他隔离级别中不会出现** 幻读现象例子: 可重复读模式下,比如有个用户表,手机号码为主键,有两个事物进行如下操作 事务A操作如下: 1、打开事务 2、查询号码为X的记录,不存在 3、插入号码为X的数据,插入报错(为什么会报错,先向下看) 4、查询号码为X的记录,发现还是不存在(由于是可重复读,所以读取记录X还是不存在的) 事物B操作:在事务A第2步操作时插入了一条X的记录,所以会导致A中第3步插入报错(违反了唯一约束) 上面操作对A来说就像发生了幻觉一样,明明查询X(A中第二步、第四步)不存在,但却无法插入成功 **幻读可以这么理解:事务中后面的操作(插入号码X)需要上面的读取操作(查询号码X的记录)提供支持,但读取操作却不能支持下面的操作时产生的错误,就像发生了幻觉一样。** 如果还是理解不了的,继续向下看,后面后详细的演示。 ## 事务的隔离级别 当多个事务同时进行的时候,如何确保当前事务中数据的正确性,比如A、B两个事物同时进行的时候,A是否可以看到B已提交的数据或者B未提交的数据,这个需要依靠事务的隔离级别来保证,不同的隔离级别中所产生的效果是不一样的。 事务隔离级别主要是解决了上面多个事务之间数据可见性及数据正确性的问题。 **隔离级别分为4种:** 1. **读未提交:READ-UNCOMMITTED** 2. **读已提交:READ-COMMITTED** 3. **可重复读:REPEATABLE-READ** 4. **串行:SERIALIZABLE** 上面4中隔离级别越来越强,会导致数据库的并发性也越来越低。 ## 查看隔离级别 ```java mysql> show variables like 'transaction_isolation'; +-----------------------+----------------+ | Variable_name | Value | +-----------------------+----------------+ | transaction_isolation | READ-COMMITTED | +-----------------------+----------------+ 1 row in set, 1 warning (0.00 sec) ``` ## 隔离级别的设置 分2步骤,修改文件、重启mysql,如下: 修改mysql中的my.init文件,我们将隔离级别设置为:READ-UNCOMMITTED,如下: ```java transaction-isolation=READ-UNCOMMITTED ``` 以管理员身份打开cmd窗口,重启mysql,如下: ``` C:\Windows\system32>net stop mysql mysql 服务正在停止.. mysql 服务已成功停止。 C:\Windows\system32>net start mysql mysql 服务正在启动 . mysql 服务已经启动成功。 ``` ## 各种隔离级别中会出现的问题 | 隔离级别 | 脏读 | 不可重复读 | 幻读 | | ---------------- | ---- | ---------- | ---- | | READ-UNCOMMITTED | 有 | 有 | 无 | | READ-COMMITTED | 无 | 有 | 无 | | REPEATABLE-READ | 无 | 无 | 有 | | SERIALIZABLE | 无 | 无 | 无 | > 表格中和网上有些不一样,主要是幻读这块,幻读只会在可重复读级别中才会出现,其他级别下不存在。 下面我们来演示一下,各种隔离级别中可见性的问题,开启两个窗口,叫做A、B窗口,两个窗口中登录mysql。 ## READ-UNCOMMITTED:读未提交 将隔离级别置为`READ-UNCOMMITTED`: ```java transaction-isolation=READ-UNCOMMITTED ``` 重启mysql: ```java C:\Windows\system32>net stop mysql mysql 服务正在停止.. mysql 服务已成功停止。 C:\Windows\system32>net start mysql mysql 服务正在启动 . mysql 服务已经启动成功。 ``` 查看隔离级别: ```java mysql> show variables like 'transaction_isolation'; +-----------------------+----------------+ | Variable_name | Value | +-----------------------+----------------+ | transaction_isolation | READ-UNCOMMITTED | +-----------------------+----------------+ 1 row in set, 1 warning (0.00 sec) ``` 先清空test1表数据: ```java delete from test1; select * from test1; ``` 按时间顺序在2个窗口中执行下面操作: | 时间 | 窗口A | 窗口B | | ---- | -------------------- | ----------------------------- | | T1 | start transaction; | | | T2 | select * from test1; | | | T3 | | start transaction; | | T4 | | insert into test1 values (1); | | T5 | | select * from test1; | | T6 | select * from test1; | | | T7 | | commit; | | T8 | commit; | | A窗口如下: ```java mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> select * from test1; Empty set (0.00 sec) mysql> select * from test1; +------+ | a | +------+ | 1 | +------+ 1 row in set (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.00 sec) ``` B窗口如下: ```java mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> insert into test1 values (1); Query OK, 1 row affected (0.00 sec) mysql> select * from test1; +------+ | a | +------+ | 1 | +------+ 1 row in set (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.00 sec) ``` 看一下: T2-A:无数据,T6-A:有数据,T6时刻B还未提交,此时A已经看到了B插入的数据,**说明出现了脏读**。 T2-A:无数据,T6-A:有数据,查询到的结果不一样,**说明不可重复读**。 **结论:读未提交情况下,可以读取到其他事务还未提交的数据,多次读取结果不一样,出现了脏读、不可重复读** ## READ-COMMITTED:读已提交 将隔离级别置为`READ-COMMITTED` ```java transaction-isolation=READ-COMMITTED ``` 重启mysql: ``` C:\Windows\system32>net stop mysql mysql 服务正在停止.. mysql 服务已成功停止。 C:\Windows\system32>net start mysql mysql 服务正在启动 . mysql 服务已经启动成功。 ``` 查看隔离级别: ```java mysql> show variables like 'transaction_isolation'; +-----------------------+----------------+ | Variable_name | Value | +-----------------------+----------------+ | transaction_isolation | READ-COMMITTED | +-----------------------+----------------+ 1 row in set, 1 warning (0.00 sec) ``` 先清空test1表数据: ```java delete from test1; select * from test1; ``` 按时间顺序在2个窗口中执行下面操作: | 时间 | 窗口A | 窗口B | | ---- | -------------------- | ----------------------------- | | T1 | start transaction; | | | T2 | select * from test1; | | | T3 | | start transaction; | | T4 | | insert into test1 values (1); | | T5 | | select * from test1; | | T6 | select * from test1; | | | T7 | | commit; | | T8 | select * from test1; | | | T9 | commit; | | A窗口如下: ```java mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> select * from test1; Empty set (0.00 sec) mysql> select * from test1; Empty set (0.00 sec) mysql> select * from test1; +------+ | a | +------+ | 1 | +------+ 1 row in set (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.00 sec) ``` B窗口如下: ```java mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> insert into test1 values (1); Query OK, 1 row affected (0.00 sec) mysql> select * from test1; +------+ | a | +------+ | 1 | +------+ 1 row in set (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.00 sec) ``` 看一下: T5-B:有数据,T6-A窗口:无数据,A看不到B的数据,**说明没有脏读**。 T6-A窗口:无数据,T8-A:看到了B插入的数据,此时B已经提交了,A看到了B已提交的数据,**说明可以读取到已提交的数据**。 T2-A、T6-A:无数据,T8-A:有数据,多次读取结果不一样,**说明不可重复读**。 **结论:读已提交情况下,无法读取到其他事务还未提交的数据,可以读取到其他事务已经提交的数据,多次读取结果不一样,未出现脏读,出现了读已提交、不可重复读。** ## REPEATABLE-READ:可重复读 将隔离级别置为`REPEATABLE-READ` ``` transaction-isolation=REPEATABLE-READ ``` 重启mysql: ```java C:\Windows\system32>net stop mysql mysql 服务正在停止.. mysql 服务已成功停止。 C:\Windows\system32>net start mysql mysql 服务正在启动 . mysql 服务已经启动成功。 ``` 查看隔离级别: ```java mysql> show variables like 'transaction_isolation'; +-----------------------+----------------+ | Variable_name | Value | +-----------------------+----------------+ | transaction_isolation | REPEATABLE-READ | +-----------------------+----------------+ 1 row in set, 1 warning (0.00 sec) ``` 先清空test1表数据: ```java delete from test1; select * from test1; ``` 按时间顺序在2个窗口中执行下面操作: | 时间 | 窗口A | 窗口B | | ---- | -------------------- | ----------------------------- | | T1 | start transaction; | | | T2 | select * from test1; | | | T3 | | start transaction; | | T4 | | insert into test1 values (1); | | T5 | | select * from test1; | | T6 | select * from test1; | | | T7 | | commit; | | T8 | select * from test1; | | | T9 | commit; | | | T10 | select * from test1; | | A窗口如下: ```java mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> select * from test1; Empty set (0.00 sec) mysql> select * from test1; Empty set (0.00 sec) mysql> select * from test1; Empty set (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.00 sec) mysql> select * from test1; +------+ | a | +------+ | 1 | | 1 | +------+ 2 rows in set (0.00 sec) ``` B窗口如下: ```java mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> insert into test1 values (1); Query OK, 1 row affected (0.00 sec) mysql> select * from test1; +------+ | a | +------+ | 1 | | 1 | +------+ 2 rows in set (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.00 sec) ``` 看一下: T2-A、T6-A窗口:无数据,T5-B:有数据,A看不到B的数据,**说明没有脏读**。 T8-A:无数据,此时B已经提交了,A看不到B已提交的数据,A中3次读的结果一样都是没有数据的,**说明可重复读**。 **结论:可重复读情况下,未出现脏读,未读取到其他事务已提交的数据,多次读取结果一致,即可重复读。** ## 幻读演示 幻读只会在`REPEATABLE-READ`(可重复读)级别下出现,需要先把隔离级别改为可重复读。 将隔离级别置为`REPEATABLE-READ` ``` transaction-isolation=REPEATABLE-READ ``` 重启mysql: ```java C:\Windows\system32>net stop mysql mysql 服务正在停止.. mysql 服务已成功停止。 C:\Windows\system32>net start mysql mysql 服务正在启动 . mysql 服务已经启动成功。 ``` 查看隔离级别: ```java mysql> show variables like 'transaction_isolation'; +-----------------------+----------------+ | Variable_name | Value | +-----------------------+----------------+ | transaction_isolation | REPEATABLE-READ | +-----------------------+----------------+ 1 row in set, 1 warning (0.00 sec) ``` 准备数据: ```java mysql> create table t_user(id int primary key,name varchar(16) unique key); Query OK, 0 rows affected (0.01 sec) mysql> insert into t_user values (1,'路人甲Java'),(2,'路人甲Java'); ERROR 1062 (23000): Duplicate entry '路人甲Java' for key 'name' mysql> select * from t_user; Empty set (0.00 sec) ``` > 上面我们创建t_user表,name添加了唯一约束,表示name不能重复,否则报错。 按时间顺序在2个窗口中执行下面操作: | 时间 | 窗口A | 窗口B | | ---- | ------------------------------------------------------------ | ------------------------------------------------------------ | | T1 | start transaction; | | | T2 | | start transaction; | | T3 | | -- 插入`路人甲Java`<br />insert into t_user values (1,'路人甲Java'); | | T4 | | select * from t_user; | | T5 | -- 查看`路人甲Java`是否存在<br />select * from t_user where name='路人甲Java'; | | | T6 | | commit; | | T7 | -- 插入`路人甲Java`<br />insert into t_user values (2,'路人甲Java'); | | | T8 | -- 查看`路人甲Java`是否存在<br />select * from t_user where name='路人甲Java'; | | | T9 | commit; | | A窗口如下: ```java mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> select * from t_user where name='路人甲Java'; Empty set (0.00 sec) mysql> insert into t_user values (2,'路人甲Java'); ERROR 1062 (23000): Duplicate entry '路人甲Java' for key 'name' mysql> select * from t_user where name='路人甲Java'; Empty set (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.00 sec) ``` B窗口如下: ```java mysql> start transaction; Query OK, 0 rows affected (0.00 sec) mysql> insert into t_user values (1,'路人甲Java'); Query OK, 1 row affected (0.00 sec) mysql> select * from t_user; +----+---------------+ | id | name | +----+---------------+ | 1 | 路人甲Java | +----+---------------+ 1 row in set (0.00 sec) mysql> commit; Query OK, 0 rows affected (0.00 sec) ``` 看一下: A想插入数据`路人甲Java`,插入之前先查询了一下(T5时刻)该用户是否存在,发现不存在,然后在T7时刻执行插入,报错了,报数据已经存在了,因为T6时刻`B`已经插入了`路人甲Java`。 然后A有点郁闷,刚才查的时候不存在的,然后A不相信自己的眼睛,又去查一次(T8时刻),发现`路人甲Java`还是不存在的。 此时A心里想:数据明明不存在啊,为什么无法插入呢?这不是懵逼了么,A觉得如同发生了幻觉一样。 ## SERIALIZABLE:串行 > **SERIALIZABLE会让并发的事务串行执行(多个事务之间读写、写读、写写会产生互斥,效果就是串行执行,多个事务之间的读读不会产生互斥)。** > > **读写互斥**:事务A中先读取操作,事务B发起写入操作,事务A中的读取会导致事务B中的写入处于等待状态,直到A事务完成为止。 > > 表示我开启一个事务,为了保证事务中不会出现上面说的问题(脏读、不可重复读、读已提交、幻读),那么我读取的时候,其他事务有修改数据的操作需要排队等待,等待我读取完成之后,他们才可以继续。 > > 写读、写写也是互斥的,读写互斥类似。 > > 这个类似于java中的`java.util.concurrent.lock.ReentrantReadWriteLock`类产生的效果。 下面演示读写互斥的效果。 将隔离级别置为`SERIALIZABLE` ``` transaction-isolation=SERIALIZABLE ``` 重启mysql: ```java C:\Windows\system32>net stop mysql mysql 服务正在停止.. mysql 服务已成功停止。 C:\Windows\system32>net start mysql mysql 服务正在启动 . mysql 服务已经启动成功。 ``` 查看隔离级别: ```java mysql> show variables like 'transaction_isolation'; +-----------------------+--------------+ | Variable_name | Value | +-----------------------+--------------+ | transaction_isolation | SERIALIZABLE | +-----------------------+--------------+ 1 row in set, 1 warning (0.00 sec) ``` 先清空test1表数据: ```java delete from test1; select * from test1; ``` 按时间顺序在2个窗口中执行下面操作: | 时间 | 窗口A | 窗口B | | ---- | -------------------- | ----------------------------- | | T1 | start transaction; | | | T2 | select * from test1; | | | T3 | | start transaction; | | T4 | | insert into test1 values (1); | | T5 | commit; | | | T6 | | commit; | 按时间顺序运行上面的命令,会发现T4-B这样会被阻塞,直到T5-A执行完毕。 上面这个演示的是读写互斥产生的效果,大家可以自己去写一下写读、写写互斥的效果。 **可以看出来,事务只能串行执行了。串行情况下不存在脏读、不可重复读、幻读的问题了。** ## 关于隔离级别的选择 1. 需要对各种隔离级别产生的现象非常了解,然后选择的时候才能游刃有余 2. 隔离级别越高,并发性也低,比如最高级别`SERIALIZABLE`会让事物串行执行,并发操作变成串行了,会导致系统性能直接降低。 3. 具体选择哪种需要结合具体的业务来选择。 4. 读已提交(READ-COMMITTED)通常用的比较多。 ## 总结 1. 理解事务的4个特性:原子性、一致性、隔离性、持久性 2. 掌握事务操作常见命令的介绍 3. `set autocommit`可以设置是否开启自动提交事务 4. start transaction:开启事务 5. start transaction read only:开启只读事物 6. commit:提交事务 7. rollback:回滚事务 8. savepoint:设置保存点 9. rollback to 保存点:可以回滚到某个保存点 10. 掌握4种隔离级别及了解其特点 11. 了解脏读、不可重复读、幻读 <a style="display:none" target="_blank" href="https://mp.weixin.qq.com/s/_S1DD2JADnXvpexxaBwLLg" style="color:red; font-size:20px; font-weight:bold">继续收门徒,亲手带,月薪 4W 以下的可以来找我</a> ## 最新资料 1. <a href="https://mp.weixin.qq.com/s?__biz=MzkzOTI3Nzc0Mg==&mid=2247484964&idx=2&sn=c81bce2f26015ee0f9632ddc6c67df03&scene=21#wechat_redirect" target="_blank">尚硅谷 Java 学科全套教程(总 207.77GB)</a> 2. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484192&idx=1&sn=505f2faaa4cc911f553850667749bcbb&scene=21#wechat_redirect" target="_blank">2021 最新版 Java 微服务学习线路图 + 视频</a> 3. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484573&idx=1&sn=7f3d83892186c16c57bc0b99f03f1ffd&scene=21#wechat_redirect" target="_blank">阿里技术大佬整理的《Spring 学习笔记.pdf》</a> 4. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247484544&idx=2&sn=c1dfe907cfaa5b9ae8e66fc247ccbe84&scene=21#wechat_redirect" target="_blank">阿里大佬的《MySQL 学习笔记高清.pdf》</a> 5. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247485167&idx=1&sn=48d75c8e93e748235a3547f34921dfb7&scene=21#wechat_redirect" target="_blank">2021 版 java 高并发常见面试题汇总.pdf</a> 6. <a href="https://mp.weixin.qq.com/s?__biz=MzkwOTAyMTY2NA==&mid=2247485664&idx=1&sn=435f9f515a8f881642820d7790ad20ce&scene=21#wechat_redirect" target="_blank">Idea 快捷键大全.pdf</a> ![](https://itsoku.oss-cn-hangzhou.aliyuncs.com/itsoku/blog/article/1/2883e86e-3eff-404a-8943-0066e5e2b454.png)
#custom-toc-container