当前位置: 首页 > news >正文

【眼见为实】自己动手实践理解数据库READ UNCOMMITED SERIALIZABLE

目录

  • 准备工作
    • ①准备测试表和测试数据
    • ②关闭数据库事务自动提交
    • ③设置InnoDB存储引擎隔离级别
  • [READ UNCOMMITTED]
    • [READ UNCOMMITTED]能解决的问题
    • [READ UNCOMMITTED]不能解决的问题
  • [SERIALIZABLE]

我们自己通过Sql语句模拟场景来验证Mysql InnoDB引擎事务各级隔离级别对应封锁协议的工作机制。在开始实践之前我们需要做一些准备工作。

准备工作

①准备测试表和测试数据

需要建立一个测试数据表,建表语句:

create table users 
(
    id int auto_increment not null primary key,
    name char(10) not null,
    state int not null
); 

然后插入一条测试数据:

insert into users values(1,'swj',0);

②关闭数据库事务自动提交

# 0为关闭 1为开启
SET autocommit = 0;

设置完成后我们可以通过下列语句查看是否关闭了自动提交。

show variables like 'autocommit';

OFF关闭无误。
mark

③设置InnoDB存储引擎隔离级别

首先我们可以使用下面的语句分别查看数据库的系统级隔离级别和会话级隔离级别。

select @@global.tx_isolation,@@tx_isolation;

mark
这也说明了MySql数据库默认使用的隔离级别是可重复读(REPEATABLE-READ)
我们可以使用下面的语句设置隔离级别。隔离级别有READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE四种。

SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE}

[READ UNCOMMITTED]

首先设置数据库隔离级别为读未提交(READ UNCOMMITTED):

set global transaction isolation level READ UNCOMMITTED ;
set session transaction isolation level READ UNCOMMITTED ; 

[READ UNCOMMITTED]能解决的问题

我们来看一下为什么[READ UNCOMMITTED]能解决丢失更新的问题:
事务1

START TRANSACTION;
① UPDATE users SET state=state+1 WHERE id=1;
② SELECT sleep(10);
COMMIT;

事务2

START TRANSACTION;
① UPDATE users SET state=state+1 WHERE id=1;
COMMIT;

事务1先于事务2执行。
事务1的执行信息

[SQL 1]START TRANSACTION;
受影响的行: 0
时间: 0.000s

[SQL 2]
UPDATE users SET state=state+1 WHERE id=1;
受影响的行: 1
时间: 0.001s

[SQL 3]
SELECT sleep(10);
受影响的行: 0
时间: 10.000s

[SQL 4]
COMMIT;
受影响的行: 0
时间: 0.068s

事务2的执行信息

[SQL 1]START TRANSACTION;
受影响的行: 0
时间: 0.000s

[SQL 2]
UPDATE users SET state=state+1 WHERE id=1;
受影响的行: 1
时间: 8.787s

[SQL 3]
COMMIT;
受影响的行: 0
时间: 0.098s

执行结果
mark
结论:读未提交[READ UNCOMMITTED]隔离级别可以解决丢失更新的问题。

分析:因为读未提交[READ UNCOMMITTED]隔离级别对应数据库的一级封锁协议。一级封锁协议在修改数据之前对其加X锁,直到事务结束释放X锁。读数据不加锁。因为事务1先执行修改,修改前申请持有X锁,事务结束释放X锁。持锁时间段为[SQL 2]开始前到[SQL 4]结束,持锁时间大约为10.069s。事务2也执行修改操作,修改前也申请持有X锁。因为事务1执行更新操作等待10秒才会提交释放锁,所以事务2申请持锁需要等待,直到事务1结束才能获取到锁的持有权进行修改。事务2的执行信息中的[SQL 2]时间为8.787s(因为手速原因存在误差,实际应该为10秒左右)就能说明这一点。这样对同一数据的修改会变成串行化的修改,所以不会出现因为并发只进行一次+1的情况,也就不会出现丢失修改的问题。

[READ UNCOMMITTED]不能解决的问题

读未提交,顾名思义,一个事务可以读到另一个事务没有提交的内容,如果另一个事务进行回滚就会产生脏读。
我们来模拟一下脏读:
事务1

START TRANSACTION;
① UPDATE users SET state=1 WHERE id=1;
② SELECT sleep(5);
ROLLBACK;

事务2

START TRANSACTION;
① SELECT * FROM users WHERE id=1;
COMMIT;

事务1先于事务2执行。
事务1的执行信息

[SQL 1]START TRANSACTION;
受影响的行: 0
时间: 0.000s

[SQL 2]
UPDATE users SET state=1 WHERE id=1;
受影响的行: 1
时间: 0.002s

[SQL 3]
SELECT sleep(5);
受影响的行: 0
时间: 5.000s

[SQL 4]
ROLLBACK;
受影响的行: 0
时间: 0.067s

事务2的执行信息

[SQL 1]START TRANSACTION;
受影响的行: 0
时间: 0.001s

