首先,我们先设置MySQL事务隔离级别为READ-UNCOMMITTED

  1. 在my.ini配置文件最后加上如下配置
#可选参数有:READ-UNCOMMITTED, READ-COMMITTED, REPEATABLE-READ, SERIALIZABLE.
[mysqld]
transaction-isolation = READ-UNCOMMITTED
  1. 重启MySQL服务

1、脏读

提出问题
同一个应用程序中的多个事务或不同应用程序中的多个事务在同一个数据集上并发执行时, 可能会出现许多意外的问题。
例如: 已知有两个事务A和B, B读取了已经被A更新但还没有被提交的数据,之后,A回滚事务,B读取的数据就是脏数据。
场景:
Tom的账户money=0,公司发工资把5000元打到Tom的账户上,Tom的money=money+5000元,但是该事务并未提交,而Tom正好去查看账户,发现工资已经到账,账户money=5000元,非常高兴,可是不幸的是,公司发现发给Tom的工资金额不对,应该是2000元,于是迅速回滚了事务,修改金额后,将事务提交,Tom再次查看账户时发现账户money=2000元,Tom空欢喜一场,从此郁郁寡欢,走上了不归路……
事务流程如下:

事务A(代表公司) 事务B(代表Tom)
read(money);
money=money+5000;
write(money)
read(money);(money=5000)
rollback;(money=0)
money=money+2000
submit ;
read(money);(money=2000)

分析:上述情况即为脏读,两个并发的事务:“事务A:公司给Tom发工资”、“事务B:Tom查询工资账户”,事务B读取了事务A尚未提交的数据。
实验
我们在java代码中观察这种情况:

public class Boss {//公司给Tom发工资public static void main(String[] args) {Connection connection = null;Statement statement = null;try {Class.forName("com.mysql.jdbc.Driver");String url = "jdbc:mysql://127.0.0.1:3306/test";connection = DriverManager.getConnection(url, "root", "root");connection.setAutoCommit(false);statement = connection.createStatement();String sql = "update account set money=money+5000 where card_id='6226090219290000'";statement.executeUpdate(sql);Thread.sleep(10000);//10秒后发现工资发错了connection.rollback();sql = "update account set money=money+2000 where card_id='6226090219290000'";statement.executeUpdate(sql);connection.commit();} catch (Exception e) {e.printStackTrace();} finally {//释放资源}}
}
public class Employee {//Tom查询余额public static void main(String[] args) {Connection connection = null;Statement statement = null;ResultSet resultSet = null;try {Class.forName("com.mysql.jdbc.Driver");String url = "jdbc:mysql://127.0.0.1:3306/test";connection = DriverManager.getConnection(url, "root", "root");statement = connection.createStatement();String sql = "select balance from account where card_id='6226090219290000'";resultSet = statement.executeQuery(sql);if(resultSet.next()) {System.out.println(resultSet.getDouble("balance"));}} catch (Exception e) {e.printStackTrace();} finally {//释放资源}}
}

在执行Boss中main方法后立即执行Employee中的main方法得:

在执行Boss中main方法后等待10秒,执行Employee中的main方法得:

得出结论
事务隔离级别为Read uncommitted(读未提交)时会出现“脏读”

2、不可重复读

提出问题
场景:Tom拿着工资卡去消费,酒足饭饱后在收银台买单,服务员告诉他本次消费1000元,Tom将银行卡给服务员,服务员将银行卡插入POS机,POS机读到卡里余额为3000元,就在Tom磨磨蹭蹭输入密码时,他老婆以迅雷不及掩耳盗铃之势把Tom工资卡的3000元转到自己账户并提交了事务,当Tom输完密码并点击“确认”按钮后,POS机检查到Tom的工资卡已经没有钱,扣款失败,Tom十分纳闷,明明卡里有钱,于是怀疑POS有鬼,和收银小姐姐大打出手,300回合之后终因伤势过重而与世长辞,Tom老婆痛不欲生,郁郁寡欢,从此走上了不归路…
事务流程如下:

