Mysql 系列文章主页

===============

刚开始学习 Mysql 锁的时候,觉得 Mysql 使用的是行锁,再加上其默认的可重复读的隔离级别,那就应该能够自动解决并发事务更新的问题。可事实上,并不能解决!

可是,为什么不能解决呢?

带着问题,自己写了个简单Demo,来测试并分析其中道理:

Demo 代码路径:https://github.com/cyhbyw/cyh_Spring_IsolationConcurrencyTransaction

Demo 工程名称:usingMybatis

0 准备工作

工程中所有文件如下图所示;

代码运行前,注意设置数据库的用户名和密码,同时,需要运行 test.sql 文件中的脚本用以初始化数据;

1 测试

1.1 业务代码

     @Transactional(rollbackFor = RuntimeException.class)
public void increaseMoney(Integer id) {
Employee employee = employeeMapper.findById(id);
final Integer oldMoney = employee.getMoney();
LOGGER.info("oldMoney: {}", oldMoney);
employee.setMoney(oldMoney + 1);
employeeMapper.updateEmployee(employee);
}

代码说明:

  • 第1行,添加事务
  • 第3行,取出数据
  • 第4,5行,取出旧的Money值并记录日志
  • 第6行,为 Employee 对象设置新的值(就是+1操作)
  • 第7行,执行Sql更新

1.2 测试代码

     private void increaseMoney() {
int threadCount = 100;
while (threadCount-- > 0) {
new Thread(() -> employeeService.increaseMoney(1)).start();
}
}

代码说明:

  • 启了 100 根线程去调用业务方法
  • 每个线程将 Money+1,理论上,最后的Money=100(初始值为0)

1.3 测试结果

预期结果:100,实际结果:22

提示:22这个值不固定(因为是多线程访问嘛,而线程的执行是由CPU决定的,具有一定波动性)

1.4 程序日志

 2018-04-25 11:32:05,416  INFO [Thread-83] (EmployeeService.java:27) - oldMoney: 0
