1. MySQL中的锁与锁策略

在MySQL中,为了应对并发场景下的读写,锁通常分为两类:共享锁以及排他锁。其中,共享锁允许多个连接在同一时间并发的读取相同的资源,彼此之间互不影响,所以又称为读锁。排他锁则会阻塞其他尝试获取共享锁或者排他锁的操作,确保同一时间只有一个连接可以写入数据,并禁止其他用户的读写,又称写锁。

在实际使用下,加锁往往意味着高昂的开销,MySQL为了平衡锁的开销以及并发的线程之间的安全,采用了两种不同的锁策略:

  1. table lock(表锁)

表锁会锁定整张表,如果当前有用户正在执行写操作并且获取了写锁,这可能导致整张表被锁定,阻塞其他用户的读写操作。如果用户执行的是读操作,则会获取读锁,此时其他用户的并发读操作将被接受,写操作会被阻塞。

举个例子,执行语句

:

如果b字段不存在索引,那么会锁住所有的记录,即锁上了表锁。

  1. row lock(行锁)

行锁的粒度是在每一条行数据,这意味行锁可以尽可能的支持并发处理,相应的行锁

开销也会比较大。并且,在InnoDB中的行锁是针对索引加的锁,不是针对记录加的锁,并且该索引不能失效,否则行锁将会自动升级为表锁

相比较而言,表锁的优势在于开销小,加锁快,无死锁,劣势是锁的粒度大,发生锁冲突的概率较高,并发能力较弱。而行锁则相反。实际使用中,两者都会由MySQL自动加锁。行锁冲突可以通过执行 show status like 'innodb_row_lock%'语句进行分析,表锁冲突则可通过执行show status like 'table_locks%' 进行查看。

  1. MySQL中的事务与隔离级别

事务就是一组原子性的sql,要么MySQL引擎会全部执行这一组sql语句,要么全部不

行(不允许任何一条失败)。失败的语句将导致事务的整个回滚。事务系统通常满足四个特性,分别为原子性(要么全部执行、要么全部回滚)、一致性(数据必须从一个一致性状态转换为另一种一致性状态)、隔离性(事务未执行成功,其他人无法看到结果)、持久性(事务在commit之后,数据不会丢失)。

由上述概念可知,事务是用来保障数据的一致性以及完整性的。也是MySQL中用来平衡效率与安全之间的一种手段,所以,InnoDB引擎下的事务通常提供了四种事务的隔离级别,方便用户自己在效率和安全之间做出权衡。

  1. READ UNCOMMITED(未提交读)

事务中的修改,即使该事务未提交,对其他的事务也是可见的。可以读取到其他事务

中的数据,又称为脏读,在实际数据库事务中,脏读会破坏数据的一致性,对业务产生极大影响,所以一般不推荐采用READ UNCOMMITED作为数据库事务的隔离级别。

  1. READ COMMITED(提交读)

在提交读级别中,数据库将保证如果一个事务没有完全执行成功(commit完成),事务中的操作对其他的事务是不可见的。在该隔离级别下,虽然杜绝了脏读的发生,但是还是存在着不可重复读以及幻读的问题。不可重复读发生在事务T1读取了一行数据,事务T2接着修改或者删除了该行数据(已提交),当T1事务再次读取同一行数据的时候,发现数据已经被修改或者被删除。示例如下图:

幻读则发生在事务T1读取了满足某条件的一个数据集,事务T2此时插入了一行或者多行满足T1查询条件的的数据并提交,当T1再次采用相同的条件进行读取时,得到了与第一次不同的结果集。示例如下:

  1. REPEATABLE READ(提交读)

REPEATEABLE READ是MySQL的默认隔离级别,它确保同一个事务的多个实例在并

发读取数据时,会看到同样的数据。按照该隔离级别定义,还是会存在幻读的问题。MySQL通过InnoDB存储引擎的多版本并发控制机制(MVCC)解决了部分该问题的场景,但仍然存在,此处后文会另有分析。

  1. SERIALIZABLE(串行化)

串行化是最严格的隔离级别,通过给事务中的每次读写操作都加锁,保证了不产生任何

脏读、不可重复读以及幻读问题,但是随之引入的是大量的读超时以及锁竞争,导致数据库性能的严重下降。

另外来看看ANSI SQL STANDARD中,对于数据库隔离级别以及相应问题的规定:

所以,对于REPEATABLE READ隔离级别下,是允许出现幻读的。

  1. MySQL中的MVCC

MVCC(multiple-version-concurrency-control)是个行级锁的变种,它在普通读情况下避

