mysql:如何解决数据修改冲突(事务+行级锁的实际运用)
摘要:最近做一个接诊需求遇到一个问题,假设一个订单咨询超过3次就不能再接诊,但如果两个医生同时对该订单进行咨询,查数据库的时候查到的接诊次数都是2次,那两个医生都能接诊,所谓接诊可以理解为更新了接诊次数,此时就出现了问题(接诊了4次)。
其实这个问题看似很明朗,但想要完全解决需要理解事务和锁的概念,以前总对事务的隔离级别有点云里雾里,现在可以通过这个案例可以理清楚。
事务
操作数据库最小的工作单位,简单讲就是将多条dml(增删改)语句联合完成。要么同时成功,要么同时失败。看到这里你可能会发现光加事务解决不了上述问题,而且加了事务之后,多条事务之间的相互关系就涉及到事务的隔离级别,所以接着往下看。
事务隔离级别
READ UNCOMMITTED(读未提交,脏读)
事务中的修改,即使没有提交,对其他会话也是可见的。可以读取未提交的数据——脏读。脏读会导致很多问题,一般不适用这个隔离级别.。
-- ------------------------- read-uncommitted实例 ------------------------------
-- 设置全局系统隔离级别
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
-- Session A
START TRANSACTION;
SELECT * FROM USER;
UPDATE USER SET NAME="READ UNCOMMITTED";
-- commit; -- Session B
SELECT * FROM USER; //SessionB Console 可以看到Session A未提交的事物处理,在另一个Session 中也看到了,这就是所谓的脏读
id name
2 READ UNCOMMITTED
34 READ UNCOMMITTED
READ COMMITTED(读已提交,不可重复读)
一般数据库都默认使用这个隔离级别(MySQL 不是), 这个隔离级别保证了一个事务如果没有完全成功(commit 执行完),事务中的操作对其他会话是不可见的。
-- ------------------------- read-cmmitted实例 ------------------------------
-- 设置全局系统隔离级别
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- Session A
START TRANSACTION;
SELECT * FROM USER;
UPDATE USER SET NAME="READ COMMITTED";
-- COMMIT; -- Session B
SELECT * FROM USER; //Console OUTPUT:
id name
2 READ UNCOMMITTED
34 READ UNCOMMITTED ---------------------------------------------------
-- 当 Session A执行了commit,Session B得到如下结果:
id name
2 READ COMMITTED
34 READ COMMITTED
REPEATABLE READ (可重复读)
一个事务中多次执行统一读 SQL,返回结果一样。这个隔离级别解决了脏读的问题,幻读问题。这里指的是 innodb 的 rr 级别,innodb 中使用 next-key 锁对"当前读"进行加锁,锁住行以及可能产生幻读的插入位置,阻止新的数据插入产生幻行。

