5分钟带你读懂事务隔离性与隔离级别

前言

我们在上一章节中介绍过数据库的带你了解数据库中事务的ACID特性的相关用法。本章节主要来介绍下数据库中一个非常重要的知识点事务的隔离级别。如有错误还请大家及时指出~

问题:

  • 事务的隔离级别有哪些?
  • 如果并发事务没有进行隔离,会出现什么问题?

以下都是采用mysql数据库

在多个事务并发做数据库操作的时候,如果没有有效的避免机制,就会出现种种问题。大体上有以下问题:

一、引发的问题

在并发事务没有进行隔离的情况下,会发生如下问题。

问题一:脏读

脏读指一个事务读取了另外一个事务未提交的数据。

具体看后文案例介绍

问题二:不可重复读

不可重复读指在一个事务内读取表中的某一行数据,多次读取结果不同。

不可重复读和脏读的区别是,脏读是读取前一事务未提交的脏数据,不可重复读是重新读取了前一事务已提交的数据。

具体看后文案例介绍

问题三:幻读(虚读)

幻读(虚读)指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致。

具体看后文案例介绍

二、概念

2.1 事务的隔离级别分为:

  • Read uncommitted(读未提交)
  • Read Committed(读已提交)
  • Repeatable Reads(可重复读)
  • Serializable(串行化)

Read uncommitted

读未提交:隔离级别最低的一种事务级别。在这种隔离级别下,会引发脏读、不可重复读和幻读。

Read Committed

读已提交读到的都是别人提交后的值。这种隔离级别下,会引发不可重复读和幻读,但避免了脏读。

Repeatable Reads

可重复读这种隔离级别下,会引发幻读,但避免了脏读、不可重复读。

Serializable

串行化是最严格的隔离级别。在Serializable隔离级别下,所有事务按照次序依次执行。脏读、不可重复读、幻读都不会出现。

三、操作

3.1 查看事务隔离级别

SHOW VARIABLES LIKE ‘tx_isolation‘;

查看全局的事务隔离级别

SHOW GLOBAL VARIABLES LIKE ‘tx_isolation‘;

使用系统变量查询

SELECT @@global.tx_isolation;

SELECT @@session.tx_isolation;

SELECT @@tx_isolation;

3.2 设置MysQL的事务隔离级别

语法

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

GLOBAL:设置全局的事务隔离级别

SESSION:设置当前session的事务隔离级别,如果语句没有指定GLOBAL或SESSION,默认值为SESSION

使用系统变量设置事务隔离级别

SET GLOBAL tx_isolation=‘REPEATABLE-READ‘;

SET SESSION tx_isolation=‘SERIALIZABLE‘;

四、案例分析

下面实际操作中使用到的一些并发控制语句,可看上面的操作介绍

作为演示:product表

productId productName productPrice productCount
1 xiaomi 1999 100

带着上面的我们来看一下,事务在没有隔离性的情况下,会引发哪些问题?

同时打开两个窗口模拟2个用户并发访问数据库

4.1 事务隔离级别设置为read uncommitted

查询事务隔离级别

SELECT @@tx_isolation;

设置隔离级别为未提交读

SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

注意:需要同时修改两个窗口的事务隔离级别

以下我们以两位用户抢小米手机为例

时间轴 事务A 事务B
T1 start transaction;
T2 select p.productName,p.productCount from product p where p.productId=1;(productCount =100)
T3 start transaction;
T4 select p.productName,p.productCount from product p where p.productId=1;(productCount =100)
T5 update product set productCount = 99 where productId = 1;
T6 select p.productName,p.productCount from product p where p.productId=1;(productCount =99)
T7 ROLLBACK;
T8 select p.productName,p.productCount from product p where p.productId=1;(productCount =100)


T1—— A用户开启事务,start transaction;

T2—— A用户查询当前小米手机剩余数量,select p.productName,p.productCount from product p where p.productId=1;此时数量显示为100。

T3——B用户开启事务,start transaction;

T4——B用户查询当前小米手机剩余数量,select p.productName,p.productCount from product p where p.productId=1;此时数量显示为100。

T5—— B用户购买了一台小米手机,update product set productCount = 99 where productId = 1; 此时只修改数据并未提交事务。

T6—— A用户刷新页面,select p.productName,p.productCount from product p where p.productId=1;此时数量显示为99。

T7—— B用户购买失败,回滚事务。

T8—— A用户查询当前小米手机剩余数量,select p.productName,p.productCount from product p where p.productId=1;此时数量显示为100。


小结:

事务A读取了未提交的数据,事务B的回滚,导致了事务A的数据不一致,导致了事务A的脏读

4.2 事务隔离级别设置为Read Committed

查询事务隔离级别

SELECT @@tx_isolation;

更改数据库隔离级别,设置隔离级别为提交读

SET SESSION  TRANSACTION ISOLATION LEVEL READ COMMITTED;

注意:需要同时修改两个窗口的事务隔离级别

