IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> 数据库事务隔离级别举例详解(续) -> 正文阅读

[游戏开发]数据库事务隔离级别举例详解(续)

一、准备工作

MySQL8中,实际运行一些并发的事务,看四个隔离级别中脏读、不可重复读和幻读的影响。
在上一篇数据库事务隔离级别举例详解中涉及的事务A和事务B,分别对应Navicat的2个会话窗口。
测试每一种隔离级别前,应该在2个会话中,先一起设置好一样的隔离级别,如下,

-- 查看默认的自动提交
SHOW VARIABLES LIKE 'autocommit';
-- 查看默认存储引擎
SHOW VARIABLES LIKE '%engine%';
-- 查看MySQL版本
SELECT VERSION();
-- 查看默认的事务隔离级别(mysql8以下)
SELECT @@tx_isolation;
SHOW GLOBAL VARIABLES LIKE 'tx_isolation';
-- 查看默认的事务隔离级别(mysql8)
SELECT @@transaction_isolation;
SHOW GLOBAL VARIABLES LIKE 'transaction_isolation';

-- 设置当前会话的自动提交
SHOW SESSION VARIABLES LIKE 'transaction_isolation';
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;

实例中涉及到的建薪资明细表和插薪资项数据相关的sql脚本需要提前执行,如下,

CREATE TABLE `pay_sheet` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `item_name` varchar(255) DEFAULT NULL,
  `salary` decimal(10,2) unsigned DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

INSERT INTO `pay_sheet`(`item_name`, `salary`, `name`) VALUES ('默认薪资项', 1000.00, 'Jay');

二、测试

2.1、读未提交

2.1.1、脏读

  • 事务A会话:
-- 读未提交的脏读
SHOW SESSION VARIABLES LIKE 'transaction_isolation';
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

-- 事务A在事务B提交前查看
BEGIN; -- step1
SELECT salary FROM pay_sheet WHERE NAME = 'Jay'; -- step4
SELECT salary FROM pay_sheet WHERE NAME = 'Jay'; -- step6
COMMIT;
ROLLBACK;
  • 事务B会话:
-- 读未提交的脏读
SHOW SESSION VARIABLES LIKE 'transaction_isolation';
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

-- 事务B修改
BEGIN; -- step2
UPDATE pay_sheet SET salary = '2000' WHERE NAME = 'Jay'; -- step3
COMMIT;
ROLLBACK; -- step5

按照上面对sql标注的step次序,依次执行对应行,

  1. 事务A的step4读出salary=2000;
  2. 接着事务B的step5回滚;
  3. 然后事务A的step6又读出salary=1000。

之前step4读到的2000就是脏读

2.1.2、不可重复读

  • 事务A会话:
-- 读未提交的不可重复读
SHOW SESSION VARIABLES LIKE 'transaction_isolation';
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

-- 事务A在事务B修改前后查看
BEGIN; -- step1
SELECT salary FROM pay_sheet WHERE NAME = 'Jay'; -- step3
SELECT salary FROM pay_sheet WHERE NAME = 'Jay'; -- step5
COMMIT;
ROLLBACK;
  • 事务B会话:
-- 读未提交的不可重复读
SHOW SESSION VARIABLES LIKE 'transaction_isolation';
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

-- 事务B修改
BEGIN; -- step2
UPDATE pay_sheet SET salary = '2000' WHERE NAME = 'Jay'; -- step4
COMMIT;
ROLLBACK; -- step6

按照上面对sql标注的step次序,依次执行对应行,

  1. 事务A(在事务B修改前)的step3读出salary=1000;
  2. 接着事务B的step4修改salary为2000;
  3. 然后事务A(在事务B修改后)的step5读出salary=2000。

事务A在事务B修改前后的step3和step5读不一致就是不可重复读

2.1.3、幻读

  • 事务A会话:
-- 读未提交的幻读
SHOW SESSION VARIABLES LIKE 'transaction_isolation';
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

-- 事务A在事务B新增前后查看
BEGIN; -- step1
SELECT SUM(salary) FROM pay_sheet WHERE NAME = 'Jay'; -- step3
SELECT SUM(salary) FROM pay_sheet WHERE NAME = 'Jay'; -- step5
COMMIT;
ROLLBACK;
  • 事务B会话:
-- 读未提交的幻读
SHOW SESSION VARIABLES LIKE 'transaction_isolation';
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

-- 事务B新增
BEGIN; -- step2
INSERT INTO `pay_sheet`(`item_name`, `salary`, `name`) VALUES ('额外薪资项', 500.00, 'Jay'); -- step4
COMMIT;
ROLLBACK; --step6

