侧边栏壁纸
博主昵称
流苏小筑

步伐虽小,密而不止

事务基础知识

2024年06月24日 45阅读 0评论 0点赞

1. 数据库事务概述

1.1 存储引擎支持情况

SHOW ENGINES 命令来查看当前 MySQL 支持的存储引擎都有哪些,以及这些存储引擎是否支持事务。

m4gvvcfr.png

能看出在 MySQL 中,只有InnoDB 是支持事务的。

1.2 基本概念

事务:一组逻辑操作单元,使数据从一种状态变换到另一种状态。

事务处理的原则:保证所有事务都作为 一个工作单元 来执行,即使出现了故障,都不能改变这种执行方 式。当在一个事务中执行多个操作时,要么所有的事务都被提交( 存下来;要么数据库管理系统将 放弃 所作的所有 commit ),那么这些修改就 永久 地保 修改 ,整个事务回滚( rollback )到最初状态。

# 案例:AA用户给BB用户转账100
update account set money = money - 100 where name = 'AA';
# 服务器宕机
update account set money = money + 100 where name = 'BB';

1.3 事务的ACID特性

  • 原子性(atomicity):

原子性是指事务是一个不可分割的工作单位,要么全部提交,要么全部失败回滚。

  • 一致性(consistency):

一致性是指事务执行前后,数据从一个 合法性状态 变换到另外一个 合法性状态 。这种状态 是 语义上 的而不是语法上的,跟具体的业务有关。

  • 隔离型(isolation):

事务的隔离性是指一个事务的执行 不能被其他事务干扰,即一个事务内部的操作及使用的数据对 并发其他事务是隔离的,并发执行的各个事务之间不能相互干扰。

m4gvvj2u.png

  • 持久性(durability):

    持久性是指一个事务一旦被提交,它对数据库中数据的改变就是 永久性的 ,接下来的其他操作和数据库 故障不应该对其有任何影响。

    持久性是通过 事务日志 来保证的。日志包括了 重做日志 和 回滚日志 。当我们通过事务对数据进行修 改 的时候,首先会将数据库的变化信息记录到重做日志中,然后再对数据库中对应的行进行修改。这样 做 的好处是,即使数据库系统崩溃,数据库重启后也能找到没有更新到数据库系统中的重做日志,重新 执 行,从而使事务具有持久性。

总结:ACID是事务的四大特征,在这四个特性中,原子性是基础,隔离性是手段,一致性是约束条件, 而持久性是我们的目的。

数据库事务,其实就是数据库设计者为了方便起见,把需要保证 原子性、 隔离性、 一致性 和 持久性 的一个或多个数据库操作称为一个事务

1.4 事务的状态

我们现在知道 事务 是一个抽象的概念,它其实对应着一个或多个数据库操作,MySQL根据这些操作所 执 行的不同阶段把 事务 大致划分成几个状态:

  • 活动的(active)

事务对应的数据库操作正在执行过程中时,我们就说该事务处在

  • 部分提交的(partially committed)

当事务中的最后一个操作执行完成,但由于操作都在内存中执行,所造成的影响并 没有刷新到磁盘 时,我们就说该事务处在 部分提交的 状态。

  • 失败的(failed)

当事务处在 活动的 或者 部分提交的 状态时,可能遇到了某些错误(数据库自身的错误、操作系统 错误或者直接断电等)而无法继续执行,或者人为的停止当前事务的执行,我们就说该事务处在 失 败的 状态。

  • 中止的(aborted)

如果事务执行了一部分而变为 失败的 状态,那么就需要把已经修改的事务中的操作还原到事务执 行前的状态。换句话说,就是要撤销失败事务对当前数据库造成的影响。我们把这个撤销的过程称 之为 回滚 。当 回滚 操作执行完毕时,也就是数据库恢复到了执行事务之前的状态,我们就说该 事 务处在了 中止的 状态。

举例:

UPDATE accounts SET money = money - 50 WHERE NAME = 'AA';
UPDATE accounts SET money = money + 50 WHERE NAME = 'BB';
  • 提交的(committed)

当一个处在 部分提交的 状态的事务将修改过的数据都 在了 提交的 状态。

一个基本的状态转换图如下所示:

m4gvvpxk.png

图中可见,只有当事务处于 提交的或者 中止的状态时,一个事务的生命周期才算是结束了。对于 已经提交的事务来说,该事务对数据库所做的修改将永久生效,对于处于中止状态的事务,该事务 对数据库所做的所有修改都会被回滚到没执行该事务之前的状态。

2. 如何使用事务

使用事务有两种方式,分别为 显式事务隐式事务

2.1 显式事务

步骤1: START TRANSACTION 或者 BEGIN ,作用是显式开启一个事务。

