绵阳市三台县城乡建设局网站,品牌建设的核心,手机免费代理ip网站,网站备案 企业 个人第13章_事务基础知识
#x1f3e0;个人主页#xff1a;shark-Gao
#x1f9d1;个人简介#xff1a;大家好#xff0c;我是shark-Gao#xff0c;一个想要与大家共同进步的男人#x1f609;#x1f609;
#x1f389;目前状况#xff1a;23届毕业生#xff0c;目前…第13章_事务基础知识
个人主页shark-Gao
个人简介大家好我是shark-Gao一个想要与大家共同进步的男人
目前状况23届毕业生目前在某公司实习
❤️欢迎大家这里是CSDN我总结知识的地方欢迎来到我的博客我亲爱的大佬
️个人小站 个人博客欢迎大家访问
配套视频参考MySQL数据库天花板–康师傅
1. 数据库事务概述
1.1 存储引擎支持情况
SHOW ENGINES 命令来查看当前 MySQL 支持的存储引擎都有哪些以及这些存储引擎是否支持事务。 能看出在 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
原子性是指事务是一个不可分割的工作单位要么全部提交要么全部失败回滚。即要么转账成功要么转账失败是不存在中间的状态。如果无法保证原子性会怎么样就会出现数据不一致的情形A账户减去100元而B账户增加100元操作失败系统将无故丢失100元。
一致性consistency
国内很多网站上对一致性的阐述有误具体你可以参考 Wikipedia 对Consistency的阐述
根据定义一致性是指事务执行前后数据从一个 合法性状态 变换到另外一个 合法性状态 。这种状态是 语义上 的而不是语法上的跟具体的业务有关。
那什么是合法的数据状态呢满足 预定的约束 的状态就叫做合法的状态。通俗一点这状态是由你自己来定义的比如满足现实世界中的约束。满足这个状态数据就是一致的不满足这个状态数据就 是不一致的如果事务中的某个操作失败了系统就会自动撤销当前正在执行的事务返回到事务操作 之前的状态。
**举例1**A账户有200元转账300元出去此时A账户余额为-100元。你自然就发现此时数据是不一致的为什么呢因为你定义了一个状态余额这列必须0。
**举例2**A账户有200元转账50元给B账户A账户的钱扣了但是B账户因为各种意外余额并没有增加。你也知道此时的数据是不一致的为什么呢因为你定义了一个状态要求AB的总余额必须不变。
**举例3**在数据表中我们将姓名字段设置为唯一性约束这时当事务进行提交或者事务发生回滚的时候如果数据表的姓名不唯一就破坏了事务的一致性要求。
隔离型isolation
事务的隔离性是指一个事务的执行不能被其他事务干扰即一个事务内部的操作及使用的数据对并发的其他事务是隔离的并发执行的各个事务之间不能相互干扰。
如果无法保证隔离性会怎么样假设A账户有200元B账户0元。A账户往B账户转账两次每次金额为50 元分别在两个事务中执行。如果无法保证隔离性会出现下面的情形
UPDATE accounts SET money money - 50 WHERE NAME AA;
UPDATE accounts SET money money 50 WHERE NAME BB;持久性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 当一个处在 部分提交的 状态的事务将修改过的数据都 同步到磁盘 上之后我们就可以说该事务处在了 提交的 状态。 一个基本的状态转换图如下所示 图中可见只有当事务处于提交的或者中止的状态时一个事务的生命周期才算是结束了。对于已经提交的事务来说该事务对数据库所做的修改将永久生效对于处于中止状态的事务该事务对数据库所做的所有修改都会被回滚到没执行该事务之前的状态。
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 # 开启读写事务和一致性读注意
READ ONLY和READ WRITE是用来设置所谓的事务访问模式的就是以只读还是读写的方式来访问数据库中的数据一个事务的访问模式不能同时即设置为只读的也设置为读写的所以不能同时把READ ONLY和READ WRITE放到START TRANSACTION语句后边。如果我们不显式指定事务的访问模式那么该事务的访问模式就是读写模式
**步骤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;2.3 隐式提交数据的情况 数据定义语言Data definition language缩写为DDL 数据库对象指的就是数据库、表、视图、存储过程等结构。当我们CREATE、ALTER、DROP等语句去修改数据库对象时就会隐式的提交前边语句所属于的事务。即 BEGIN;SELECT ... # 事务中的一条语句
UPDATE ... # 事务中的一条语句
... # 事务中的其他语句CREATE TABLE ... # 此语句会隐式的提交前边语句所属于的事务隐式使用或修改mysql数据库中的表 当我们使用ALTER USER、CREATE USER、DROP USER、GRANT、RENAME USER、REVOKE、SET PASSWORD等语句时也会隐式的提交前边语句所属于的事务。 事务控制或关于锁定的语句 ① 当我们在一个事务还没提交或者回滚时就又使用 START TRANSACTION 或者 BEGIN 语句开启了另一个事务时会隐式的提交上一个事务。即 BEGIN;SELECT ... # 事务中的一条语句
UPDATE ... # 事务中的一条语句
... # 事务中的其他语句BEGIN; # 此语句会隐式的提交前边语句所属于的事务② 当前的 autocommit 系统变量的值为 OFF 我们手动把它调为 ON 时也会 隐式的提交前边语句所属的事务。 ③ 使用 LOCK TABLES 、 UNLOCK TABLES 等关于锁定的语句也会 隐式的提交 前边语句所属的事务。 加载数据的语句 使用LOAD DATA语句来批量往数据库中导入数据时也会隐式的提交前边语句所属的事务。 关于MySQL复制的一些语句 使用START SLAVE、STOP SLAVE、RESET SLAVE、CHANGE MASTER TO等语句会隐式的提交前边语句所属的事务 其他的一些语句 使用ANALYZE TABLE、CACHE INDEX、CAECK TABLE、FLUSH、LOAD INDEX INTO CACHE、OPTIMIZE TABLE、REPAIR TABLE、RESET等语句也会隐式的提交前边语句所属的事务。
2.4 使用举例1提交与回滚
我们看下在 MySQL 的默认状态下下面这个事务最后的处理结果是什么。
情况1
CREATE TABLE user(name varchar(20), PRIMARY KEY (name)) ENGINEInnoDB;BEGIN;
INSERT INTO user SELECT 张三;
COMMIT;BEGIN;
INSERT INTO user SELECT 李四;
INSERT INTO user SELECT 李四;
ROLLBACK;SELECT * FROM user;运行结果1 行数据
mysql commit;
Query OK, 0 rows affected (0.00 秒)mysql BEGIN;
Query OK, 0 rows affected (0.00 秒)mysql INSERT INTO user SELECT 李四;
Query OK, 1 rows affected (0.00 秒)mysql INSERT INTO user SELECT 李四;
Duplicate entry 李四 for key user.PRIMARY
mysql ROLLBACK;
Query OK, 0 rows affected (0.01 秒)mysql select * from user;
--------
| name |
--------
| 张三 |
--------
1 行于数据集 (0.01 秒)情况2
CREATE TABLE user (name varchar(20), PRIMARY KEY (name)) ENGINEInnoDB;BEGIN;
INSERT INTO user SELECT 张三;
COMMIT;INSERT INTO user SELECT 李四;
INSERT INTO user SELECT 李四;
ROLLBACK;运行结果2 行数据
mysql SELECT * FROM user;
--------
| name |
--------
| 张三 |
| 李四 |
--------
2 行于数据集 (0.01 秒)情况3
CREATE TABLE user(name varchar(255), PRIMARY KEY (name)) ENGINEInnoDB;SET completion_type 1;
BEGIN;
INSERT INTO user SELECT 张三;
COMMIT;INSERT INTO user SELECT 李四;
INSERT INTO user SELECT 李四;
ROLLBACK;SELECT * FROM user;运行结果1 行数据
mysql SELECT * FROM user;
--------
| name |
--------
| 张三 |
--------
1 行于数据集 (0.01 秒)当我们设置 autocommit0 时不论是否采用 START TRANSACTION 或者 BEGIN 的方式来开启事 务都需要用 COMMIT 进行提交让事务生效使用 ROLLBACK 对事务进行回滚。 当我们设置 autocommit1 时每条 SQL 语句都会自动进行提交。 不过这时如果你采用 START TRANSACTION 或者 BEGIN 的方式来显式地开启事务那么这个事务只有在 COMMIT 时才会生效 在 ROLLBACK 时才会回滚。 2.5 使用举例2测试不支持事务的engine
CREATE TABLE test1(i INT) ENGINEInnoDB;CREATE TABLE test2(i INT) ENGINEMYISAM;针对于InnoDB表
BEGIN;
INSERT INTO test1 VALUES(1);
ROLLBACK;SELECT * FROM test1;结果没有数据
针对于MYISAM表
BEGIN;
INSERT INTO test1 VALUES(1);
ROLLBACK;SELECT * FROM test2;结果有一条数据
2.6 使用举例3SAVEPOINT
创建表并添加数据
CREATE TABLE account(
id INT PRIMARY KEY AUTO_INCREMENT,
NAME VARCHAR(15),
balance DECIMAL(10,2)
);INSERT INTO account(NAME,balance)
VALUES
(张三,1000),
(李四,1000);BEGIN;
UPDATE account SET balance balance - 100 WHERE NAME 张三;
UPDATE account SET balance balance - 100 WHERE NAME 张三;
SAVEPOINT s1; # 设置保存点
UPDATE account SET balance balance 1 WHERE NAME 张三;
ROLLBACK TO s1; # 回滚到保存点结果张三800.00
ROLLBACK;结果张三1000.00
3. 事务隔离级别
MySQL是一个 客户端服务器 架构的软件对于同一个服务器来说可以有若干个客户端与之连接每 个客户端与服务器连接上之后就可以称为一个会话 Session 。每个客户端都可以在自己的会话中 向服务器发出请求语句一个请求语句可能是某个事务的一部分也就是对于服务器来说可能同时处理多个事务。事务有 隔离性 的特性理论上在某个事务 对某个数据进行访问 时其他事务应该进行排队 当该事务提交之后其他事务才可以继续访问这个数据。但是这样对 性能影响太大 我们既想保持事务的隔离性又想让服务器在处理访问同一数据的多个事务时 性能尽量高些 那就看二者如何权衡取 舍了。
3.1 数据准备
CREATE TABLE student (studentno INT,name VARCHAR(20),class varchar(20),PRIMARY KEY (studentno)
) EngineInnoDB CHARSETutf8;然后向这个表里插入一条数据
INSERT INTO student VALUES(1, 小谷, 1班);现在表里的数据就是这样的
mysql select * from student;
--------------------------
| studentno | name | class |
--------------------------
| 1 | 小谷 | 1班 |
--------------------------
1 row in set (0.00 sec)3.2 数据并发问题
针对事务的隔离性和并发性我们怎么做取舍呢先看一下访问相同数据的事务在 不保证串行执行 也 就是执行完一个再执行另一个的情况下可能会出现哪些问题
1. 脏写 Dirty Write
对于两个事务 Session A、Session B如果事务Session A 修改了 另一个 未提交 事务Session B 修改过 的数据那就意味着发生了 脏写示意图如下 Session A 和 Session B 各开启了一个事务Sesssion B 中的事务先将studentno列为1的记录的name列更新为’李四’然后Session A中的事务接着又把这条studentno列为1的记录的name列更新为’张三’。如果之后Session B中的事务进行了回滚那么Session A中的更新也将不复存在这种现象称之为脏写。这时Session A中的事务就没有效果了明明把数据更新了最后也提交事务了最后看到的数据什么变化也没有。这里大家对事务的隔离性比较了解的话会发现默认隔离级别下上面Session A中的更新语句会处于等待状态这里只是跟大家说明一下会出现这样的现象。
2. 脏读 Dirty Read
对于两个事务 Session A、Session BSession A 读取 了已经被 Session B 更新 但还 没有被提交 的字段。 之后若 Session B 回滚 Session A 读取 的内容就是 临时且无效 的。 Session A和Session B各开启了一个事务Session B中的事务先将studentno列为1的记录的name列更新 为’张三’然后Session A中的事务再去查询这条studentno为1的记录如果读到列name的值为’张三’而 Session B中的事务稍后进行了回滚那么Session A中的事务相当于读到了一个不存在的数据这种现象就称之为 脏读 。
3. 不可重复读 Non-Repeatable Read
对于两个事务Session A、Session BSession A 读取了一个字段然后 Session B 更新了该字段。 之后 Session A 再次读取 同一个字段 值就不同 了。那就意味着发生了不可重复读。 我们在Session B中提交了几个 隐式事务 注意是隐式事务意味着语句结束事务就提交了这些事务 都修改了studentno列为1的记录的列name的值每次事务提交之后如果Session A中的事务都可以查看到最新的值这种现象也被称之为 不可重复读 。
4. 幻读 Phantom
对于两个事务Session A、Session B, Session A 从一个表中 读取 了一个字段, 然后 Session B 在该表中 插 入 了一些新的行。 之后, 如果 Session A 再次读取 同一个表, 就会多出几行。那就意味着发生了幻读。 Session A中的事务先根据条件 studentno 0这个条件查询表student得到了name列值为’张三’的记录 之后Session B中提交了一个 隐式事务 该事务向表student中插入了一条新记录之后Session A中的事务 再根据相同的条件 studentno 0查询表student得到的结果集中包含Session B中的事务新插入的那条记 录这种现象也被称之为 幻读 。我们把新插入的那些记录称之为 幻影记录 。 3.3 SQL中的四种隔离级别
上面介绍了几种并发事务执行过程中可能遇到的一些问题这些问题有轻重缓急之分我们给这些问题 按照严重性来排一下序
脏写 脏读 不可重复读 幻读我们愿意舍弃一部分隔离性来换取一部分性能在这里就体现在设立一些隔离级别隔离级别越低并发问题发生的就越多。 SQL标准 中设立了4个 隔离级别
READ UNCOMMITTED 读未提交在该隔离级别所有事务都可以看到其他未提交事务的执行结 果。不能避免脏读、不可重复读、幻读。READ COMMITTED 读已提交它满足了隔离的简单定义一个事务只能看见已经提交事务所做 的改变。这是大多数数据库系统的默认隔离级别但不是MySQL默认的。可以避免脏读但不可 重复读、幻读问题仍然存在。REPEATABLE READ 可重复读事务A在读到一条数据之后此时事务B对该数据进行了修改并提 交那么事务A再读该数据读到的还是原来的内容。可以避免脏读、不可重复读但幻读问题仍 然存在。这是MySQL的默认隔离级别。SERIALIZABLE 可串行化确保事务可以从一个表中读取相同的行。在这个事务持续期间禁止 其他事务对该表执行插入、更新和删除操作。所有的并发问题都可以避免但性能十分低下。能避 免脏读、不可重复读和幻读。
SQL标准 中规定针对不同的隔离级别并发事务可以发生不同严重程度的问题具体情况如下 脏写 怎么没涉及到因为脏写这个问题太严重了不论是哪种隔离级别都不允许脏写的情况发生。
不同的隔离级别有不同的现象并有不同的锁和并发机制隔离级别越高数据库的并发性能就越差4 种事务隔离级别与并发性能的关系如下 3.4 MySQL支持的四种隔离级别 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.5 如何设置事务的隔离级别
通过下面的语句修改事务的隔离级别
SET [GLOBAL|SESSION] TRANSACTION ISOLATION LEVEL 隔离级别;
#其中隔离级别格式READ UNCOMMITTEDREAD COMMITTEDREPEATABLE READSERIALIZABLE或者
SET [GLOBAL|SESSION] TRANSACTION_ISOLATION 隔离级别
#其中隔离级别格式READ-UNCOMMITTEDREAD-COMMITTEDREPEATABLE-READSERIALIZABLE关于设置时使用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_isolationSERIALIZABLE那么事务的默认隔离界别就从原来的REPEATABLE-READ变成了SERIALIZABLE。 小结 数据库规定了多种事务隔离级别不同隔离级别对应不同的干扰程度隔离级别越高数据一致性就越好但并发性越弱。 3.6 不同隔离级别举例
初始化数据
TRUNCATE TABLE account;
INSERT INTO account VALUES (1,张三,100), (2,李四,0);演示1. 读未提交之脏读
设置隔离级别为未提交读 脏读就是指当前事务就在访问数据并且对数据进行了修改而这种修改还没有提交到数据库中这时另外一个事务也访问了这个数据然后使用了这个数据。
演示2读已提交 演示3. 不可重复读
设置隔离级别为可重复读事务的执行流程如下 当我们将当前会话的隔离级别设置为可重复读的时候当前会话可以重复读就是每次读取的结果集都相同而不管其他事务有没有提交。但是在可重复读的隔离级别上会出现幻读的问题。
演示4幻读 4. 事务的常见分类
从事务理论的角度来看可以把事务分为以下几种类型
扁平事务Flat Transactions带有保存点的扁平事务Flat Transactions with Savepoints链事务Chained Transactions嵌套事务Nested Transactions分布式事务Distributed Transactions