关于mysql的事务,大多数java开发都是非常熟悉的。但是近期面试了一批同学,发现很多同学对mysql事务的理解都是停留在spring的@Transactional上,问到为什么使用事务时往往会背书一样回答出ACID的一些概念。照本宣科其实并不是我想听到的答案,相信真正了解过,思考过mysql原理和设计思想的开发都应该有更深一步的体会。

那么,想要深入谈谈某件事情,那首先得回到“这件事情是做什么的,有什么用”这个方向上面。那在设计事务的初衷,它是用来做什么的呢?我个人觉得至少有以下几点作用,分别以具体的应用场景举例:

①某个业务操作是不可分割的,要么全部失败,要么全部成功。如电子记账系统里面,A给了B100块钱,要么交易失败,A和B的余额都不变,要么交易成功A的余额-100同时B的余额+100。

②开发人员为了性能和效率,采用反范式设计,即允许少量的冗余,通过空间换时间的方法来缩短查询业务的响应时间。

这里展开说明一下所谓范式设计的概念。通常情况下,范式设计包括三点:1NF确保原子性(Atomicity)原子性的粒度、原子性的价值——也就是把值当做单值用,建议不要把值拆开; 2NF检查对键的完全依赖价值在在于控制数据冗余和查询性能;3NF检查属性的独立性,不在多张表中存储相同意义的数据。虽然范式设计可以使得表结构设计变得简洁明了便于维护,但是缺点是,范式化的表,在查询的时候经常需要很多的关联,因为单独一个表内不存在冗余和重复数据。这导致多次的关联,增加查询代价,可能使一些索引策略无效。所以我们经常需要将一列常用属性重复存储到多张表里面,在这些列上面分别设置索引。

如:某个业务要对博文进行排序,排序规则为按照博主的用户等级倒序排列。根据3NF的范式设计规则,那么资源表是不应该存在博主相关的字段的。但是往往为了性能原因,要在博文对应的表中新增用户等级这个冗余字段,并在用户等级提升时做冗余更新。那么这种情况下的更新一样可能会遇到原子性或者一致性问题。同理的,有时候会遇到需要频繁计数的场景,由于innoDB下查询count(*)会遇到全索引扫描的问题,性能比较低,往往需要计数表或者计数缓存的方案,那么前者在更新是也会遇到原子性或者一致性问题。

有人会觉得用redis等缓存计数是一个不错的方案,但实际上,这种方案也有一定的问题。首先就是需要额外维护一个组件,缓存带来的更新丢失也是一个大问题。但实际上,将计数保存在缓存系统中的方式,还不只是丢失更新的问题。即使 Redis 正常工作,这个值还是逻辑上不精确的。我们可以看一下一下的时序图:

上图中,会话 A 是一个插入交易记录的逻辑,往数据表里插入一行 R,然后 Redis 计数加 1;会话 B 就是查询页面显示时需要的数据。在上图的这个时序里,在 T3 时刻会话 B 来查询的时候,会显示出新插入的 R 这个记录,但是 Redis 的计数还没加 1。这时候,就会出现我们说的数据不一致。你一定会说,这是因为我们执行新增记录逻辑时候,是先写数据表,再改 Redis 计数。而读的时候是先读 Redis,再读数据表,这个顺序是相反的。那么,如果保持顺序一样的话,是不是就没问题了?我们现在把会话 A 的更新顺序换一下,再看看执行结果:

你会发现,这时候反过来了,会话 B 在 T3 时刻查询的时候,Redis 计数加了 1 了,但还查不到新插入的 R 这一行,也是数据不一致的情况。

在并发系统里面,我们是无法精确控制不同线程的执行时刻的,因为存在图中的这种操作序列,所以,我们说即使 Redis 正常工作,这个计数值还是逻辑上不精确的。

③隔离性问题,正在处理的事务我不想被其他事务感知到,以免影响正常的业务逻辑。比如我有一个入库操作,会进行大量的数据插入,我还有一个定时任务用来做统计,会统计入库相关的大量指标。那么如果我在某个商品时入库就进行统计,有可能只统计到一半插入的数据,另一半的操作还在写入就没办法统计到。这种情况下我肯定是希望我能统计到相同一个时间版本的数据,这就涉及到事务的隔离性问题了。