按照上面对sql标注的step次序,依次执行对应行,

  1. 事务A(在事务B新增前)的step3读出sum(salary)=1000;
  2. 接着事务B的step4新增了额外的薪资项salary为500;
  3. 然后事务A(在事务B新增后)的step5读出sum(salary)=1500。

事务A在事务B新增前后的step3和step5读的总薪资不一致就是幻读

2.2、读已提交

2.2.1、不可重复读

  • 事务A会话:
-- 读已提交的不可重复读
SHOW SESSION VARIABLES LIKE 'transaction_isolation';
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

-- 事务A在事务B修改前后查看
BEGIN; -- step1
SELECT salary FROM pay_sheet WHERE NAME = 'Jay'; -- step3
SELECT salary FROM pay_sheet WHERE NAME = 'Jay'; -- step6
COMMIT;
ROLLBACK;
  • 事务B会话:
-- 读已提交的不可重复读
SHOW SESSION VARIABLES LIKE 'transaction_isolation';
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

-- 事务B修改
BEGIN; -- step2
UPDATE pay_sheet SET salary = '2000' WHERE NAME = 'Jay'; -- step4
COMMIT; -- step5
ROLLBACK;	

按照上面对sql标注的step次序,依次执行对应行,

  1. 事务A(在事务B修改前)的step3读出salary=1000;
  2. 接着事务B的step4修改salary为2000,且step5手动提交;
  3. 然后事务A(在事务B修改后)的step6读出salary=2000。

事务A在事务B修改前后的step3和step6读不一致就是不可重复读

2.2.2、幻读

  • 事务A会话:
-- 读已提交的幻读
SHOW SESSION VARIABLES LIKE 'transaction_isolation';
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

-- 事务A在事务B新增前后查看
BEGIN; -- step1
SELECT SUM(salary) FROM pay_sheet WHERE NAME = 'Jay'; -- step3
SELECT SUM(salary) FROM pay_sheet WHERE NAME = 'Jay'; -- step6
COMMIT;
ROLLBACK;
  • 事务B会话:
-- 读已提交的幻读
SHOW SESSION VARIABLES LIKE 'transaction_isolation';
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

-- 事务B新增
BEGIN; -- step2
INSERT INTO `pay_sheet` (`item_name`, `salary`, `name`) VALUES ('额外薪资项', 500.00, 'Jay'); -- step4
COMMIT; -- step5
ROLLBACK;

按照上面对sql标注的step次序,依次执行对应行,

  1. 事务A(在事务B新增前)的step3读出sum(salary)=1000;
  2. 接着事务B的step4新增了额外的薪资项salary为500,且step5手动提交;
  3. 然后事务A(在事务B新增后)的step6读出sum(salary)=1500。

事务A在事务B新增前后的step3和step6读的总薪资不一致就是幻读

2.3、可重复读

2.3.1、幻读

  • 事务A会话:
-- 可重复读的幻读
SHOW SESSION VARIABLES LIKE 'transaction_isolation';
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;

-- 事务A在事务B新增前后查看
BEGIN; -- step1
SELECT SUM(salary) FROM pay_sheet WHERE NAME = 'Jay'; -- step3
SELECT SUM(salary) FROM pay_sheet WHERE NAME = 'Jay'; -- step6
COMMIT;
ROLLBACK;
  • 事务B会话:
-- 可重复读的幻读
SHOW SESSION VARIABLES LIKE 'transaction_isolation';
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;

-- 事务B新增
BEGIN; -- step2
INSERT INTO `pay_sheet` (`item_name`, `salary`, `name`) VALUES ('额外薪资项', 500.00, 'Jay'); -- step4
COMMIT; -- step5
ROLLBACK;

按照上面对sql标注的step次序,依次执行对应行,

  1. 事务A(在事务B新增前)的step3读出sum(salary)=1000;
  2. 接着事务B的step4新增了额外的薪资项salary为500,且step5手动提交;
  3. 然后事务A(在事务B新增后)的step6读出sum(salary)=1500。

事务A在事务B新增前后的step3和step6读的总薪资不一致就是幻读

2.4、串行化

串行化隔离级别中,通过互斥锁避免了脏读、不可重复读和幻读。
若事务A和事务B同时读相同的数据行不会加锁互斥;若不是读读,而是读写、写读、写写时,后面操作的事务只能等待前一个事务提交后(释放锁),才会继续(获取到锁)自动执行。
测试串行化之前,可以在pay_sheet表中,加个不相关的记录做对比,如下,

INSERT INTO `pay_sheet`(`item_name`, `salary`, `name`) VALUES ('默认薪资项', 8000.00, 'Tony');

这样表中就有了两行数据。