时间轴 事务A 事务B
T1 start transaction;
T2 select p.productName,p.productCount from product p where p.productId=1;(productCount =100)
T3 start transaction;
T4 select p.productName,p.productCount from product p where p.productId=1;(productCount =100)
T5 update product set productCount = 99 where productId = 1;
T7 select p.productName,p.productCount from product p where p.productId=1;(productCount =100)
T6 commit;
T8 select p.productName,p.productCount from product p where p.productId=1;(productCount =99)


这里就不再对流程做过多赘述。

小结:

可以看到避免了脏读现象,但是却出现了,一个事务还没有结束,就发生了不可重复读问题,即事务A来说 productCount从 100->100->99。但这个过程中事务并未提交结束。

4.3 事务隔离级别设置为Repeatable Read(mysql默认级别)

查询事务隔离级别

SELECT @@tx_isolation;

更改数据库隔离级别,设置隔离级别为可重复读

SET SESSION  TRANSACTION ISOLATION LEVEL REPEATABLE READ;

注意:需要同时修改两个窗口的事务隔离级别

时间轴 事务A 事务B
T1 start transaction;
T2 select p.productName,p.productCount from product p where p.productId=1;(productCount =100)
T3 start transaction;
T4 select p.productName,p.productCount from product p where p.productId=1;(productCount =100)
T5 update product set productCount = 99 where productId = 1;
T7 select p.productName,p.productCount from product p where p.productId=1;(productCount =100)
T6 commit;
T8 select p.productName,p.productCount from product p where p.productId=1;(productCount =100)


这里就不再对流程做过多赘述。

小结:

可以看到可重复读隔离级别避免了脏读不可重复读的问题,但是出现了幻读现象。事务A查询到的小米数量等于100,但是事务B修改了数量为99,但是事务A读取到的值还是100。当事务A去减1等于99时,是错误的,此时应该是99-1=98才对。接下来我们再提高一个事务隔离级别。

4.4 事务隔离级别设置为Serializable

查询事务隔离级别

SELECT @@tx_isolation;

更改数据库隔离级别,设置隔离级别为串行化

SET SESSION  TRANSACTION ISOLATION LEVEL REPEATABLE READ;
时间轴 事务A 事务B
--- --- ---
T1 start transaction;
T2 start transaction;
T2 select p.productName,p.productCount from product p where p.productId=1;(productCount =100);
T4 update product set productCount = 99 where productId = 1;(等待中..)


这里就不再对流程做过多赘述。

小结:

在我们Serializable隔离级别中,我们可以看到事务B去做修改动作时卡主了,不能向下执行。这是因为:给事务A的select操作上了锁,所以事务B去修改值的话,就会被卡主。只有当事务A操作执行完毕,才会执行事务B的操作。这样就避免了上述三个问题了。

问题本身

  • 回到问题的本身,其实我们并不需要将事务提到这么高。
  • 问题的本身就是,当我们读完了的时候,就要在上面加锁。我们不希望别人能够去读它。因为别人读到了count,就会修改count的值,并写进去。所以我们在select 操作的时候,加上for update。这时候就会把这行操作给锁掉了。那么另外一个人也进行相同的操作,也表示select 出来的count需要进行update,需要锁住。
select p.productName,p.productCount from product p where p.productId=1 for update;

PS: 在实际开发过程中,这样的加锁行为,是非常的耗系统性能的。下一章节我们将来介绍悲观锁与乐观锁

文末

本章节主要介绍了数据库中事务的ADID特性中的隔离性,在没有隔离的情况下会发生什么问题,相信大家通过本章,对数据库事务中的隔离性有了一定的了解,下篇文章我们将介绍数据库中的悲观锁与乐观锁

欢迎关注公众号:Coder编程

获取最新原创技术文章和相关免费学习资料,随时随地学习技术知识!

推荐阅读

带你了解数据库中JOIN的用法

带你了解数据库中group by的用法

带你了解数据库中事务的ACID特性

Github个人主页目录

Gitee个人主页目录

欢迎大家关注并Star~

原文地址:https://www.cnblogs.com/coder-programming/p/10693473.html

时间: 2024-09-29 23:39:16

5分钟带你读懂事务隔离性与隔离级别的相关文章

少啰嗦!一分钟带你读懂Java的NIO和经典IO的区别

