继续讲解 Mysql 数据库中最重要的一个概念:事务
文章目录
- 事务
- 1.1 什么是事务
- 1.2 执行原理
- 1.3 如何操作事务
- 1.4 事务的特点(ACID原则)
- 1.5 事务并发
- 1.6 事务隔离级别
- 1.6.1 事务并发问题操作演示
- 1.6.2 脏读演示
- 1.6.3 不可重复读演示
- 1.6.4 幻读演示
事务
1.1 什么是事务
1、事务是构成多用户使用数据库的基础。
2、举个例子来理解事务:
例子:向公司添加一名新的员工,这个过程大致分为这三步:在数据库中创建一条新的记录 ----> 为新员工添加部门 ----> 建立他的工资和奖金记录。
如果这 3 步中任何一步失败,则系统就必须撤销在此之前所有的变化,删除所有不完整记录的痕迹。这 3 个任务就构成了一个事务,其中任何一个任务的失败都会导致整个事务被撤销。
3、事务:是原子操作,是一个最小执行单元,由一个或多个 SQL 语句组成。这个单元中的每个 SQL 语句是相互依赖的,而且单元作为一个整体是不可分割的。如果单元中的一个语句不能完成,整个单元就会回滚(撤销),所有影响到的数据将返回到事务开始以前的状态。
4、简单总结事务的定义:
- 一个数据库操作序列
- 一个不可分割的工作单位
- 恢复和并发控制的基本单位
事务和程序的比较:
- 在关系数据库中,一个事务可以是一条或多条 SQL 语句,也可以包含一个或多个程序。
- 一个程序通常包含多个事务。
1.2 执行原理
1、数据库会为每一个客户端都维护 一个独立空间的缓存区(回滚段)
2、一个事务中所有的 增删改 语句的执行结果会先缓存在回滚段中,而不是持久化到数据库中(查询不存在事务,不影响)
- 成功:当事务中所有 SQL 语句均正常结束(commit),才会将回滚段中的数据同步到数据库。
- 失败:整个事务将回滚(rollback)
1.3 如何操作事务
1、默认情况下事务是自动提交的(比如:你写一个 SQL 语句,在没有开始事务的情况下,就默认提交了,假如后面 SQL 语句有问题,前面的需要回滚,但是你已经提交了,就回滚不了了,所以这样不行)
事务开始:设置事务为手动提交:set autocommit=0;
0 表示手动提交,1 表示自动提交
事务结束:分为两种情况:
- 提交事务,事务就结束了,命令为:commit;
- 回滚事务,事务也结束了,命令为:rollback;
2、未来在 java 代码中的操作
try{
执行事务
//上面没报错就提交事务
提交事务
} catch() {
回滚事务
}
1.4 事务的特点(ACID原则)
原子性(Atomicity):表示一个事务内的所有操作是一个整体,要么全部成功,要么全部失败。
一致性(Consistency):表示一个事务内有一个操作失败时,所有更改过的数据都必须回滚到修改前的状态。
隔离性(Isolation):指一个事务的操作不能影响到另外一个事务的执行。
持久性(Durability):指即使系统崩溃,一个提交的事务仍然存在(也就是说:一个事务操作完成对数据库的影响是永久的)
1.5 事务并发
什么是事务并发:多个人(人:事务)同时操作同一个表中的数据。
带来的问题:
-
脏读: 事务A读到了事务B未提交保存的数据。
举例:比如账户里面有800块钱,此时事务A正常读。但是此时事务B给账户加了200块钱,事务A再读时,读到了1000块钱,但是现在事务B要回滚,这200块没有成功,但是事务A读到了,这不允许,因为事务A读取到了一个不存在的脏数据。
-
不可重复读: 事务A在同一个事务中,因为事务B修改了数据,并提交了,同样的条件下,造成事务A两次读取到的数据不一致。
例如:事务在 T1 时间读取到了某一行数据, 在 T2 时间重新读取这一行时候,这一行的数据已经发生修改,所以再次读取时得到了一个和 T1 查询时不同的结果。(因为中间有其他事务提交了 修改)
-
幻读: 事务A在同一个事务中,因为事务B新增了数据,并提交了,如果事务A修改数据, 会出现多修改了一条数据,出现幻觉。
第一次和第二次读出来的记录数不一样。(因为中间有其他事务提交了 插入/删除 )
区别:
- 不可重复读:重点是修改,读的数据不一样;
- 幻读:重点是新增或者是修改,读的记录数不一样。
1.6 事务隔离级别
使用事务隔离级别来解决:读未提交: read uncommitted、读已提交: read committed、可重复读(mysql 默认的隔离级别): repeatable read、串行化: serializable
×:不能解决 √:能解决 | |||
---|---|---|---|
脏读 | 不可重复读 | 幻读 | |
读未提交 | × | × | × |
读已提交 | √ | × | × |
可重复读 | √ | √ | × |
串行化 | √ | √ | √ |
命令行查看事务隔离级别的时候报错:
原因:老版本 MySQL 比如 5 中用的是 tx_isolation
,而应该是在 5.7.20 版本之后,用的是 transaction_isolation
。 所以:在 MySQL 8 及之后的版本中,只需将语句中的 tx_isolation
替换为 transaction_isolation
即可:
每启动一个 MySQL 程序,就会获得一个单独的数据库连接。
-- 查看当前的隔离级别
select @@transaction_isolation;
-- 设置当前mysql连接的隔离级别(session表示连接的当前窗口)
set session transaction isolation level 隔离级别的英文名称;
-- 设置数据库系统全局的隔离级别
set global transaction isolation level 隔离级别的英文名称;
1.6.1 事务并发问题操作演示
下面进行实际操作演示事务并发:由于 navicat 中无法演示不同事务,这里直接开两个 cmd 窗口来演示不同的事务。
事务并发操作的表为:
1.6.2 脏读演示
每个问题的演示都要设置为手动提交:每个事务都需要。
但是设置隔离级别,这里只设置了出现问题的事务(比如:事务A,最好每个事务也设置下)
这也出现了另外一个问题,同一个事务内,事务A三次读取的数据不一样,导致不可重复读
怎么解决脏读? 设置隔离级别:读已提交:read committed(这里只给事务A设置了隔离,也可以再给B设置同样的隔离级别)
1.6.3 不可重复读演示
解决: 设置隔离级别:可重复读 repeatable read
1.6.4 幻读演示
注意:幻读在实际开发中是可以接收的
解决: 设置隔离级别:serializable。一般不用解决