2.4.1、读写互斥

  • 事务A会话:
-- 串行化的读写互斥
SHOW SESSION VARIABLES LIKE 'transaction_isolation';
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;

-- 事务A先查看一些行数据
BEGIN; -- step1
SELECT salary FROM pay_sheet WHERE NAME = 'Jay'; -- step3
COMMIT; -- step5
ROLLBACK;
  • 事务B会话:
-- 串行化的读写互斥
SHOW SESSION VARIABLES LIKE 'transaction_isolation';
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;

-- 事务B尝试修改事务A刚刚读的数据
BEGIN; -- step2
SELECT salary FROM pay_sheet WHERE NAME = 'Tony'; -- step4
UPDATE pay_sheet SET salary = '2000' WHERE NAME = 'Jay'; -- step4
COMMIT;
ROLLBACK;

按照上面对sql标注的step次序,依次执行对应行,

  1. 事务A的step3读出Jay的salary=1000(此时Jay这一行记录就被锁住了);
  2. 接着事务B的step4先尝试读取Tony这一行,成功(Tony这一行没有被锁);
  3. 但是事务B的step4也尝试修改Jay的salary为2000,发现会话阻塞(一直在等待释放Jay这一行记录的锁);
  4. 然后事务A的step5提交或回滚,与此同时事务B可以自动修改Jay的salary(获取到了锁)。

如果事务A一直不释放,事务B的会话报错锁等待超时:
Lock wait timeout exceeded; try restarting transaction, Time: 51.563000s
锁等待超时

2.4.2、写读互斥

  • 事务A会话:
-- 串行化的写读互斥
SHOW SESSION VARIABLES LIKE 'transaction_isolation';
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;

-- 事务A尝试修改事务B刚刚修改的数据
BEGIN; -- step1
SELECT salary FROM pay_sheet WHERE NAME = 'Jay'; -- step4
COMMIT;
ROLLBACK;
  • 事务B会话:
-- 串行化的写读互斥
SHOW SESSION VARIABLES LIKE 'transaction_isolation';
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;

-- 事务B先查看一些行数据
BEGIN; -- step2
UPDATE pay_sheet SET salary = '2000' WHERE NAME = 'Jay'; -- step3
COMMIT;
ROLLBACK; -- step5

就相当于把上面读写互斥的顺序反过来,

  1. 事务B的step3修改Jay的salary为2000(此时Jay这一行记录就被锁住了);
  2. 接着事务A的step4也尝试读取Jay的salary,发现会话阻塞(一直在等待释放Jay这一行记录的锁);
  3. 然后事务B的step5提交或回滚,与此同时事务A才自动查询到Jay的salary(获取到了锁)。

2.4.3、写写互斥

  • 事务A会话:
-- 串行化的写写互斥
SHOW SESSION VARIABLES LIKE 'transaction_isolation';
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;

-- 事务A再尝试修改事务B刚刚修改的数据
BEGIN; -- step1
UPDATE pay_sheet SET salary = '3000' WHERE NAME = 'Jay'; -- step4
COMMIT;
ROLLBACK;
  • 事务B会话:
-- 串行化的写写互斥
SHOW SESSION VARIABLES LIKE 'transaction_isolation';
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;

-- 事务B先修改一些行数据
BEGIN; -- step2
UPDATE pay_sheet SET salary = '2000' WHERE NAME = 'Jay'; -- step3
COMMIT;
ROLLBACK; -- step5
  1. 事务B的step3先修改Jay的salary为2000(此时Jay这一行记录就被锁住了);
  2. 接着事务A的step4也尝试修改Jay的salary为3000,发现会话阻塞(一直在等待释放Jay这一行记录的锁);
  3. 然后事务B的step5提交或回滚,与此同时事务A才可以自动修改Jay的salary为3000(获取到了锁)。

2.4.4、读读不互斥

  • 事务A会话:
-- 串行化的读读不互斥
SHOW SESSION VARIABLES LIKE 'transaction_isolation';
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;

-- 事务A先查看Jay的这一行数据
BEGIN; -- step1
SELECT salary FROM pay_sheet WHERE NAME = 'Jay'; -- step3
COMMIT;
ROLLBACK; -- step5
  • 事务B会话:
-- 串行化的读读不互斥
SHOW SESSION VARIABLES LIKE 'transaction_isolation';
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;

-- 事务B也尝试查看Jay的这一行数据
BEGIN; -- step2
SELECT salary FROM pay_sheet WHERE NAME = 'Jay'; -- step4
COMMIT;
ROLLBACK;

事务A和事务B的读都不阻塞。

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2022-04-09 18:49:29  更:2022-04-09 18:50:42 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/16 21:04:34-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码