MySQL-基础篇 ( 事务 )
目录事务简介
- 事务是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败
- 典型案例:银行转账,一方转账减钱时,若是中途出了问题,此方的钱数不会减少,而是回滚到转帐前的状态
- 默认 MySQL 的事务是自动提交的,也就是说,当执行一条 DML 语句,MySQL 会立即隐式的提交事务
- 即中途出了问题,可能会导致一方钱数减少了,但另一方钱数没有增加
事务操作
方式一
-
查看事务提交方式
- 直接输入语句:
SELECT @@autocommit;
- 1 表示当前事务是自动提交
- 直接输入语句:
-
设置事务提交方式
- 设置为手动提交:
SET @@autocommit = 0;
- 设置为手动提交:
-
提交事务
- 设置为手动提交后,SQL 语句并不会对数据库进行更改,想要提交到数据库需要在操作的最后执行:
COMMIT;
- 即,如果设置为了手动提交,则往后的所有对数据库的操作都要在执行后再
COMMIT;
才行
- 设置为手动提交后,SQL 语句并不会对数据库进行更改,想要提交到数据库需要在操作的最后执行:
-
回滚事务
- 设置为手动提交后,当事务中途出现错误,就不要再执行
COMMIT
提交了,而是执行:ROLLBACK;
进行回滚事务 - 因为在报错前的语句还是都执行完成了,所以要回滚让执行过的恢复到初始状态
- 设置为手动提交后,当事务中途出现错误,就不要再执行
-
代码模拟:
-- 查看事务提交方式 SELECT @@AUTOCOMMIT; -- 设置事务提交方式,1为自动提交,0为手动提交,该设置只对当前会话有效 SET @@AUTOCOMMIT = 0; -- 设置完手动提交后: select * from account where name = '张三'; update account set money = money - 1000 where name = '张三'; update account set money = money + 1000 where name = '李四'; commit; -- 按照需要回滚事务:ROLLBACK;
方式二
-
开启事务
- 在语句执行之前先执行:
START TRANSACTION;
或BEGIN;
- 在语句执行之前先执行:
-
提交事务
- 同方式一,成功了就继续执行:
COMMIT;
- 同方式一,成功了就继续执行:
-
回滚事务
- 同方式一,失败出错了,就不执行
COMMIT;
,而是执行:ROLLBACK;
- 同方式一,失败出错了,就不执行
-
代码模拟:
start transaction; select * from account where name = '张三'; update account set money = money - 1000 where name = '张三'; update account set money = money + 1000 where name = '李四'; commit; -- 按照需要回滚事务:ROLLBACK;
事务四大特性 ( ACID )
- 原子性 ( Atomicity ):事务是一组操作,是不可分割的最小操作单元,要么全部成功,要么全部失败
- 一致性 ( Consistency ):事务完成时,必须使所有数据都保持一致状态
- 隔离性 ( Isolation ):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行
- 持久性 ( Durability ):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的
- 即 ( 以上述转账为例 ):
- 原子性 —— 转账过程其中一步失败了,整个操作就失败了
- 一致性 —— 不管事务成与不成,最后总余额数一致
- 隔离性 —— 若干个并发事务,事务 A 和事务 B 两者执行互不干扰,都是独立环境下进行的
- 持久性 —— 不管转账成与不成,最后的对数据库的改变是永久的 ( 数据库数据最终是保存、持久化到磁盘中的 —— 各信息都是在 MySQL 文件夹下以某种格式被保存着 )
并发事务问题
- 多个并发事务在执行的过程中所出现的并发事务问题:
问题 | 描述 |
---|---|
脏读 | 一个事务读到另一个事务还没提交 ( 操作没完成 ) 的数据 |
不可重复读 | 一个事务先后读取同一条记录,但两次读取的数据不同 ( 其他操作在两次读取的中途对记录进行了更改 ) |
幻读 | 一个事务按照条件查询数据时,没有对应的数据行,但是在执行插入数据时,又发现这行数据已经存在 ( 数据被其他事务操作了 ),好像出现了 “ 幻影 ” |
事务隔离级别
- 为了解决在多个并发事务执行的过程中所出现的各种并发事务问题
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
Read uncommitted —— 读未提交 | Y | Y | Y |
Read committed —— 读已提交 | N | Y | Y |
Repeatable Read ( 默认 ) —— 可重复读 | N | N | Y |
Serializable —— 串行化 | N | N | N |
-
MySQL 默认的隔离级别是 Repeatable Read,而 Orcale 默认的就是 Read committed 了
- Y 表示会出现,N 表示该问题不会出现
- 从上到下隔离级别越高,但性能也就越差
- 即 Read uncommitted 是效率最高的,但安全性是最差的
- 从上到下隔离级别越高,但性能也就越差
- Y 表示会出现,N 表示该问题不会出现
-
查看事务隔离级别:
SELECT @@TRANSACTION_ISOLATION;
-
MySQL 下:
-
-
设置事务隔离级别:
SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE};
- SESSION 是会话级别,表示只针对当前客户端窗口有效,GLOBAL 表示对所有客户端会话窗口有效
- 大括号内的就是自行设置的隔离级别,不区分大小写
-
模拟脏读情况:
-
可打开两个 cmd 命令行窗口模拟两个客户端,两个并发事务
-
两个窗口,登录一样的用户,同样执行:
-
mysql> user 数据库;
set ... level read uncommitted;
start transaction;
-
-
A 窗口读两次一个数据,B 窗口在 A 读两次之间进行对此数据的更改,并在 A 读完再
commit;
- 就会发现 B 还没提交,A 两次的读取就不一样了
-
紧接着将 A 级别进行更改为
read committed
后,重复上述操作,就会发现 A 两次的读取一样了,只有 B 提交后 A 第三次读取才会查到更改后的数据
-
-
而上述情况的 A 的第一、二次读取与第三次读取结果不一致,这就是 " 不可重复读 " 的情况了,只有再升一个隔离等级后,就会发现第一、二、三次结构都一致,只有 A
commit;
提交后再第四次查询才会发现有了更改 ( B 提交了的更改 ) -
最后一种幻读情况就不再演示,即 A 查空 id,B 插此空 id 并提交,就会使得 A 无法再插入却还读不到的 " 幻读 " 问题
- 提高等级后解决方法就是,A 开启事务查后,B 无法执行任何语句,只有等 A 插、查操作等都完成了,
commit;
提交了,B 才能继续执行语句 —— 规避了 " 幻读 "
- 提高等级后解决方法就是,A 开启事务查后,B 无法执行任何语句,只有等 A 插、查操作等都完成了,