2018-04-25 11:32:05,416 INFO [Thread-18] (EmployeeService.java:27) - oldMoney: 0
2018-04-25 11:32:05,416 INFO [Thread-19] (EmployeeService.java:27) - oldMoney: 0
2018-04-25 11:32:05,416 INFO [Thread-54] (EmployeeService.java:27) - oldMoney: 0
2018-04-25 11:32:05,416 INFO [Thread-15] (EmployeeService.java:27) - oldMoney: 0
2018-04-25 11:32:05,416 INFO [Thread-52] (EmployeeService.java:27) - oldMoney: 0
2018-04-25 11:32:05,416 INFO [Thread-17] (EmployeeService.java:27) - oldMoney: 0
2018-04-25 11:32:05,416 INFO [Thread-55] (EmployeeService.java:27) - oldMoney: 0
2018-04-25 11:32:05,416 INFO [Thread-53] (EmployeeService.java:27) - oldMoney: 0
2018-04-25 11:32:05,416 INFO [Thread-20] (EmployeeService.java:27) - oldMoney: 0
2018-04-25 11:32:05,416 INFO [Thread-43] (EmployeeService.java:27) - oldMoney: 0
2018-04-25 11:32:05,416 INFO [Thread-2] (EmployeeService.java:27) - oldMoney: 0
2018-04-25 11:32:05,416 INFO [Thread-56] (EmployeeService.java:27) - oldMoney: 0
2018-04-25 11:32:05,416 INFO [Thread-8] (EmployeeService.java:27) - oldMoney: 0
2018-04-25 11:32:05,416 INFO [Thread-16] (EmployeeService.java:27) - oldMoney: 0
2018-04-25 11:32:05,462 INFO [Thread-14] (EmployeeService.java:27) - oldMoney: 1
2018-04-25 11:32:05,527 INFO [Thread-27] (EmployeeService.java:27) - oldMoney: 2
2018-04-25 11:32:05,534 INFO [Thread-26] (EmployeeService.java:27) - oldMoney: 2
2018-04-25 11:32:05,534 INFO [Thread-22] (EmployeeService.java:27) - oldMoney: 2
2018-04-25 11:32:05,535 INFO [Thread-23] (EmployeeService.java:27) - oldMoney: 2
2018-04-25 11:32:05,534 INFO [Thread-24] (EmployeeService.java:27) - oldMoney: 2
2018-04-25 11:32:05,538 INFO [Thread-29] (EmployeeService.java:27) - oldMoney: 3
2018-04-25 11:32:05,538 INFO [Thread-25] (EmployeeService.java:27) - oldMoney: 3
2018-04-25 11:32:05,540 INFO [Thread-32] (EmployeeService.java:27) - oldMoney: 3
2018-04-25 11:32:05,541 INFO [Thread-30] (EmployeeService.java:27) - oldMoney: 3
2018-04-25 11:32:05,540 INFO [Thread-28] (EmployeeService.java:27) - oldMoney: 3
2018-04-25 11:32:05,542 INFO [Thread-31] (EmployeeService.java:27) - oldMoney: 3
2018-04-25 11:32:05,542 INFO [Thread-33] (EmployeeService.java:27) - oldMoney: 3
2018-04-25 11:32:05,548 INFO [Thread-37] (EmployeeService.java:27) - oldMoney: 4
2018-04-25 11:32:05,550 INFO [Thread-21] (EmployeeService.java:27) - oldMoney: 4
2018-04-25 11:32:05,550 INFO [Thread-34] (EmployeeService.java:27) - oldMoney: 4
2018-04-25 11:32:05,575 INFO [Thread-39] (EmployeeService.java:27) - oldMoney: 5
2018-04-25 11:32:05,575 INFO [Thread-38] (EmployeeService.java:27) - oldMoney: 5
2018-04-25 11:32:05,576 INFO [Thread-41] (EmployeeService.java:27) - oldMoney: 5
2018-04-25 11:32:05,575 INFO [Thread-40] (EmployeeService.java:27) - oldMoney: 5
2018-04-25 11:32:05,577 INFO [Thread-42] (EmployeeService.java:27) - oldMoney: 5
2018-04-25 11:32:05,577 INFO [Thread-44] (EmployeeService.java:27) - oldMoney: 6
2018-04-25 11:32:05,578 INFO [Thread-45] (EmployeeService.java:27) - oldMoney: 6
2018-04-25 11:32:05,602 INFO [Thread-35] (EmployeeService.java:27) - oldMoney: 7
2018-04-25 11:32:05,603 INFO [Thread-13] (EmployeeService.java:27) - oldMoney: 7
2018-04-25 11:32:05,604 INFO [Thread-36] (EmployeeService.java:27) - oldMoney: 7
2018-04-25 11:32:05,604 INFO [Thread-12] (EmployeeService.java:27) - oldMoney: 8
2018-04-25 11:32:05,605 INFO [Thread-10] (EmployeeService.java:27) - oldMoney: 8
2018-04-25 11:32:05,606 INFO [Thread-9] (EmployeeService.java:27) - oldMoney: 9
2018-04-25 11:32:05,606 INFO [Thread-6] (EmployeeService.java:27) - oldMoney: 9
2018-04-25 11:32:05,606 INFO [Thread-1] (EmployeeService.java:27) - oldMoney: 9
2018-04-25 11:32:05,644 INFO [Thread-47] (EmployeeService.java:27) - oldMoney: 10
2018-04-25 11:32:05,644 INFO [Thread-48] (EmployeeService.java:27) - oldMoney: 10
2018-04-25 11:32:05,644 INFO [Thread-46] (EmployeeService.java:27) - oldMoney: 10
2018-04-25 11:32:05,649 INFO [Thread-49] (EmployeeService.java:27) - oldMoney: 10
2018-04-25 11:32:05,650 INFO [Thread-75] (EmployeeService.java:27) - oldMoney: 10
2018-04-25 11:32:05,651 INFO [Thread-76] (EmployeeService.java:27) - oldMoney: 10
2018-04-25 11:32:05,651 INFO [Thread-50] (EmployeeService.java:27) - oldMoney: 10
2018-04-25 11:32:05,686 INFO [Thread-79] (EmployeeService.java:27) - oldMoney: 11
2018-04-25 11:32:05,686 INFO [Thread-78] (EmployeeService.java:27) - oldMoney: 11
2018-04-25 11:32:05,686 INFO [Thread-77] (EmployeeService.java:27) - oldMoney: 11
2018-04-25 11:32:05,693 INFO [Thread-80] (EmployeeService.java:27) - oldMoney: 11
2018-04-25 11:32:05,693 INFO [Thread-100] (EmployeeService.java:27) - oldMoney: 11
2018-04-25 11:32:05,693 INFO [Thread-81] (EmployeeService.java:27) - oldMoney: 11
2018-04-25 11:32:05,694 INFO [Thread-51] (EmployeeService.java:27) - oldMoney: 11
2018-04-25 11:32:05,694 INFO [Thread-82] (EmployeeService.java:27) - oldMoney: 11
2018-04-25 11:32:05,737 INFO [Thread-59] (EmployeeService.java:27) - oldMoney: 12
2018-04-25 11:32:05,738 INFO [Thread-58] (EmployeeService.java:27) - oldMoney: 12
2018-04-25 11:32:05,739 INFO [Thread-57] (EmployeeService.java:27) - oldMoney: 12
2018-04-25 11:32:05,746 INFO [Thread-60] (EmployeeService.java:27) - oldMoney: 12
2018-04-25 11:32:05,746 INFO [Thread-99] (EmployeeService.java:27) - oldMoney: 12
2018-04-25 11:32:05,746 INFO [Thread-62] (EmployeeService.java:27) - oldMoney: 12
2018-04-25 11:32:05,748 INFO [Thread-61] (EmployeeService.java:27) - oldMoney: 12
2018-04-25 11:32:05,786 INFO [Thread-63] (EmployeeService.java:27) - oldMoney: 13
2018-04-25 11:32:05,786 INFO [Thread-66] (EmployeeService.java:27) - oldMoney: 13
2018-04-25 11:32:05,786 INFO [Thread-64] (EmployeeService.java:27) - oldMoney: 13
2018-04-25 11:32:05,792 INFO [Thread-65] (EmployeeService.java:27) - oldMoney: 13
2018-04-25 11:32:05,792 INFO [Thread-97] (EmployeeService.java:27) - oldMoney: 13
2018-04-25 11:32:05,792 INFO [Thread-98] (EmployeeService.java:27) - oldMoney: 13
2018-04-25 11:32:05,792 INFO [Thread-68] (EmployeeService.java:27) - oldMoney: 13
2018-04-25 11:32:05,792 INFO [Thread-96] (EmployeeService.java:27) - oldMoney: 13
2018-04-25 11:32:05,839 INFO [Thread-95] (EmployeeService.java:27) - oldMoney: 14
2018-04-25 11:32:05,840 INFO [Thread-92] (EmployeeService.java:27) - oldMoney: 14
2018-04-25 11:32:05,839 INFO [Thread-93] (EmployeeService.java:27) - oldMoney: 14
2018-04-25 11:32:05,860 INFO [Thread-94] (EmployeeService.java:27) - oldMoney: 14
2018-04-25 11:32:05,862 INFO [Thread-67] (EmployeeService.java:27) - oldMoney: 14
2018-04-25 11:32:05,861 INFO [Thread-89] (EmployeeService.java:27) - oldMoney: 14
2018-04-25 11:32:05,867 INFO [Thread-91] (EmployeeService.java:27) - oldMoney: 14
2018-04-25 11:32:05,887 INFO [Thread-90] (EmployeeService.java:27) - oldMoney: 15
2018-04-25 11:32:05,890 INFO [Thread-88] (EmployeeService.java:27) - oldMoney: 16
2018-04-25 11:32:05,893 INFO [Thread-85] (EmployeeService.java:27) - oldMoney: 17
2018-04-25 11:32:05,893 INFO [Thread-86] (EmployeeService.java:27) - oldMoney: 17
2018-04-25 11:32:05,894 INFO [Thread-84] (EmployeeService.java:27) - oldMoney: 17
2018-04-25 11:32:05,894 INFO [Thread-87] (EmployeeService.java:27) - oldMoney: 18
2018-04-25 11:32:05,895 INFO [Thread-5] (EmployeeService.java:27) - oldMoney: 18
2018-04-25 11:32:05,896 INFO [Thread-4] (EmployeeService.java:27) - oldMoney: 19
2018-04-25 11:32:05,935 INFO [Thread-71] (EmployeeService.java:27) - oldMoney: 20
2018-04-25 11:32:05,935 INFO [Thread-70] (EmployeeService.java:27) - oldMoney: 20
2018-04-25 11:32:05,936 INFO [Thread-73] (EmployeeService.java:27) - oldMoney: 20
2018-04-25 11:32:05,941 INFO [Thread-69] (EmployeeService.java:27) - oldMoney: 20
2018-04-25 11:32:05,941 INFO [Thread-74] (EmployeeService.java:27) - oldMoney: 20
2018-04-25 11:32:05,941 INFO [Thread-72] (EmployeeService.java:27) - oldMoney: 20
2018-04-25 11:32:05,941 INFO [Thread-7] (EmployeeService.java:27) - oldMoney: 20
2018-04-25 11:32:05,978 INFO [Thread-11] (EmployeeService.java:27) - oldMoney: 21
2018-04-25 11:32:05,978 INFO [Thread-3] (EmployeeService.java:27) - oldMoney: 21

