事务的本质和死锁的原理
仅以MySQL和Spring为例,本文不介绍事务和锁的概念。
本文使用伪代码表示方法代码,仅仅表达方法的意义及事务注解。
事务的形状
在我心中,事务一直是这个样子的
x轴是上锁的资源,y轴是消耗的时间,
事务方块随着时间的流逝向下移动,
当碰触x轴时资源加锁,越过x轴时资源解锁
上图是对于方法a的事务形状,我起名【 事务方块】。
@Transactional
function a(){
对A表修改,耗时五秒
}
一、多事务
当一个方法调用多个被事务注解的子方法时情况是
上图是对于方法abc调用时,a方法、b方法、c方法的事务形状
function abc(){
this.a(){}
this.c(){}
}
@Transactional
function a(){
对A表修改,耗时五秒
}
@Transactional
function b(){
对B表修改,耗时五秒
}
@Transactional
function c(){
对C表修改,耗时五秒
}
其中abc方法开始执行时,执行到a方法,锁定a表,当a方法结束b方法开始时,a表解锁,b表锁定,当b方法结束c方法开始时,b表解锁c表锁定。
相当于下图的三个事务方块联合且相对位置锁定一起下落,总运行时间15秒
如果并发请求两次abc方法则事务方块如下图
其中a表会先被请求1锁定5秒后解锁,再被请求2锁定5秒,
其中b表也会先被请求1锁定5秒后解锁,再被请求2锁定5秒,
其中c表也会先被请求1锁定5秒后解锁,再被请求2锁定5秒,
而请求1在解锁表a后紧接着又锁定了表b五秒,同时表a再被请求2锁定5秒
依次类推,请求2都在请求1解锁对应的表之后,锁定该表,
那么总运行时间20秒。
二、大事务
@Transactional
function abc(){
this.a(){}
this.b(){}
this.c(){}
}
function a(){
对A表修改,耗时五秒
}
function b(){
对B表修改,耗时五秒
}
function c(){
对C表修改,耗时五秒
}
其中abc方法上有事务注解,而子方法a、b、c上没有事务注解,事务方块形状如下图
a、b、c三个颜色的方块是结合在一起的只能一起执行,那么a表被锁定15秒,b表被锁定10秒,c表被锁定5秒
如果也并发请求两次abc方法则总耗时30秒,显而易见事务方块越大,耗时越长。
在这个abc方法中,事务锁定表是懒锁定的方式,就是说
当abc中a方法开始执行时,只锁定了a表,执行完a方法后,
开始执行b方法锁定b表,此时a表不解锁,当b方法执行完后,
开始执行c方法锁定c表,此时a、b两个表都不解锁,当c方法执行完后,a、b、c三个表一起解锁。
这就造成了以下情况。
三、死锁
有如下两个方法ab和ba
@Transactional
function ab(){
this.a(){}
this.b(){}
}
@Transactional
function ba(){
this.b(){}
this.a(){}
}
当ab和ba方法同时被执行时,事务方块类似下图,但不完全
当ab和ba方法同时被执行时,ab锁定a表,ba锁定b表,
当ab执行完a方法请求锁定b表时,ba也执行完了b方法请求锁定a表,
但ab没有解开对a表的锁定,ba也没有解开对b表的锁定,那么相互等待对方解锁,这就是死锁。
所以减少死锁出现的几率的办法是减小事务方块的大小,即减小事务方块消耗的时间或减小事务方块锁定的资源【表或行】
所以行级锁不易出现死锁,表级锁易出现死锁,是因为行级锁事务方块小,但消耗时间不一定,还是需要参考事务消耗时间。
如果主键是int类型自增id,则相当于把事务可锁定资源从一个表分成了21亿份的行,可见使用行级锁时事务方块明显变小。
四、名称来历
事务方块的名字来历是因为联想到俄罗斯方块,俄罗斯方块是为了给方块找一个位置放置,而事务方块是为了给方块找一个时间间隙执行通过,两者目的不同。
很抱歉我不会做动图,请自行联想俄罗斯方块下落的情况。
也可以类比成要过桥的车辆,桥宽度固定,有的车占用一车道,有的车占用两车道,有的车占用半车道,
车的形状多种多样,如老式武装三轮摩托车像ab方法的小车,大客车就是一车道的长车,自行车是半车道的小车。类似的情形很多,可自行联想。
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 从 Dagger 到 Hilt,谷歌为何执着于让我们用依赖注入?
- 为什么非对称加密比对称加密慢?
- 如何优化Nginx的处理性能
- 一键生成前后端代码,Mybatis-Plus代码生成器让我舒服了
- 各类好玩免费API推荐,强烈建议收藏
- 回敬Python蹭女神热度助发朋友圈,Java实现头像分成九宫图,再生成拼图头像
- java作业:实现数组翻转
- windows下搭建spark测试环境
- 微信小程序实时语音识别实践
- 轻量安全的部署方案
- 阿里2020.7.31笔试
- 聊聊dubbo-go的PrometheusReporter
- 聊聊dubbo-go的apolloConfiguration
- 看了这篇,关于浏览器缓存你还有哪些疑问?
- 深入分析Volatile的实现原理