mysql> BEGIN;
#或者
mysql> START TRANSACTION;

START TRANSACTION 语句相较于 BEGIN 特别之处在于,后边能跟随几个 修饰符 :

① READ ONLY :标识当前事务是一个 修饰符 : 只读事务 ,也就是属于该事务的数据库操作只能读取数据,而不 能修改数据。

补充:只读事务中只是不允许修改那些其他事务也能访问到的表中的数据,对于临时表来说(我们 使用CREATE TMEPORARY TABLE 创建的表),由于它们只能在当前会话中可见,所有只读事务 其实也是可以对临时表进行增、删、改操作的。

② READ WRITE :标识当前事务是一个 读写事务 也可以修改数据。

③ WITH CONSISTENT SNAPSHOT :启动一致性读。

比如:

START TRANSACTION READ ONLY; # 开启一个只读事务
START TRANSACTION READ ONLY, WITH CONSISTENT SNAPSHOT # 开启只读事务和一致性读
START TRANSACTION READ WRITE, WITH CONSISTENT SNAPSHOT # 开启读写事务和一致性读

步骤2:一系列事务中的操作(主要是DML,不含DDL)

步骤3:提交事务 或 中止事务(即回滚事务)

# 提交事务。当提交事务后,对数据库的修改是永久性的。
mysql> COMMIT;

# 回滚事务。即撤销正在进行的所有没有提交的修改
mysql> ROLLBACK;
# 将事务回滚到某个保存点。
mysql> ROLLBACK TO [SAVEPOINT]

#其中关于SAVEPOINT相关操作有:
# 在事务中创建保存点,方便后续针对保存点进行回滚。一个事务中可以存在多个保存点。
SAVEPOINT 保存点名称;

# 删除某个保存点
RELEASE SAVEPOINT 保存点名称;

2.2 隐式事务

MySQL中有一个系统变量 autocommit :

 mysql> SHOW VARIABLES LIKE 'autocommit';
 +---------------+-------+
 | Variable_name | Value |
 +---------------+-------+
 | autocommit    |  ON   |
 +---------------+-------+
 1 row in set (0.01 sec)

当然,如果我们想关闭这种 自动提交 的功能,可以使用下边两种方法之一:

  • 显式的的使用 START TRANSACTION 或者 BEGIN 语句开启一个事务。这样在本次事务提交或者回 滚前会暂时关闭掉自动提交的功能。
  • 把系统变量 autocommit 的值设置为 OFF ,就像这样:
SET autocommit = OFF;
#或
SET autocommit = 0;

这样的话,我们写入的多条语句就算是属于同一个事务了,直到我们显式的写出C0MMIT语句来把这个事务提交掉,或者显式的写出ROLLBACK语句来把这个事务回滚掉。

2.3 隐式提交数据的情况

1.数据定义语言(Data definition language,缩写为:DDL)

数据库对象,指的就是 数据库、表、视图、存储过程等结构。当我们 CREATE、ALTER、DROP 等语句 去修改数据库对象时,就会隐式的提交前边语句所属于的事务。即:

BEGIN;
SELECT ... # 事务中的一条语句
UPDATE ... # 事务中的一条语句
... # 事务中的其他语句
CREATE TABLE ... # 此语句会隐式的提交前边语句所属于的事务

2. 隐式使用或修改mysql数据库中的表

当我们使用 ALTER USER 、 CREATE USER 、 DROP USER 、 GRANT 、 SET PASSWORD 等语句时也会隐式的提交前边语句所属于的事务。

3. 事务控制或关于锁定的语句

①当我们在一个事务还没提交或者回滚时就又使用 START TRANSACTION 或者 BEGIN 语句开启了 另一个事务时,会隐式的提交上一个事务。即:

BEGIN;
SELECT ... # 事务中的一条语句
UPDATE ... # 事务中的一条语句
... # 事务中的其他语句
BEGIN; # 此语句会隐式的提交前边语句所属于的事务

②当前的 autocommit 系统变量的值为 OFF ,我们手动把它调为 ON 时,也会 隐式的提交前边语 句所属的事务。

③使用 LOCK TABLES 、 UNLOCK TABLES 等关于锁定的语句也会 隐式的提交 前边语句所属的事务。

4. 加载数据的语句

使用 LOAD DATA 语句来批量往数据库中导入数据时,也会 隐式的提交前边语句所属的事务。

5. 关于MySQL复制的一些语句

使用 START SLAVE、STOP SLAVE、RESET SLAVE、CHANGE MASTER TO 等语句会隐式的提交前边语 句所属的事务

6. 其他的一些语句