免了加锁操作,因此开销更低。其原理具体为,在InnoDB存储引擎中,每行数据会加入一些隐藏字段DATA_TRX_ID,DATA_ROLL_PTR,DB_ROW_ID,DELETE_BIT。DATA_TRX_ID 字段记录了数据的创建和删除时间,这个时间指的是对数据进行操作的事务的id, DATA_ROLL_PTR 指向当前数据的undo log记录,回滚数据就是通过这个指针,DELETE BIT位用于标识该记录是否被删除,这里的不是真正的删除数据,而是标志出来的删除。真正意义的删除是在mysql进行数据的GC,清理历史版本数据的时候。

相应的,其DML的处理方式也发生了变化:

SELECT语句先查找DATA_TRX_ID早于当前事务ID的数据行。这样就保证了读取的数据要么是在这个事务开始之前就已经commit了的(早于当前事务ID),要么是在这个事务中自身创建的数据(等于当前事务ID)。查找行的DELETE_BIT为1时,查找删除事务ID对应的事务,确定此条记录在当前事务开始之前,行没有被删除。

INSERT语句会在新插入行数据之后,保存当前事务ID作为行的DATA_TRX_ID。

DELETE语句为每一条删除的记录保存当前的事务ID作为行的删除标记。

UPDATE语句将复制变更的记录,并把新记录的DATA_TRX_ID置为当前事务ID,同时更新老记录中的DB_ROLL_PT指向了上一个版本。

所以在并发读的时候,不需要等到访问行上的锁释放,只需要读取一个行的快照即可。既然是多版本的读取,就肯定读取不到其他事务中的新插入的数据了,也就避免了上述场景中提到的幻读。

  1. 幻读

从上述信息我们已经知道,在REPEATABLE READ级别下,InnoDB采取多版本策略成功

避免了部分幻读现象,但是实际使用中,还是会有幻读产生,先看场景:

通过MVCC,在事务中的多次读取不会出现幻读,但是此时的插入操作依旧会发生主键重复的错误,并且因为MVCC机制,在上图中的会话1无论读取多少次都不会读到导致冲突产生的数据,确实就如“幻影”一般诡异。

为了解决上述场景中的幻读,需要简单提一下InnoDB的行锁机制,在InnoDB引擎下存在三种行锁,分别为:

  1. Record Lock:在单行记录上的锁
  2. Gap Lock:间隙锁,锁定一个范围,但不包括记录本身,。GAP锁的目的,是为了防止同一事务的两次读出现幻读的情况
  3. Next-Key Lock: 前两个锁的共同使用,即锁定了记录本身,也锁定了一定的范围。

通常情况下,INSERT/UPDATE/DELETE默认会在操作的记录上加上Next-Key Lock,而

普通的SELECT因为MVCC的关系反而只需要读取快照即可,所以如果业务需要再REPEATABLE READ场景下保证绝对不产生幻读,需要手动给SELECT加锁,在类似SELECT…WHERE加入FOR UPDATE(排它锁)或者LOCK IN SHARE MODE(共享锁)。

  1. 总结
  1. InnoDB中使用索引作为检索条件修改数据时采用行锁,否则使用表锁
  2. InnoDB自动给修改操作加锁,给查询操作不自动加锁
  3. 在REPEATABLE READ级别下,如果要完全杜绝幻读,需要手动给关键查询语句加锁
  4. 表的大部分数据需要修改时,行锁反而不如表锁更有效率

最后,本文只是就网易云信业务中数据库的一些使用场景和问题作了总结,数据库的实

际使用还存在诸多学问,希望能抛砖引玉,让更多人分享出自己的心得。

邀请好友使用网易云信,好友下单成功即可获得500元网易考拉/严选无门槛现金券,点击立即推荐>>


网易云信(NeteaseYunXin)是集网易19年IM以及音视频技术打造的PaaS服务产品,来自网易核心技术架构的通信与视频云服务,稳定易用且功能全面,致力于提供全球领先的技术能力和场景化解决方案。开发者通过集成客户端SDK和云端OPEN API,即可快速实现包含IM、音视频通话、直播、点播、互动白板、短信等功能。