会话T1事务中执行一次查询,然后会话T2新插入一行记录,这行记录恰好可以满足T1所使用的查询的条件。然后T1又使用相同 的查询再次对表进行检索,但是此时却看到了事务T2刚才插入的新行。这个新行就称为“幻像”,因为对T1来说这一行就像突然 出现的一样。innoDB 的 RR 级别无法做到完全避免幻读。
SERIALIZABLE (可串行化)
最强的隔离级别,通过给事务中每次读取的行加锁,写加写锁,保证不产生幻读问题,但是会导致大量超时以及锁争用问题。
mysql默认的隔离级别是可重复读,看到这里大家应该明白,即使隔离级别是可重复读,但因为select操作时并未加锁,导致都会查到符合条件的数据,所以这里要引入一个锁的概念:行级锁。
行级锁
共享锁(S) 共享锁也称为读锁,读锁允许多个连接可以同一时刻并发的读取同一资源,互不干扰;
排他锁(X) 排他锁也称为写锁,一个写锁会阻塞其他的写锁或读锁,保证同一时刻只有一个连接可以写入数据,同时防止其他用户对这个数据的读写。
总结(解决方案)
其实上文分析了那么多,最后的解决方案很简单。就是在原来的read和update合起来加事务,原来的select语句加排他锁,即在select语句后面加for update。假如有事务A,B,加入排他锁之后,假设事务A先获得锁,事务B必须等到事务A commit之后才能开始select,所以读到的是最新修改的数据。至于为什么不加共享锁,除了可能造成脏写之后,在这种情况下还可能造成死锁。假如两个事务 A 、 B 都读取同一行记录,那么在这一行就加上了共享锁,但是 A 和B 事务中都需要修改这一行,那么都要等待对方释放共享锁才能进行,结果造成了死锁。
mysql:如何解决数据修改冲突(事务+行级锁的实际运用)的更多相关文章
- Mysql在InnoDB引擎下索引失效行级锁变表锁案例
先做好准备,创建InnoDB引擎数据表,并添加了相应的索引 DROP TABLE IF EXISTS `innodb_lock`; CREATE TABLE `innodb_lock` ( `a` ) ...
- Mysql的行级锁
我们首先需要知道的一个大前提是:mysql的锁是由具体的存储引擎实现的.所以像Mysql的默认引擎MyISAM和第三方插件引擎 InnoDB的锁实现机制是有区别的. Mysql有三种级别的锁定:表级锁 ...
- SQL Server 中 ROWLOCK 行级锁
一.ROWLOCK的使用 1.ROWLOCK行级锁确保,在用户取得被更新的行,到该行进行更新,这段时间内不被其它用户所修改.因而行级锁即可保证数据的一致性,又能提高数据操作的并发性. 2.ROWLOC ...
- Mysql事务及行级锁的理解
在最近的开发中,碰到一个需求签到,每个用户每天只能签到一次,那么怎么去判断某个用户当天是否签到呢?因为当属表设计的时候,每个用户签到一次,即向表中插入一条记录,根据记录的数量和时间来判断用户当天是否签 ...
- Mysql事务及行级锁
事务隔离级别 数据库事务隔离级别,只是针对一个事务能不能读取其它事务的中间结果. Read Uncommitted (读取未提交内容) 在该隔离级别,所有事务都可以看到其他未提交事务的执行结果.本隔离 ...
- Mysql的事务及行级锁
转自:http://www.cnblogs.com/edwinchen/p/4171866.html 以签到为例,每个用户每天只能签到一次,那么怎么去判断某个用户当天是否签到呢?因为当初表设计的时候, ...
- [数据库事务与锁]详解五: MySQL中的行级锁,表级锁,页级锁
注明: 本文转载自http://www.hollischuang.com/archives/914 在计算机科学中,锁是在执行多线程时用于强行限制资源访问的同步机制,即用于在并发控制中保证对互斥要求的 ...
- MySQL行级锁,表级锁,页级锁详解
页级:引擎 BDB. 表级:引擎 MyISAM , 理解为锁住整个表,可以同时读,写不行 行级:引擎 INNODB , 单独的一行记录加锁 表级,直接锁定整张表,在你锁定期间,其它进程无法对该表进行写 ...
- MySQL行级锁、表级锁、页级锁详细介绍
原文链接:http://www.jb51.net/article/50047.htm 页级:引擎 BDB.表级:引擎 MyISAM , 理解为锁住整个表,可以同时读,写不行行级:引擎 INNODB , ...
随机推荐
- Restful规则及JPA导致内存溢出
HTTP动词 对于资源的具体操作类型,由HTTP动词表示. 常用的HTTP动词有下面五个(括号里是对应的SQL命令). GET(SELECT):从服务器取出资源(一项或多项). POST(CREATE ...
- DateUtils 时间工具类
首先,定义`时间枚举值` public enum TimeEnum { /** * 时间格式 */ YYYY_MM_DD("yyyy-MM-dd"), YYYY_MM_DD_HH_ ...
- MySQL在Windows下压缩包方式安装与卸载
一.MySQL的卸载: 1.停止MySQL服务 2.移除MySQL 二.安装: 1.官网下载压缩版 https://downloads.mysql.com/archives/community/ 2. ...
- C# AutoMapper:流行的对象映射框架,可减少大量硬编码,很小巧灵活,性能表现也可接受。
AutoMapper 是一个对象-对象映射器,可以将一个对象映射到另一个对象. 官网地址:http://automapper.org/ 官方文档:https://docs.automapper.org ...
- WEBSERVICE 分析器错误信息: 未能创建类型
问题描述 分析器错误 说明: 在分析向此请求提供服务所需资源时出错.请检查下列特定分析错误详细信息并适当地修改源文件. 分析器错误信息: 未能创建类型"Service1". 源错误 ...
- spring mvc与mybatis事务整合
之前公司用的是mybatis,但事务管理这块是用ejb的CMT容器管理的事务.基本原理是ejb请求进来,业务代码会创建一个mybatis的session然后放入当前线程,之后所有的方法操作涉及到数据库 ...
- maven项目添加jar包
使用集成工具创建一个maven项目,如果需要添加开发包,只需去maven仓库找到对应的包,将配置信息加入pom.xml文件即可.这样,我们就再也不用到处寻找和下载jar包了. 用例:开发工具 STS ...
- node获取请求参数的方法get与post请求
1.get请求 get的请求参数是携带在url中的,因此需要引入url模块对请求进行解析,再使用url.parse()方法,get请求多用于页面跳转.表单等请求中,例如page页码.表单账号密码等 先 ...
- ES6箭头函数与this指向
一.ES6箭头函数 ES6之前的语法想要定义一个函数,如图example1,ES6之后定义一个函数如图example2,相比较之下简洁了很多 二.函数的this指向 非箭头函数,谁调用函数this指向 ...
- ORB-SLAM3 细读单目初始化过程(上)
作者:乔不思 来源:微信公众号|3D视觉工坊(系投稿) 3D视觉精品文章汇总:https://github.com/qxiaofan/awesome-3D-Vision-Papers/ 点击上方&qu ...