mysql 事务控制语句(事务操作) (TCL --- Tran

2023-03-12  本文已影响0人  暴躁程序员

一、mysql 事务的概念

一个或者多个 sql 语句组成一个执行单元,如果执行单元中某个 sql 语句执行失败,那么整个单元将会回滚,如果执行单元中所有 sql 语句都执行成功,那么事务顺利执行

1.mysql 事务的特性(ACID)

1. 原子性(Atomicity)
事务中的sql语句组成一个不可分割的执行单元,执行单元中的sql要么全部执行,要么全部不执行
2. 一致性(Consistency)
事务必须使数据库从一个一致性状态变换到另外一个一致性状态
3. 隔离性(Isolation)
并发执行的各个事务之间是相互隔离的,不能相互干扰(通过隔离级别控制)
4. 持久性(Durability)
事务一旦被提交,它对数据库中数据的改变就是永久的,不受数据库宕机和其他操作等因素影响

2.mysql 存储引擎

SHOW ENGINES 查看 mysql 存储引擎

CSV
MRG_MyISAM
MEMORY
Aria
MyISAM
SEQUENCE
InnoDB  (支持事务)
PERFORMANCE_SCHEMA

二、创建事务

1. 隐式事务

mysql 默认开启事务自动提交,此状态下,每一条 sql 都是一个单独的事务且执行后会自动提交

SHOW VARIABLES LIKE 'autocommit'   -- 查看是否开启事务自动提交,ON 开启,OFF 关闭
SET autocommit = 1; -- 开启事务自动提交(只在当前会话下有效)
SET autocommit = 0; -- 关闭事务自动提交(只在当前会话下有效)

2. 显式事务

显式事务具有明显的开始和结束标记,必须先关闭事务自动提交(只在当前会话下有效)
示例:张三有 1000 元,李四有 1000 元,张三给李四转 200 元,事务执行成功后,张三有 1000-200=800 元,李四有 1000+200=1200 元

  1. 创建测试表和数据
-- 创建账户表
CREATE TABLE account(
    id INT PRIMARY KEY AUTO_INCREMENT,
    uname VARCHAR(22) NOT NULL,
    balance DOUBLE
)
-- 插入测试数据
INSERT INTO account(uname,balance) VALUES('张三',1000.00),('李四',1000.00)
  1. 创建显式事务
SET autocommit = 0; -- 1, 关闭事务自动提交
START TRANSACTION; -- 2, 开启事务
-- 3. 执行 sql 执行单元
UPDATE account SET balance = 1000.00-200 WHERE id=1 AND uname = '张三';
UPDATE account SET balance = 1000.00+200 WHERE id=2 AND uname = '李四';

COMMIT; -- 4. 结束事务(COMMIT提交事务,或者 ROLLBACK 回滚事务)

三、mysql 事务的隔离级别

1. mysql 事务的隔离级别从低到高

分别是:读未提交、读已提交、可重复读(mysql 默认)、串行化,默认隔离级别为可重复读

SELECT @@global.tx_isolation        -- 查看系统的隔离级别
SELECT @@tx_isolation               -- 查看当前会话事务的隔离级别
  1. 读未提交
    存在问题:脏读,不可重复度,幻读
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;  -- 设置当前会话事务的隔离级别为 读未提交
  1. 读已提交
    存在问题:不可重复度,幻读
    解决问题:脏读
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;  -- 设置当前会话事务的隔离级别为 读已提交
  1. 可重复读(mysql 默认)
    存在问题:幻读
    解决问题:脏读,不可重复度
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;  -- 设置当前会话事务的隔离级别为 可重复读
  1. 串行化
    存在问题:无,但是串行加锁,性能低
    解决问题:脏读,不可重复度,幻读
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;;  -- 设置当前会话事务的隔离级别为 串行化

2. 脏读、不可重复读、幻读(在并发请求中出现)

出现 脏读、不可重复读、幻读的原因:在 A 事务已经开始并且还未结束的这段时间,如果 B 事务对 A 事务涉及到的数据执行了插入、删除、修改操作,那么在 A 事务结束前,可以查看到其他事务修改的数据但是无法看到其他事务插入和删除的数据,即:
如果 B 事务修改数据,A 事务第二次查看了数据,让 B 事务以 ROLLBACK 回滚的方式结束事务,那么会出现脏读的问题(读取了不存在的数据)
如果 B 事务修改了数据,A 事务第二次查看了数据,让 B 事务以 COMMIT 提交的方式结束事务,那么会出现不可重复读的问题(事务结束前,先后读取的数据不同)
如果 B 事务删除和插入了数据并提交事务,A 事务第二次查看了数据,结果和第一次查看的结果相同,或者 A 事务第二次插入数据,结果插入失败,那么会出现幻读读的问题(插入:看不到已经存在的数据,删除:看到了已经不存在的数据),

创建测试表和数据

-- 创建账户表
CREATE TABLE account(
    id INT PRIMARY KEY AUTO_INCREMENT,
    uname VARCHAR(22) NOT NULL,
    balance DOUBLE
)
-- 插入测试数据
INSERT INTO account(uname,balance) VALUES('张三',1000.00),('李四',1000.00)
  1. 脏读:一个事务读取到另一个事务还没有提交的数据
    示例:张三给李四转 200 元,此事务开启但还未结束时,李四查询了账户表,结果是张三 800,李四 1200,如果张三在结束事务时使用了 ROLLBACK 回滚,那么李四查询的 张三 800,李四 1200 就是错误的脏数据
-- 第一步:张三给李四转200元,开启事务但未结束事务
-- 在张三客户端下
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SET autocommit = 0;
START TRANSACTION;
UPDATE account SET balance = 1000.00-200 WHERE id=1 AND uname = '张三';
UPDATE account SET balance = 1000.00+200 WHERE id=2 AND uname = '李四';

-- 第二步:李四在张三未结束事务时,查询了数据
-- 在李四客户端下
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SELECT * FROM account;

-- 第三步:张三通过ROLLBACK回滚结束事务
-- 在张三客户端下
ROLLBACK;

-- 解决脏读的问题后:正常情况下 李四查询的结果是 张三 1000,李四1000
  1. 不可重复读:一个事务先后读取同一条记录,但两次读取的数据不同(读、修改)
    示例:李四在一个事务中需要查询多次账户表,第一次查询结果:张三 1000,李四 1000,然后张三给李四转了 200 元,第二次查询结果:张三 800,李四 1200,造成了在同一个事务中多次读取的数据不一致
-- 第一步:李四第一次查询结果:张三 1000,李四1000
-- 在李四客户端下
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SET autocommit = 0;
START TRANSACTION;
SELECT * FROM account;

-- 第二步:张三给李四转200元,并提交
-- 在张三客户端下
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SET autocommit = 0;
START TRANSACTION;
UPDATE account SET balance = 1000.00-200 WHERE id=1 AND uname = '张三';
UPDATE account SET balance = 1000.00+200 WHERE id=2 AND uname = '李四';
COMMIT;

-- 第三步:李四第二次查询结果:张三 800,李四1200
-- 在李四客户端下
SELECT * FROM account;
COMMIT;

-- 解决不可重复读的问题后:正常情况下 第一次和第二次的查询的结果都是 张三 1000,李四1000
  1. 幻读:一个事务在前后两次查询同一个范围的时候,后一次查询看到了前一次查询没有看到的行(读、插入、删除)
    示例:李四在一个事务中需要查询两次账户表,第一次查询结果:1 张三,2 李四,此时 admin 新插入了一个王五,再删除 1 张三 并提交事务,第二次查询结果:1 张三,2 李四,但是实际表中是 2 李四,3 王五,造成了幻读问题(李四的事务还未结束前的这段时间对于其他事务的插入和删除都看不到,如果在此期间 admin 执行:判断 id=3 数据是否存在,不存在则插入数据,那么会插入失败,原因是 id 为 3 的数据已经存在,不可再次插入)
-- 第一步:李四第一次查询结果:1 张三,2 李四
-- 在李四客户端下
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SET autocommit = 0;
START TRANSACTION;
SELECT id,uname FROM account;


-- 第二步:admin 插入和删除一条数据并提交,实际表中数据:2 李四,3 王五
-- 在admin客户端下
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SET autocommit = 0;
START TRANSACTION;
INSERT INTO account(uname,balance) VALUES('王五',1000.00);
DELETE FROM account WHERE id = 1;
COMMIT;

-- 第三步:李四第二次查询结果:1 张三,2 李四
-- 在李四客户端下
SELECT * FROM account;
COMMIT;

四、回滚点 SAVEPOINT

结束事务时回滚到指定位置

SET autocommit = 0;
START TRANSACTION;
INSERT INTO account(uname,balance) VALUES('王五',1000.00);
SAVEPOINT x; -- 定义回滚点
INSERT INTO account(uname,balance) VALUES('赵六',1000.00);
ROLLBACK TO x; -- 回滚到指定回滚点
上一篇下一篇

猜你喜欢

热点阅读