事务A(代表POS机) 事务B(代表老婆)
read(money);
输入密码 read(money);
money=money-3000;(转账)
write(money);submit ;
read(money);
余额不足!

分析:上述情况即为不可重复读,两个并发的事务,“事务A:POS机扣款”、“事务B:Tom的老婆网上转账”,事务A事先读取了数据,事务B紧接了更新数据并提交了事务,而事务A再次读取该数据扣款时,数据已经发生了改变。
实验
我们在java代码中观察这种情况:

public class Machine {//POS机扣款public static void main(String[] args) {Connection connection = null;Statement statement = null;ResultSet resultSet = null;try {double sum=1000;//消费金额Class.forName("com.mysql.jdbc.Driver");String url = "jdbc:mysql://127.0.0.1:3306/test";connection = DriverManager.getConnection(url, "root", "root");connection.setAutoCommit(false);statement = connection.createStatement();String sql = "select money from account where card_id='6226090219290000'";resultSet = statement.executeQuery(sql);if(resultSet.next()) {System.out.println("余额:"+resultSet.getDouble("money"));}System.out.println("请输入支付密码:");Thread.sleep(10000);//10秒后密码输入成功resultSet = statement.executeQuery(sql);if(resultSet.next()) {double money = resultSet.getDouble("money");System.out.println("余额:"+money);if(money<sum) {System.out.println("余额不足,扣款失败!");return;}}sql = "update account set money=money-"+sum+" where card_id='6226090219290000'";statement.executeUpdate(sql);connection.commit();System.out.println("扣款成功!");} catch (Exception e) {e.printStackTrace();} finally {//释放资源}}
}
public class Wife {//Tom的老婆网上转账public static void main(String[] args) {Connection connection = null;Statement statement = null;try {double money=3000;//转账金额Class.forName("com.mysql.jdbc.Driver");String url = "jdbc:mysql://127.0.0.1:3306/test";connection = DriverManager.getConnection(url, "root", "root");connection.setAutoCommit(false);statement = connection.createStatement();String sql = "update account set money=money-"+money+" where card_id='6226090219290000'";statement.executeUpdate(sql);sql = "update account set money=money+"+money+" where card_id='6226090219299999'";statement.executeUpdate(sql);connection.commit();System.out.println("转账成功");} catch (Exception e) {e.printStackTrace();} finally {//释放资源}}
}

在执行Machine中main方法后立即执行Wife中的main方法得:

等待10秒后Machine中main方法执行结果:

得出结论
事务隔离级别为Read uncommitted(读未提交)时会出现“不可重复读”

3、幻读

幻读(Phantom Read): 已知有两个事务A和B,A从一个表中读取了数据,然后B在该表中插入了一些新数据,导致A再次读取同一个表, 就会多出几行。
提出问题
场景:Tom的老婆工作在银行部门,她时常通过银行内部系统查看Tom的工资卡消费记录。2019年5月的某一天,她查询到Tom当月工资卡的总消费额为80元,Tom的老婆非常吃惊,心想“老公真是太节俭了,嫁给他真好!”,而Tom此时正好在外面胡吃海塞后在收银台买单,消费1000元,即新增了一条1000元的消费记录并提交了事务,沉浸在幸福中的老婆查询了Tom当月工资卡消费明细一探究竟,可查出的结果竟然发现有一笔1000元的消费,Tom的老婆瞬间怒气冲天,外卖订购了一个大号的榴莲,傍晚降临,Tom生活在了水深火热之中,只感到膝盖针扎的痛…
事务流程如下:

事务A(代表老婆) 事务B(代表Tom消费)
read(消费记录);
消费金额80元 read(money);
money=money-1000;(消费)
write(money);submit ;
read(消费记录);
消费金额1080元