实际上,mysql的默认引擎InnoDB 的行数据有多个版本,每个数据版本有自己的 row trx_id,每个事务或者语句有自己的一致性视图。普通查询语句是一致性读,一致性读会根据 row trx_id 和一致性视图确定数据版本的可见性。对于可重复读,查询只承认在事务启动前就已经提交完成的数据;对于读提交,查询只承认在语句启动前就已经提交完成的数据;更新数据都是先读后写的,而这个读,只能读当前的值,称为“当前读”(current read)。

也就是说,mysql的读分为两种,一种是版本读,一个是当前读。在版本读的时候,如果没有获取到该行数据的写锁,读到的很有可能不是最新提交的数据。比如最常见的更新丢失场景:我要读表t中某一行的a列(int类型),将其加一并更新到数据库里面,常用的做法有两种(假定是第一行,a初始值为1,假设有两个会话A和B,A先B后,mysql的隔离级别为默认的可重复读):

1.在代码里面先select a from t where id = 1,然后讲这个值赋予到内存变量x,然后在应用程序内存里面进行加一得到x = (1 + 1 = 2),然后 update a set a = 2 where id = 1。

2.使用手写的sql执行:update t set a = a + 1 where id = 1;

在第一种情况下,这段逻辑是由java应用程序+jdbc驱动+mysql数据库共同完成的,而且这段程序逻辑如果部署在服务器上运行,是很有可能有很多线程同时操作的。这个时候如果有两个线程同时需要进行做加法操作,则在高并发的时候可能出现AB线程同时读取到值为1,然后同时将该行数据update为2,那么,其中B线程就会出现“更新丢失”的现象。

这种情况其实是比较常见的,往往在长事务的情况下。A线程在+1操作后,应用程序还继续进行了其他sql操作,导致没有提交事务,在A线程的事务时间窗口内,B线程无论怎么读,读到的都是1这个旧制,导致计算的最终值有误,最终执行了update t set a = 2 where id = 1这条错误sql。这就是因为在事务的隔离机制下,存在着“版本读”这个效果。

而在第二种情况下,应用程序通过jdbc驱动同时向mysql server提交两个sql操作请求。这种情况下,Mysql会出现更新丢失的情况嘛?答案是不会的。因为在Mysql中,为了确保单条sql是原子性,无论是非声明事务开启与否,innoDB都会为单条sql开启事务,为其分配单独的事务id。那么A获取到id=1这条数据的行锁时,B对id=1的这行数据的更新和读取将会被阻塞,直到A更新完毕后B会话才能继续执行。而且,此时哪怕A会话所在的事务没有提交,B在更新时会采取“当前读”的策略,读到的总是最新的数据,不会出现更新丢失的情况。

④持久性问题,如我在写数据的时候,希望它像是合同一样,我在签字之前完全可以放弃提交(事务的回滚),但是我一旦签字,那么这东西的效用应该是持久的。这里提醒一下,不要过于依赖数据库的回滚功能,如果一个方法需要频繁回滚,那么这一定是有问题的。

以上就是数据库最基本的ACID问题,在实际的开发中,问题往往是这几种要素的排列组合。复杂的问题也往往导致了解决方案的不同,了解清楚mysql的原理将有助我们选择最优的方案,可以将数据控制的更精准,查询变得更迅捷。在第二讲中,我会结合spring的常见orm框架,进行进一步的讲解,欢迎各位朋友点赞收藏~有疑问的可以评论区一起讨论~

