
事务指把一组 SQL 语句打包成为一个整体,在这组 SQL 的执行过程中,要么全部成功,要么全部失败。这组 SQL 语句可以是一条也可以是多条。
一个完整的事务,绝对不是简单的 sql 集合,还需要满足如下四个属性:
Atomicity):一个事务中的所有操作,要么全部完成,要么全部不完成。事务在执行过程中发生错误,会被回滚到事务开始前的状态,就像这个事务从来没有执行过一样。Consistency):一个事务的执行前后,数据必须保持一致,或者修改的数据必须符合预期。Isolation):多个事务之间不能相互影响,通过不同的隔离级别对安全和性能做出平衡。Durability):事务处理结束后,对数据的修改必须保存到磁盘中,即便系统故障也不会丢失。之所以要使用事务,是因为在日常的业务场景中大量存在需要用事务来保证安全的情况,而有支持事务的数据库能够简化我们的编程模型,让我们专注于业务逻辑本身,而不是关心底层的事务安全问题,简单的说,事务就是为了给应用层服务而产生的。
InnoDB 引擎通过什么技术来保证事务的这四个特性的呢❓❓❓
undo log(回滚日志)redo log(重做日志)MVCC(多版本并发控制)、锁机制
# 开始⼀个新的事务
START TRANSACTION;
# 或
BEGIN;
# 提交当前事务,并对更改持久化保存
COMMIT;
# 回滚当前事务到某个保存点(若无指定保存点则回滚到最开始的状态),取消其更改
ROLLBACK [to 保存点];
# 设置保存点
SAVEPOINT 保存点名称在 MySQL 中,自动提交(Auto Commit)和手动提交(Manual Commit)是两种不同的事务提交方式。查看当前事务的提交方式指令:
show variables like 'autocommit';MySQL 处于自动提交模式。SQL 语句都会被立即提交到数据库,无需手动执行提交操作。当执行一条 SQL 语句后,该语句的结果就会立即生效,且无法回滚。SQL 语句作为一个事务进行提交。SQL 语句可以作为一个原子操作进行提交,要么全部成功提交,要么全部回滚。通过以下语句设置事务为自动或手动提交:
# 设置事务自动提交
SET AUTOCOMMIT=1; # 方式一
SET AUTOCOMMIT=ON; # 方式二
# 设置事务手动提交
SET AUTOCOMMIT=0; # 方式一
SET AUTOCOMMIT=OFF; # 方式二 START TRANSACTION 或 BEGIN 开启事务,就必须通过 COMMIT 提交才会持久化,与是否设置 set autocommit 无关。因为只要启动了事务,mysql 就会自动切换为手动提交模式。commit 或 rollback。commit),则不可以回滚(rollback)rollback)不会结束事务。MySQL 会自动回滚。InnoDB 每一条 SQL 语言都默认封装成事务,进行自动提交(select 有特殊情况,因为 MySQL 有 MVCC,后面会讲)MySQL 服务端是允许多个客户端连接的,这意味着 MySQL 会出现同时处理多个事务的情况。那么在并发处理多个事务的时候,就可能出现 脏读、不可重复读、幻读 的问题。
dirty read脏读是指一个事务读取了另一个事务可能尚未提交的数据。当一个事务读取了另一个事务的未提交数据时,如果另一个事务最终回滚,则读取到的数据实际上是无效的,没有意义。
比如在事务 A 中执行了一条 INSERT 语句,在没有执行 COMMIT 的情况下,会在事务 B 中被读取到,此时如果事务 A 执行回滚操作,那么事务 B 中读取到事务 A 写入的数据将没有意义,这种现象就是 "脏读" 。
non-repeatable read不可重复读是指在一个事务内,多次读取同一数据时,得到的【结果】不一致。这是因为在事务执行期间,其它事务可能修改了被读取的数据。
例如,事务 A 先对某条数据进行了查询,之后事务 B 对这条数据进行了修改,并且提交事务,事务 A 再对这条数据进行查询时,得到了事务 B 修改之后的结果,虽然这不是 "脏读",但这导致了事务 A 在同一个事务中以相同的条件查询得到了不同的值,这种现象就是 "不可重复读"。
phantom read幻读是指在一个事务内,多次执行同一个查询时,得到的【结果集】不一致。这是因为在事务执行期间,其他事务可能插入或删除了符合查询条件的数据。
例如,事务 A 查询了一个区间的记录得到结果集 A,事务 B 向这个区间的间隙中写入了一条记录并提交,事务 A 再查询这个区间的结果集时会查到事务 B 新写入的记录得到结果集 B,两次查询的结果集不一致,这种现象就是 "幻读"。
☠注意:虽然 InnoDB 在可重复读隔离级别中引入了 MVCC 和 next-key lock 两种解决方案,但可重复读级别还是避免不了幻读的问题,只是在很大程度上避免了幻读问题,要想真正解决幻读问题,还是需要使用可序列化级别!
幻读和不可重复读的区别如下所示:
假设有 A 和 B 这两个事务同时在处理,事务 A 先开始从数据库查询账户余额大于 100 万的记录,发现共有 5 条,然后事务 B 也按相同的搜索条件也是查询出了 5 条记录。
接下来,事务 A 插入了一条余额超过 100 万的账号,并提交了事务,此时数据库超过 100 万余额的账号个数就变为 6。
然后事务 B 再次查询账户余额大于 100 万的记录,此时查询到的记录数量有 6 条,发现和前一次读到的记录数量不一样了,就感觉发生了幻觉一样,这种现象就被称为幻读。
在数据库中,为了保证事务执行过程中尽量不受干扰,引入了一个重要特性:隔离性。
在数据库中,允许事务受不同程度的干扰,引入了一种重要特征:隔离级别。
按隔离级别高低排序如下:

针对不同的隔离级别,并发事务时可能发生的现象也会不同:

read uncommitted)read committed)repeatable read)MySQL 的默认隔离级别,它确保同一个事务,在执行多次读取操作数据时,能看到同样的数据行。这种隔离级别会引起幻读。serializable) MySQL 默认使用全局隔离级别,这意味着所有会话都将使用相同的隔离级别。
mysql> select @@global.tx_isolation;
+-----------------------+
| @@global.tx_isolation |
+-----------------------+
| READ-UNCOMMITTED | # 默认是读未提交级别
+-----------------------+一共有两种方式,它们是等价的,只不过第二种写法是第一种写法的缩写而已!
mysql> select @@session.tx_isolation;
+------------------------+
| @@session.tx_isolation |
+------------------------+
| READ-UNCOMMITTED |
+------------------------+
mysql> select @@tx_isolation;
+------------------+
| @@tx_isolation |
+------------------+
| READ-UNCOMMITTED |
+------------------+# 通过 GLOBAL 或者 SESSION 分别指定不同作用域的事务隔离级别
SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL level|access_mode;
# 隔离级别
level: {
REPEATABLE READ # 可重复读
| READ COMMITTED # 读已提交
| READ UNCOMMITTED # 读未提交
| SERIALIZABLE # 串行化
}
# 访问模式
access_mode: {
READ WRITE # 表示事务可以对数据进行读写
| READ ONLY # 表示事务是只能读,不能写
}
# 示例:
# 设置全局事务隔离级别为串行化,后续所有事务生效,不影响当前事务
SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE;
# 设置会话事务隔离级别为串行化,当前会话后续的所有事务生效,不影响当前事务,可以在任何时候执行
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
# 如果不指定任何作用域,设置只针对下一个事务,随后的事务恢复之前的隔离级别
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。