1.引言 很多初涉网络编程的程序员,在研究Java NIO(即异步IO)和经典IO(也就是常说的阻塞式IO)的API时,很快就会发现一个问题:我什么时候应该使用经典IO,什么时候应该使用NIO? 在本文中,将尝试用简明扼要的文字,阐明Java NIO和经典IO之间的差异.典型用例,以及这些差异如何影响我们的网络编程或数据传输代码的设计和实现的. 本文没有复杂理论,也没有像网上基它文章一样千篇一律的复制粘贴,有的只是接地气的通俗易懂,希望能给你带来帮助. (本文同步发布于:http://www.5

顾嘉:5分钟带你读懂《在线》逻辑

互联网成了基础设施,是创新平台:数据成了生产资料,是自然资源 :计算成了公共服务,是能源动力. ——王坚:<在线> 在我的记忆里,云计算.物联网.大数据是近年来被媒体和产业界炒得最热的几个概念了.然而时至今日,尽管有一大批创业公司以大无畏的勇气踏上了探索前路的征途,我们依然很少在现实工作和生活中真切地感受到这几个新技术带来的变化. 除了少部分出类拔萃的企业,大部分产品依旧停留在发布会的PPT中,还没来得及面世就被资本的力量洗刷殆尽.这背后自然有“物竞天择”的无常,也有市场竞争的残酷,但从主观上

全篇干货,10分钟带你读透《参与感》

全篇干货,10分钟带你读透<参与感> 2015/11/25 阅读(3164) 评论(4) 收藏(27) 加入人人都是产品经理[起点学院]产品经理实战训练营,BAT产品总监手把手带你学产品点此查看详情! 解密互联网思维 ① 雷军眼中创业成功的三大关键因素: 选个大市场.组建最优秀的团队.拿到花不完的钱.团队第一,产品第二! ② 互联网的七字诀:专注.极致.口碑.快 专注和极致是产品目标,快是行动准则,口碑则是整个互联网思维的核心,因为用户主要是以口碑来选择产品的. ③ 互联网带来的变化包括: 其

分分钟带你读懂 ButterKnife 的源码

为什么要写这一系列的博客呢? 因为在 Android 开发的过程中, 泛型,反射,注解这些知识进场会用到,几乎所有的框架至少都会用到上面的一两种知识,如 Gson 就用到泛型,反射,注解,Retrofit 也用到泛型,反射,注解 .学好这些知识对我们进阶非常重要,尤其是阅读开源框架源码或者自己开发开源框架. 前言 ButterKnife 这个开源库火了有一段时间了,刚开始它的实现原理是使用反射实现的,性能较差.再后面的 版本中逐渐使用注解+放射实现,性能提高了不少. ButterKnife是基于

Nginx系列教程(三)| 一文带你读懂 Nginx 的负载均衡

作者:JackTian 微信公众号:杰哥的IT之旅(ID:Jake_Internet) LAMP 系列导读 01. LAMP 系列教程(一)| 详解 Linux 环境下部署 HTTPD 服务 02. LAMP 系列教程(二)| 如何在 Linux 环境下部署 AWStats 分析系统来监控 Web 站点? 03. LAMP 系列教程(三)| 一文读懂 HTTPD 服务的访问控制 04. LAMP 系列教程(四)| MySQL 数据库系统(一) 05. LAMP 系列教程(五)| MySQL 数据

【 全干货 】5 分钟带你看懂 Docker !

欢迎大家前往腾讯云社区,获取更多腾讯海量技术实践干货哦~ 作者丨唐文广:腾讯工程师,负责无线研发部地图测试. 导语:Docker,近两年才流行起来的超轻量级虚拟机,它可以让你轻松完成持续集成.自动交付.自动部署,并且实现开发环境.测试环境.运维环境三方环境的真正同步.本文从Docker定义,作用,技术架构,安装和使用等全方位带你看懂Docker. Docker是啥? 打开翻译君输入Docker 结果显示码头工人,没错!码头工人搬运的是集装箱,那么今天要讲的Docker其操作的也是集装箱,这个集装

从源码入手,一文带你读懂Spring AOP面向切面编程

之前<零基础带你看Spring源码--IOC控制反转>详细讲了Spring容器的初始化和加载的原理,后面<你真的完全了解Java动态代理吗?看这篇就够了>介绍了下JDK的动态代理. 基于这两者的实现上,这次来探索下Spring的AOP原理.虽然AOP是基于Spring容器和动态代理,但不了解这两者原理也丝毫不影响理解AOP的原理实现,因为大家起码都会用. AOP,Aspect Oriented Programming,面向切面编程.在很多时候我们写一些功能的时候,不需要用到继承这么

Nginx系列教程(二)| 一文带你读懂Nginx的正向与反向代理

作者:JackTian 微信公众号:杰哥的IT之旅(ID:Jake_Internet) LAMP 系列导读 01. LAMP 系列教程(一)| 详解 Linux 环境下部署 HTTPD 服务 02. LAMP 系列教程(二)| 如何在 Linux 环境下部署 AWStats 分析系统来监控 Web 站点? 03. LAMP 系列教程(三)| 一文读懂 HTTPD 服务的访问控制 04. LAMP 系列教程(四)| MySQL 数据库系统(一) 05. LAMP 系列教程(五)| MySQL 数据

spring事务传播性与隔离级别

事务的7种传播级别: 1)PROPAGATION_REQUIRED:支持当前事务,没有事务就新建一个. 2)PROPAGATION_SUPPORTS:支持当前事务,如果没有事务,以非事务方式处理 3)PROPAGATION_MANDATORY:支持当前事务,没有事务就抛异常 4)PROPAGATION_REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起 5)PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,有事务则挂起 6)PROPAGATION_NEV