深入MYSQL之事务篇(一)相关推荐

  1. mysql人像伴随模型_还不了解MySQL跨行事务模型吗?看完这篇你也就差不多了!...

    导读 说来和MySQL倒是有缘,毕业的第一份工作就被分配到了RDS团队,主要负责把MySQL弄到云上做成数据库服务.虽说整天和MySQL打交道,但说实话那段时间并没有很深入的理解MySQL内核,做的事 ...

  2. MySQL事务篇:ACID原则、事务隔离级别及事务机制原理剖析

    引言 众所周知,MySQL数据库的核心功能就是存储数据,通常是整个业务系统中最重要的一层,可谓是整个系统的"大本营",因此只要MySQL存在些许隐患问题,对于整个系统而言都是致命的 ...

  3. mysql 事务sql_mysql存储过程之事务篇

    事务的四大特征: ACID:Atomic(原子性).Consistent(一致性).Isolated(独立性).Durable (持久性) MySQL的事务支持不是绑定在MySQL服务器本身,而是与存 ...

  4. 简单介绍MySQL开启事务的两种方式

    本篇文章给大家分享MySQL 是如何开启一个事务的,原文通过两种方式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧 方式 1 START TRANSACTION 或 ...

  5. java 一个大事务下的新增、修改、查询_重新学习Mysql数据库8:MySQL的事务隔离级别实战...

    本文转自:https://blog.csdn.net/sinat_27143551/article/details/80876127 本系列文章将整理到我在GitHub上的<Java面试指南&g ...

  6. mysql中decimal不能为空吗_程序员,知道Mysql中事务ACID的原理吗?

    点击上方"linkoffer", 选择关注公众号高薪职位第一时间送达 引言 照例,我们先来一个场景~ 面试官:"知道事务的四大特性么?" 你:"懂,A ...

  7. limit实现原理 mysql_值得一生典藏:MySQL的事务实现原理

    点击上方"匠心零度",选择"设为星标" 做积极的人,而不是积极废人 原文地址:https://www.toutiao.com/a6777338939360412 ...

  8. mysql 默认事务隔离级别_MySQL 事务隔离级别详解

    个人公众号『码农札记』,欢迎关注,查看更多精彩文章. 简介: MySQL的事务隔离级别一共有四个,分别是读未提交.读已提交.可重复读以及可串行化. 四个特性ACID 原子性 (Atomicity) 事 ...

  9. insert事务隔离mysql_MySQL数据库详解(三)MySQL的事务隔离剖析

    提到事务,你肯定不陌生,和数据库打交道的时候,我们总是会用到事务.最经典的例子就是转账,你要给朋友小王转 100 块钱,而此时你的银行卡只有 100 块钱. 转账过程具体到程序里会有一系列的操作,比如 ...

最新文章

  1. 《全球人工智能产业地图》发布(附PPT图片)
  2. 找出数组中两个只出现一次的数字
  3. spring + redis 实现数据的缓存
  4. Scala隐式转换动态丰富类库功能
  5. 7-7 六度空间 (30分)_近30年仅6人生涯总决赛首秀得分30+ 浓眉哥能成下一个吗
  6. 从Ant Build演进Gradle Build:导入Ant Build文件
  7. 微软Scott CIO也要代表公司拜访客户
  8. java unsafe park_Java魔法类——Unsafe应用解析
  9. iOS开发cocoaPod的使用
  10. 开源ckplayer 网页播放器, 跨平台(html5, mobile),flv, f4v, mp4, rtmp协议. webm, ogg, m3u8 !...
  11. Java 1.1.5 空串与 Null 串
  12. case结构条件语句
  13. 三角函数公式及工程应用
  14. 女儿当自强,男儿更当自强
  15. 菜狗杯Misc迅疾响应wp
  16. android 侧滑删除方法,Android 基于RecyclerView的Item侧滑删除
  17. [转载]如何用JDO开发数据库应用(3)
  18. “世界很美好,值得你为之奋斗”我只同意后半句。
  19. 【探花交友】阿里云OSS、百度人脸识别
  20. [Tomcat]配置默认访问端口及Tomcat默认访问项目

热门文章

  1. VMware 16安装的Ubuntu18.04 系统,无法在Windows和Ubuntu之间直接自由拖拽复制文件
  2. PTA 7-14 统计英文单词个数
  3. 华为研究印度成功经验:对英语有深刻理解力
  4. 五个小技巧让你选择一个出色而节约用水的马桶
  5. unity如何制作随机生成器
  6. Java pdf使用aspose分页转图片后再合成pdf再压缩pdf, 所有文件放到缓存中代码
  7. Scientific Toolworks Understand(代码分析软件) v5.1.1001免费版
  8. TiDB 在小米的应用实践 1
  9. 从零开始Web自动化(三):通过selenium,9行代码实现打字网站的自动打字
  10. HD2500 黑苹果完美驱动