日志分析:

  1. 日志中第1到15行,它们的 oldMoney 全部等于0
  2. 说明对应的15根线程在同一时间读到了相同的值0
  3. 这15根线程给Money设置的新值都是 0+1=1
  4. 这15根线程再分别执行Sql更新操作,期间可能会竞争Mysql的行锁

2 不能解决的原因分析

现在来分析一下为什么Mysql的行锁加上可重复读不能解决并发事务的更新问题

可重复读:表示的是在同一个事务内,所有的读取操作返回相同的数据,也就是不感知外部事务对数据的变更。假设事务A是当前事务,事务B是外部事务,事务A现在读取到 Money=10,接下来,事务B也读到值为10同时将其更新为11,但是对于事务A来说,它不能感知到外部事务对数据的变更,所以,事务A还是错误地认为当前 Money=10 并将其更新为11,于是,事务A覆盖了事务B的提交,从而造成更新丢失。

至于行锁,这只是用来保证并发事务更新时的先后顺序。比如,事务B在将Money=10更新为11时,如果正在这个更新期间(假设更新时间比较长)事务A也想做更新操作,则事务A将被阻塞,直到事务B更新完成后事务A才能更新,它与数据本身(事务A的、事务B的)没有任何关系。

由此可见,虽然 Mysql 是行锁且默认是可重复读,但是对于并发事务的更新操作,仍然会出现更新丢失问题,解决办法是——锁。