分析:上述情况即为幻读,两个并发的事务,“事务A:获取事务B消费记录”、“事务B:添加了新的消费记录”,事务A获取事务B消费记录时数据多出了一条。
实验
我们在java代码中观察这种情况:

public class Bank {//老婆查看消费记录public static void main(String[] args) {Connection connection = null;Statement statement = null;ResultSet resultSet = null;try {Class.forName("com.mysql.jdbc.Driver");String url = "jdbc:mysql://127.0.0.1:3306/test";connection = DriverManager.getConnection(url, "root", "root");connection.setAutoCommit(false);statement = connection.createStatement();String sql = "select sum(amount) total from record where card_id='6226090219290000' and date_format(create_time,'%Y-%m')='2019-05'";resultSet = statement.executeQuery(sql);if(resultSet.next()) {System.out.println("总额:"+resultSet.getDouble("total"));}Thread.sleep(10000);//30秒后查询2019年5月消费明细sql="select amount from record where card_id='6226090219290000' and date_format(create_time,'%Y-%m')='2019-05'";resultSet = statement.executeQuery(sql);System.out.println("消费明细:");while(resultSet.next()) {double amount = resultSet.getDouble("amount");System.out.println(amount);}connection.commit();} catch (Exception e) {e.printStackTrace();} finally {//释放资源}}
}
public class Husband {//Tom消费1000元public static void main(String[] args) {Connection connection = null;Statement statement = null;try {double sum=1000;//消费金额Class.forName("com.mysql.jdbc.Driver");String url = "jdbc:mysql://127.0.0.1:3306/test";connection = DriverManager.getConnection(url, "root", "root");connection.setAutoCommit(false);statement = connection.createStatement();String sql = "update account set money=money-"+sum+" where card_id='6226090219290000'";statement.executeUpdate(sql);sql = "insert into record (id,card_id,amount,create_time) values (3,'6226090219290000',"+sum+",'2019-05-19');";statement.executeUpdate(sql);connection.commit();} catch (Exception e) {e.printStackTrace();} finally {//释放资源}}
}

在执行Bank中main方法后立即执行Wife中的Husband方法得:

等待10秒后控制台输出:
得出结论
事务隔离级别为Read uncommitted(读未提交)时会出现“幻读”

所用表

create table account(id int(36) primary key comment '主键',card_id varchar(16) unique comment '卡号',name varchar(8) not null comment '姓名',money float(10,2) default 0 comment '余额'
)engine=innodb;
insert into account (id,card_id,name,money) values (1,'6226090219290000','Tom',3000);create table record(id int(36) primary key comment '主键',card_id varchar(16) comment '卡号',amount float(10,2) comment '金额',create_time date comment '消费时间'
)engine=innodb;
insert into record (id,card_id,amount,create_time) values (1,'6226090219290000',37,'2019-05-01');
insert into record (id,card_id,amount,create_time) values (2,'6226090219290000',43,'2019-05-07');