浅谈MySQL数据库中的锁与事务相关推荐

  1. 【转载】运维角度浅谈MySQL数据库优化

     运维角度浅谈MySQL数据库优化 2015-06-02 14:22:02 标签:mysql优化   mysql分库分表分区 mysql读写分离 mysql主从复制 原创作品,允许转载,转载时请务必以 ...

  2. mysql declare与set的区别_浅谈MySQL存储过程中declare和set定义变量的区别

    在存储过程中常看到declare定义的变量和@set定义的变量.简单的来说,declare定义的类似是局部变量,@set定义的类似全局变量. 1.declare定义的变量类似java类中的局部变量,仅 ...

  3. mysql key_len_浅谈mysql explain中key_len的计算方法

    mysql的explain命令可以分析sql的性能,其中有一项是key_len(索引的长度)的统计.本文将分析mysql explain中key_len的计算方法. 1.创建测试表及数据 CREATE ...

  4. mysql存储过程set什么意思_浅谈MySQL存储过程中declare和set定义变量的区别

    在存储过程中常看到declare定义的变量和@set定义的变量.简单的来说,declare定义的类似是局部变量,@set定义的类似全局变量. 1.declare定义的变量类似java类中的局部变量,仅 ...

  5. pymysq向mysql写数据 为什么本地无法查看_从运维角度浅谈MySQL数据库优化,中小企业DBA必会...

    原文:http://www.enmotech.com/web/detail/1/712/1.html(复制链接,打开浏览器即可查看原文) 作者:搬砖游击队 一个成熟的数据库架构并不是一开始设计就具备高 ...

  6. 运维角度浅谈MySQL数据库优化(转自:2018-03-10 李振良 JAVA高级架构)

    一个成熟的数据库架构并不是一开始设计就具备高可用.高伸缩等特性的,它是随着用户量的增加,基础架构才逐渐完善.这篇博文主要谈MySQL数据库发展周期中所面临的问题及优化方案,暂且抛开前端应用不说,大致分 ...

  7. 从运维角度浅谈MySQL数据库优化

    一个成熟的数据库架构并不是一开始设计就具备高可用.高伸缩等特性的,它是随着用户量的增加,基础架构才逐渐完善.这篇博文主要谈MySQL数据库发展周期中所面临的问题及优化方案,暂且抛开前端应用不说,大致分 ...

  8. 从运维角度浅谈MySQL数据库优化,中小企业DBA必会

    一个成熟的数据库架构并不是一开始设计就具备高可用.高伸缩等特性的,它是随着用户量的增加,基础架构才逐渐完善. 这篇博文主要谈MySQL数据库发展周期中所面临的问题及优化方案,暂且抛开前端应用不说,大致 ...

  9. (转)运维角度浅谈MySQL数据库优化

    转自:http://lizhenliang.blog.51cto.com/7876557/1657465 一个成熟的数据库架构并不是一开始设计就具备高可用.高伸缩等特性的,它是随着用户量的增加,基础架 ...

最新文章

  1. Swift学习笔记-协议(Protocols)
  2. matlab中clc,close,close all,clear,clear all作用区别
  3. 开源资产管理系统java_开源资产管理软件 GLPI 9.3.1 部署
  4. 推荐系统炼丹笔记:多模态推荐之用户评论篇
  5. linux用户密码转换为明文,Linux运维知识之linux下抓取内存中明文密码mimipenguin
  6. LeetCode 876. 链表的中间结点(快慢指针)
  7. script标签的加载解析执行
  8. android studio绑定数据库表,Android:数据绑定库的使用
  9. 提高你的迅雷速度,绝对值得一看
  10. sprd camera 帧率设置_UnityPlayerSetting-Android 打包设置介绍
  11. xmlns:app=http://schemas.android.com/apk/res-auto
  12. h3c trunk口改access_H3C交换机恢复出厂和各种基本配置
  13. 怎么将多个文本文件合并为一个文本文件
  14. keil生成bin文件
  15. c语言的条件运算符,条件运算符c语言
  16. python exec 函数_Python之浅谈exec函数
  17. anisotropy texture filtering
  18. 麋鹿分布图制作(二)—— 用Python和R在地图上打点
  19. 在豌豆荚安卓市场下载了伪造的ES文件管理器,该流氓apk会自动的下载手机游戏
  20. Redis的运用(简单)

热门文章

  1. HDU 3790 最短路径问题 (dijkstra)
  2. 144.⑨要写信(错排公式与高精度练习)
  3. WINCC中使用ADO对象连接数据库 例子 常用属性 方法 原创
  4. .net 流(Stream) - 文件流、内存流、网络流
  5. 两块 硬盘 主从盘跳线详解
  6. 一文攻破共用体-C语言
  7. 数据库原理上机实验内容报告代码
  8. android 得到毫秒时间戳,android – Location.getTime()总是返回没有毫秒的时间戳
  9. Python-OpenCV-- 台式机外接摄像头pyTesseract文本框实时检测
  10. 图解TCP四次握手断开连接