Mysql锁机制--并发事务带来的更新丢失问题的更多相关文章

  1. mysql锁机制和事务隔离

    mysql事务 1.InnoDB事务原理 事务(Transaction)是数据库区别于文件系统的重要特性之一,事务会把数据库从一种一致性状态转换为另一种一致性状态. 在数据库提交时,可以确保要么所有修 ...

  2. Mysql 锁机制和事务

    InnoDB 锁机制 InnoDB存储引擎支持行级锁 其大类可以细分为共享锁和排它锁两类 共享锁(S):允许拥有共享锁的事务读取该行数据.当一个事务拥有一行的共享锁时,另外的事务可以在同一行数据也获得 ...

  3. Mysql锁机制和事务控制

    如何加锁 锁定表的语法:    LOCK TABLES    tbl_name [AS alias] {READ [LOCAL] | [LOW_PRIORITY] WRITE}    [, tbl_n ...

  4. Mysql锁机制--乐观锁 & 悲观锁

    Mysql 系列文章主页 =============== 从 这篇 文章中,我们知道 Mysql 并发事务会引起更新丢失问题,解决办法是锁.所以本文将对锁(乐观锁.悲观锁)进行分析. 第一部分 悲观锁 ...

  5. (三)MySQL锁机制 + 事务

    转: (三)MySQL锁机制 + 事务 表锁(偏读) 偏向MyISAM存储引擎.开销小,加锁快,无死锁,锁定粒度大,发生锁冲突的概率最高,并发最低. 查看当前数据库中表的上锁情况,0表示未上锁. sh ...

  6. mysql锁机制(转载)

    锁是计算机协调多个进程或线程并发访问某一资源的机制 .在数据库中,除传统的 计算资源(如CPU.RAM.I/O等)的争用以外,数据也是一种供许多用户共享的资源.如何保证数据并发访问的一致性.有效性是所 ...

  7. 再谈mysql锁机制及原理—锁的诠释

    加锁是实现数据库并发控制的一个非常重要的技术.当事务在对某个数据对象进行操作前,先向系统发出请求,对其加锁.加锁后事务就对该数据对象有了一定的控制,在该事务释放锁之前,其他的事务不能对此数据对象进行更 ...

  8. mysql锁机制 读书笔记

    目录 MySQL锁机制 1.什么是锁 2.lock与latch 3.InnoDB存储引擎中的锁 3.1锁的类型 3.2 一致性非锁定读 3.3 一致性锁定读 4 锁的算法 4.1行锁的3中算法 4.2 ...

  9. Mysql锁机制介绍

    Mysql锁机制介绍 一.概况MySQL的锁机制比较简单,其最显著的特点是不同的存储引擎支持不同的锁机制.比如,MyISAM和MEMORY存储引擎采用的是表级锁(table-level locking ...

随机推荐

  1. 安装 docker-compose

    安装Docker-Compose之前,请先安装 python-pip,安装好pip之后,就可以安装Docker-Compose了. 一.检查是否已经安装 二.安装 docker-compose 1.安 ...

  2. zuul入门(3)开发zuul的过滤器

    1.编写Zuul过滤器(Java&Groovy) 理解过滤器类型和请求生命周期后,我们来编写一个Zuul过滤器.编写Zuul的过滤器非常简单,我们只需继承抽象类ZuulFilter,然后实现几 ...

  3. SpringBoot框架中JPA使用的一些问题

    主要是自己在使用JPA框架时遇到的一个坑,拿出来分享一下 首先上一个简单JPA框架实体 public interface EnterpriseInfoDao extends JpaSpecificat ...

  4. MySQL/上

    MySQL操作/上 一.视图 视图表是一个虚拟表(非真实存在),其本质是[根据sql语句获取动态的数据集,并为其命名],用户使用表只需使用(名称)即可获取结果集,并可以将其当做表来使用. 1.创建视图 ...

  5. Nginx配置特定二级域名

    首先把先在域名设置页面把二级域名解析到服务器的公网IP上,这里假设是 bbs.domainname.com 然后编辑 /etc/nginx/sites-available/domain.com.con ...

  6. ORACLE数据库编程

    第一章 Oracle数据库基本概念 一.介绍 Oracle数据库系统是美国Oracle(甲骨文)公司提供的以分布式数据库为 核心的一组软件产品,是目前最流行的客户/服务器(Client/Server, ...

  7. 用js来实现那些数据结构(栈01)

    其实说到底,在js中栈更像是一种变种的数组,只是没有数组那么多的方法,也没有数组那么灵活.但是栈和队列这两种数据结构比数组更加的高效和可控.而在js中要想模拟栈,依据的主要形式也是数组. 从这篇文章开 ...

  8. 如何在现有的 Web 应用中使用 ReactJS

    原文:How to Sprinkle ReactJS into an Existing Web Application 译者:nzbin 当我们学习一项新技术,可能是一个 JavaScript 框架, ...

  9. MySQL高可用架构之MHA 原理与实践

    MHA简介 关于MHA MHA(Master HA)是一款开源的MySQL的高可用程序,它为MySQL主从复制架构提供了automating master failover 功能.MHA在监控到mas ...

  10. requests之一:HTTP请求 状态码

    1.请求方法主要有如下几种: Verb 描述 HEAD 只获取某个资源的头部信息,元数据.比如只想了解某个文件的大小,某个资源的修改日期等 GET 获取资源,一个或者多个 POST 创建资源 PATC ...