事务隔离级别——READ-UNCOMMITTED(读未提交)相关推荐

  1. 数据库基础知识点-事务隔离级别区分(读未提交、读已提交和可重复读)

    事务隔离级别 数据库事务隔离级别分4个: 读未提交-Read uncommitted 读已提交-Read committed 可重复读-Repeatable read–MySQL 序列化-Serial ...

  2. 事务隔离级别脏读幻读_脏读和未提交读隔离级别

    事务隔离级别脏读幻读 In this article, we will discuss the Dirty Read concurrency issue and also learn the deta ...

  3. MySQL数据库中,在读已提交和可重复读这两个不同事务隔离级别下幻读的区别

    目 录 1. 前 言 1.1 并发事务存在的问题 1.2 事务的隔离级别 1.3 快照读和当前读 2. 不同事务隔离级别下幻读的区别 2.1 读已提交下的幻读 2.2 可重复读下的幻读 2.2.1 情 ...

  4. MySQL数据库——事务隔离级别Read uncommitted(一)

    文章目录 一.设置事务隔离级别 二.事务问题 1.脏读 (1) 场景 (2) 分析 (3) 模拟演示 2.不可重复读 (1)场景 (2)分析 (对比)(1*) 场景 (2*)分析 (3)模拟演示 3. ...

  5. 事务隔离级别——Read uncommitted

    根据实际需求,通过设置数据库的事务隔离级别可以解决多个事务并发情况下出现的脏读.不可重复读和幻读问题,数据库事务隔离级别由低到高依次为Read uncommitted.Read committed.R ...

  6. 5、MySQL事务隔离级别详解

    事务的隔离性就是指当多个事务同时运行时,各事务之间相互隔离,不可互相干扰.如果事务没有隔离性,就容易出现脏读.不可重复读和幻读等情况. 为了保证并发时操作数据的正确性,数据库都会有事务隔离级别的概念. ...

  7. mysql 126_MySQL教程126-MySQL事务隔离级别

    在 数据库事务的概念和特性 中介绍了 MySQL 事务的四大特性,其中事务的隔离性就是指当多个事务同时运行时,各事务之间相互隔离,不可互相干扰.如果事务没有隔离性,就容易出现脏读.不可重复读和幻读等情 ...

  8. MySQL锁、事务隔离级别、MVCC机制详解、间隙锁、死锁等

    一. 简介 1. 锁定义 锁是计算机协调多个进程或线程并发访问某一资源的机制. 在数据库中,除了传统的计算资源(如CPU.RAM.I/O等)的争用以外,数据也是一种供需要用户共享的资源.如何保证数据并 ...

  9. mysql 事务补偿_TCC补偿性策略_彻底学习数据库事务 seata分布式事务 共享 排它锁 死锁 索引 Spring事务 隔离级别等_MySQL视频-51CTO学院...

    课程总计41课时,从什么是事务讲起,直到分布式事务解决方案,很的0基础基础与提升系列课程.对于难以理解的知识点,全部用画图+实战的方式讲解. 彻底明白事务的四个特性:原子性.一致性.隔离性.持久性,用 ...

最新文章

  1. 调用wordcloud种种问题
  2. 小程序promise封装post请求_微信小程序promise封装get/post请求
  3. java修改动态视频,直播视频app源码,动态修改cron
  4. 通过改善架构来提高 ASP.Net 应用程序的性能
  5. linux同一目录文件无法执行,linux – 无法在特定目录中执行文件
  6. oracle 表(下)
  7. MySQL查询日期类数据常用函数
  8. 安卓飞机大战(六) 动态Gif图的添加
  9. Jersey实现Restful服务
  10. javaUDP逐步实现多线程发送和接收消息
  11. Sensor信号输出YUV、RGB、RAW DATA、JPEG 4种方式区别---转
  12. 记一次PS For Mac破解
  13. 计算机网络——SMTP
  14. 计算机毕业设计Java银行贷款管理系统(系统+程序+mysql数据库+Lw文档)
  15. excel自定义格式分钟计时_巧用EXCEL制作计时器
  16. Power Query 自学教程
  17. 一种获取NLP语料的基本方法
  18. 【腾讯云的1001种玩法】几种在腾讯云建立WordPress的方法(Linux)(二)
  19. 通信原理樊昌信第七版_2019年南京邮电大学通信工程考研成功经验分享
  20. GSEA文件准备及表达相关性分析(R语言)

热门文章

  1. 挑战自我的1000+篇文章总结
  2. windows安装Twisted失败
  3. “神仙打架”的预制菜赛道,国联水产携手盒马能否“C”位出道?
  4. 国际大公司到国内大公司的思考
  5. Process.GetCurrentProcess().Threads 集合中找到当前的processThread
  6. 「面试」Go中Slice的Copy问题
  7. CSS3利用animation动画实现多个图标在指定间隔后再次动作
  8. derby数据库基本语法
  9. 【电脑使用】电脑之间文件无线互传
  10. Python Django使用websocket channels3.0.3