[SQL 2]
SELECT * FROM users WHERE id=1;
受影响的行: 0
时间: 0.001s

[SQL 3]
COMMIT;
受影响的行: 0
时间: 0.000s

事务2的执行结果:
mark
事务1和事务2都执行结束时,再进行一次查询的结果:
mark
结论:读未提交[READ UNCOMMITTED]隔离级别解决不了脏读的问题,更解决不了不可重复读的问题。

分析:因为读未提交[READ UNCOMMITTED]隔离级别对应数据库的一级封锁协议。一级封锁协议在修改数据之前对其加X锁,直到事务结束释放X锁。读数据不加锁。因为事务1先执行修改,修改前申请持有X锁,持锁时间段为[SQL 2]开始前到[SQL 4]结束,持锁时间大约为5.069s。事务2执行读操作,不需要申请持锁,而是直接去磁盘读取数据。读取出的数据是事务1修改后的,而此时事务1回滚,修改的数据被还原,就产生了脏读现象。


[SERIALIZABLE]

这个级别的封锁就很好理解了,读加共享锁,写加排他锁,读写互斥。使用的悲观锁的理论,所有操作串行化执行,数据更加安全,但是并发能力非常差。如果你的业务并发的特别少或者没有并发,同时又要求数据及时可靠的话,可以使用这种模式。

我们重点探究的不是[READ UNCOMMITTED]和[SERIALIZABLE]级别,而是[READ COMMITTED]和[REPEATABLE READ]。如果您对此感兴趣可以看一下后两篇博客。


本文为博主学习感悟总结,水平有限,如果不当,欢迎指正。

如果您认为还不错,不妨点击一下下方的【推荐】按钮,谢谢支持。

转载与引用请注明出处。

转载于:https://www.cnblogs.com/songwenjie/p/8644599.html

相关文章:

  • 最长递增子序列
  • 清华产业十大创新项目评选 新华三H3Cloud OS夺冠
  • 超轻量模板引擎
  • 异构计算助力客户春节webp图片编码
  • Python练习实例100例(持续更新中)
  • 技术往事:微信估值已超5千亿,雷军曾有机会收编张小龙及其Foxmail
  • 论flex布局和box布局的华为meta8手机自带浏览器的兼容
  • ajaxfileupload-上传文件示例
  • 4027. [HEOI2015]兔子与樱花【树形DP】
  • 1491. [NOI2007]社交网络【最短路计数】
  • Java基础-比较运算符Compare Operators
  • LDAP DIT设计参考
  • 爬取校园新闻首页的新闻
  • 学习索引结构的一些案例——Jeff Dean在SystemML会议上发布的论文
  • node爬虫-使用puppeteer
  • 《Javascript数据结构和算法》笔记-「字典和散列表」
  • 【407天】跃迁之路——程序员高效学习方法论探索系列(实验阶段164-2018.03.19)...
  • ➹使用webpack配置多页面应用(MPA)
  • 30秒的PHP代码片段(1)数组 - Array
  • Android 控件背景颜色处理
  • AWS实战 - 利用IAM对S3做访问控制
  • css选择器
  • Intervention/image 图片处理扩展包的安装和使用
  • javascript 哈希表
  • nodejs:开发并发布一个nodejs包
  • webpack项目中使用grunt监听文件变动自动打包编译
  • Yii源码解读-服务定位器(Service Locator)
  • 基于OpenResty的Lua Web框架lor0.0.2预览版发布
  • 力扣(LeetCode)965
  • 每天10道Java面试题,跟我走,offer有!
  • 前端路由实现-history
  •  一套莫尔斯电报听写、翻译系统
  • 译米田引理
  • 正则表达式
  • 智能网联汽车信息安全
  • ​DB-Engines 12月数据库排名: PostgreSQL有望获得「2020年度数据库」荣誉?
  • ​虚拟化系列介绍(十)
  • ###51单片机学习(2)-----如何通过C语言运用延时函数设计LED流水灯
  • #android不同版本废弃api,新api。
  • $.ajax()方法详解
  • $refs 、$nextTic、动态组件、name的使用
  • (3)选择元素——(17)练习(Exercises)
  • (java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序】超详细~~
  • (k8s中)docker netty OOM问题记录
  • (pojstep1.1.1)poj 1298(直叙式模拟)
  • (定时器/计数器)中断系统(详解与使用)
  • (附源码)spring boot校园健康监测管理系统 毕业设计 151047
  • (黑马C++)L06 重载与继承
  • (切换多语言)vantUI+vue-i18n进行国际化配置及新增没有的语言包
  • (十一)c52学习之旅-动态数码管
  • ****** 二 ******、软设笔记【数据结构】-KMP算法、树、二叉树
  • ****Linux下Mysql的安装和配置
  • .\OBJ\test1.axf: Error: L6230W: Ignoring --entry command. Cannot find argumen 'Reset_Handler'
  • .axf 转化 .bin文件 的方法
  • .mysql secret在哪_MySQL如何使用索引