使用 ANALYZE TABLE、CACHE INDEX、CAECK TABLE、FLUSH、LOAD INDEX INTO CACHE、 OPTIMIZE TABLE、REPAIR TABLE、RESET 等语句也会隐式的提交前边语句所属的事务。

3. 事务隔离级别

MySQL是一个 客户端/服务器 架构的软件,对于同一个服务器来说,可以有若干个客户端与之连接,每 个客户端与服务器连接上之后,就可以称为一个会话( Session )。每个客户端都可以在自己的会话 中 向服务器发出请求语句,一个请求语句可能是某个事务的一部分,也就是对于服务器来说可能同时处 理多个事务。事务有 隔离性的特性,理论上在某个事务 对某个数据进行访问 时,其他事务应该进行 排 队 ,当该事务提交之后,其他事务才可以继续访问这个数据。但是这样对 性能影响太大 , 我们既想保持事务的隔离性,又想让服务器在处理访问同一数据的多个事务时 性能影响太大 ,我们既想保 性能尽量高些 ,那就看二者如何权衡 取 舍了。

3.1 SQL中的四种隔离级别

m4gvw360.png

1. 读未提交:脏读

脏读 :事务B读取到,事务A 还没有提交的数据(读取到临时数据)。

m4gvw6vv.png

2. 读已提交:不可重复读

不可重复读:在一个事物内,同一条sql多次读取到的数据不一致(受其它事务影响)。

3. 可重复读(MySql默认隔离级别):幻读

幻读:事务A的插入或者删除操作,会影响事务B内同一条查询sql的读取结果。

4. 序列化读

性能低下。

m4gvwcar.png

3.2 MySQL支持的四种隔离级别

不同的数据库厂商对SQL标准中规定的四种隔离级别支持不一样。比如,Oracle就只支持READ C0 MMITTED(默认隔离级别)和SERIALIZABLE隔离级别。MySQL虽然支持4种隔离级别,但与SQL标准中所规定的各级隔离级别允许发生的问题却有些出入,MySQL在REPEATABLE READ(可重复读)隔离级别下,使用MVCC多版本并发控制方式解决的幻读问题。

MySQL的默认隔离级别为REPEATABLE READ,我们可以手动修改一下事务的隔离级别。

# 查看隔离级别,MySQL 5.7.20的版本之前:
mysql> SHOW VARIABLES LIKE 'tx_isolation';
 +---------------+-----------------+
 | Variable_name | Value           |
 +---------------+-----------------+
 | tx_isolation  | REPEATABLE-READ |
 +---------------+-----------------+
 1 row in set (0.00 sec)
 # MySQL 5.7.20版本之后,引入transaction_isolation来替换tx_isolation
 # 查看隔离级别,MySQL 5.7.20的版本及之后:
mysql> SHOW VARIABLES LIKE 'transaction_isolation';
 +-----------------------+-----------------+
 | Variable_name         | Value           |
 +-----------------------+-----------------+
 | transaction_isolation | REPEATABLE-READ |
 +-----------------------+-----------------+
 1 row in set (0.02 sec)
#或者不同MySQL版本中都可以使用的:
SELECT @@transaction_isolation;

3.3 如何设置事务的隔离级别

通过下面的语句修改事务的隔离级别:

SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL 隔离级别;
#其中,隔离级别格式:
> READ UNCOMMITTED
> READ COMMITTED
> REPEATABLE READ
> SERIALIZABLE

或者:

SET [GLOBAL|SESSION] TRANSACTION_ISOLATION = '隔离级别'
#其中,隔离级别格式:
> READ-UNCOMMITTED
> READ-COMMITTED
> REPEATABLE-READ
> SERIALIZABLE

关于设置时使用GLOBAL或SESSION的影响:

  • 使用 GLOBAL 关键字(在全局范围影响):
SET GLOBAL TRANSACTION ISOLATION LEVEL SERIALIZABLE;
#或
SET GLOBAL TRANSACTION_ISOLATION = 'SERIALIZABLE';

则:

- 当前已经存在的会话无效 
- 只对执行完该语句之后产生的会话起作用  
  • 使用 SESSION 关键字(在会话范围影响):
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
#或
SET SESSION TRANSACTION_ISOLATION = 'SERIALIZABLE';

则:

- 对当前会话的所有后续的事务有效 
- 如果在事务之间执行,则对后续的事务有效 
- 该语句可以在已经开启的事务中间执行,但不会影响当前正在执行的事务  

如果在服务器启动时想改变事务的默认隔离级别,可以修改启动参数transaction_isolation的值。 比如,在启动服务器时指定了transaction_isolation=SERIALIZABLE,那么事务的默认隔离界别就 从原来的REPEATABLE- READ变成了SERIALIZABLE。

0

—— 评论区 ——

昵称
邮箱
网址
取消