点赞再看,养成习惯,微信搜一搜【一角钱小助手】关注更多原创技术文章。
本文 GitHub org_hejianhui/JavaStudy 已收录,有我的系列文章。

前言

  • MySQL索引底层数据结构与算法
  • MySQL性能优化原理-前篇
  • MySQL性能优化-实践篇1
  • MySQL性能优化-实践篇2

前面我们讲了MySQL数据库底层的数据结构与算法、MySQL性能优化篇一些内容。我们再来聊聊MySQL的锁与事务隔离级别,分上下两篇,本篇重点讲MySQL的行锁与事务隔离级别。

锁定义

锁是计算机协调多个进程或线程并发访问某一资源的机制。

在数据库中,除了传统的计算资源(如CPU、RAM、I/O等)的争用以外,数据也是一种供需要用户共享的资源。如何保证数据并发访问的一致性、有效性是所有数据库必须解决的一个问题,锁冲突也是影响数据库并发访问性能的一个重要因素。

锁分类

  • 从性能上分为乐观锁(用版本对比来实现)和 悲观锁
  • 从数据库操作类型分为:读锁写锁 (都属于悲观锁)
    • 读锁(共享锁):针对同一份数据,多个读操作可以同时进行而不会互相影响;
    • 写锁(排它锁):当前写操作没有完成之前,它会阻断其它写锁和读锁。
  • 从数据库操作的粒度分为:表锁行锁

对于锁深入的理解,可以查看《关于Java中锁的理解》。

MySQL的锁

  • 行锁(Record Locks)

  • 间隙锁(Gap Locks)

  • 临键锁(Next-key Locks)

  • 共享锁/排他锁(Shared and Exclusive Locks)

  • 意向共享锁/意向排他锁(Intention Shared and Exclusive Locks)

  • 插入意向锁(Insert Intention Locks)

  • 自增锁(Auto-inc Locks)

  • 预测锁,这种锁主要用于存储了空间数据的空间索引。

下篇来分别聊聊,本篇重点是行锁以及事务隔离级别。

表锁

每次操作锁住整张表。

  • 开销小,加锁快;
  • 不会出现死锁;
  • 锁粒度大,发生锁冲突的概率最高;
  • 并发度最低。

基本操作

示例表,如下:

# 建表SQL
CREATE TABLE mylock (id INT(11) NOT NULL AUTO_INCREMENT,NAME VARCHAR(20) DEFAULT NULL,PRIMARY KEY(id)
) ENGINE = MyISAM DEFAULT CHARSET = utf8;# 插入数据
INSERT INTO`test`.`mylock`(`id`,`NAME`) VALUES ('1','a');
INSERT INTO`test`.`mylock`(`id`,`NAME`) VALUES ('2','b');
INSERT INTO`test`.`mylock`(`id`,`NAME`) VALUES ('3','c');
INSERT INTO`test`.`mylock`(`id`,`NAME`) VALUES ('4','d');
  • 手动增加表锁
lock table 表名称 read(write), 表名称2 read(write);
  • 查看表上加过的锁
show open tables;
  • 删除表锁
unlock tables;

案例分析 — 加读锁

LOCK TABLE mylock read;


当前 session 和其他 seesion 都可以读该表;

当前 session 中插入或者更新锁定表都会报错,其他 session 插入或者更新则会等待。

案例分析 — 加写锁

LOCK TABLE mylock WRITE;


当前 session 对该表的增删改查都没有问题,其他 session 对该表的所有操作都会被阻塞 。

案例结论

MyISAM 在执行查询语句(SELECT)前,会自动给涉及的所有表加读锁;在执行增删改查操作前,会自动给涉及的表加写锁。

  • 对 MyISAM 表的读操作(加读锁),不会阻塞其他进程同一表的读请求,但会阻塞对同一表的写请求。只有当读锁释放后,才会执行其他进程的写操作。
  • 对 MyISAM 表的写操作(加写锁),会阻塞其他进程对同一表的读和写操作,只有当写锁释放后,才会执行其他进程的读写操作。

总结:读锁会阻塞写,但不会阻塞读;而写锁则会把读和写都阻塞

行锁

每次操作锁住一行数据。

  • 开销大,加锁慢;
  • 会出现死锁;
  • 锁定粒度最小,发生锁冲突的概率最低;
  • 并发度最高。

InnoDB 和 MyISAM 的最大不同点:

  • 支持事务(TRANSACTION)
  • 支持行级锁

行锁支持事务

事务(Transaction)及其 ACID 属性

事务是由一组 SQL 语句组成的逻辑处理单元,事务具有以下四个属性,通常简称为事务的 ACID属性

  • 原子性(Atomicity):事务是一个原子操作单元,其对数据的修改,要么全部执行,要么全部不执行。
  • 一致性(Consistent):在事务开始和完成时,数据都必须保持一致状态。这意味着所有相关的数据规则都必须应用于事务的修改,以保持数据的完整性;事务结束时,所有的内部数据结构(如B+树索引或双向链表)也都必须是正确的。
  • 隔离性(Lsolation):数据库系统提供一定的隔离机制,保障事务在不受外部并发操作影响的“独立”环境执行。这意味着事务处理过程中的中间状态对外部是不可见的,反之亦然。
  • 持久性(Durable):事务完成之后,它对于数据的修改是永久性的,即使出现系统故障也能保持。

并发事务处理带来的问题

  • 更新丢失(Lost Update)

当两个或多个事务选择同一行,然后基于最初选定的值更新该行值,由于每个事务都不知道其他事务的存在,就会发生丢失更新问题,最后的更新覆盖来其他事务所做的更新。

  • 脏读(Dirty Reads)

一个事务正在对一条记录做修改,在这个事务完成并提交前,这个条记录的数据就处于不一致的状态;这时另外一个事务也来读取同一条记录,如果不加控制,第二个事务读取来这些“脏”数据,并据此做进一步的处理,就会产生未提交的数据依赖关系。这种现象被形象的叫做“脏读”。

总结:事务A读取到来事务B已经修改但尚未提交的数据,还在这个数据基础上做来操作。此时,如果事务B回滚,事务A读取的数据无效,不符合一致性要求。

  • 不可重复读(Non-Repeatable Reads)

一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生来改变、或某些记录已经被删除了,这种现象就叫做“不可重复读”。

总结:事务A读取到了事务B已经提交的修改数据,不符合隔离性。

  • 幻读(Phantom Reads)

一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为“幻读”。

总结:事务A读取到了事务B提交的新增数据,不符合隔离性。

事务隔离级别

“脏读”、“不可重复读”、“幻读”,其实都是数据库读一致性问题,必须由数据库提供一定的事务隔离机制来解决。

数据库的事务隔离越严格,并发副作用越小,但付出的代价也就越大,因为事务隔离实质上就是使事务在一定程度上“串行化”进行,这显然与“并发”是矛盾的。

同时,不同应用对读一致性和事务隔离程度的要求也是不同的,比如许多应用对“不可重复读”和“幻读” 并不敏感,可能更关系数据并发访问的能力。

查看当前数据库的事务隔离级别

show variables like 'tx_isolation';


设置事务隔离级别

set tx_isolation='REPEATABLE-READ';

数据库版本是5.7,隔离级别是Repeatable-Read(可重复读),不同的数据库版本和隔离级别对语句的执行结果影响很大。所以需要说明版本和隔离级别

行锁与隔离级别案例分析

事务控制语句

  • BEGINSTART TRANSACTION;显式地开启一个事务;
  • COMMIT;也可以使用 COMMIT WORK,不过二者是等价的。COMMIT会提交事务,并使已对数据库进行的所有修改称为永久性的;
  • ROLLBACK;有可以使用 ROLLBACK WORK,不过二者是等价的。回滚会结束用户的事务,并撤销正在进行的所有未提交的修改;
  • SAVEPOINT identifier;SAVEPOINT允许在事务中创建一个保存点,一个事务中可以有多个SAVEPOINT;
  • RELEASE SAVEPOINT identifier;删除一个事务的保存点,当没有指定的保存点时,执行该语句会抛出一个异常;
  • ROLLBACK TO identifier;把事务回滚到标记点;
  • SET TRANSACTION;用来设置事务的隔离级别。InnoDB存储引擎提供事务的隔离级别有READ UNCOMMITTEDREAD COMMITTEDREPEATABLE READSERIALIZABLE

事务处理方法

MYSQL 事务处理主要有两种方法:

  1. BEGIN, ROLLBACK, COMMIT来实现

    • BEGIN 开始一个事务
    • ROLLBACK 事务回滚
    • COMMIT 事务确认
  2. 直接用 SET 来改变 MySQL 的自动提交模式:

    • SET AUTOCOMMIT=0 禁止自动提交
    • SET AUTOCOMMIT=1``开启自动提交

示例表,如下:

CREATE TABLE `user` (`id` INT (11) NOT NULL AUTO_INCREMENT,`name` VARCHAR (255) DEFAULT NULL,`balance` INT (11) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE = INNODB DEFAULT CHARSET = utf8;INSERT INTO `test`.`user` (`name`,`balance`) VALUES ('zhangsan','450');
INSERT INTO `test`.`user` (`name`,`balance`) VALUES ('lisi', '16000');
INSERT INTO `test`.`user` (`name`,`balance`) VALUES ('wangwu','2400');

行锁演示

一个 session 开启事务更新不提交,另一个 seesion 更新同一条记录会阻塞,更新不同记录u会阻塞。

读未提交

(1)打开一个客户端A,并设置当前事务模式为 read uncommitted (读未提交),查询表 user 的初始化值

set tx_isolation='read-uncommitted';


(2)在客户端A的事务提交之前,打开另一个客户端B,更新表 user

(3)这时,虽然客户端B的事务还没提交,但是在客户端A就可以查询到B已经更新的数据

(4)一旦客户端B的事务因为某种原因回滚,所有的操作都将会被撤销,那么客户端A查询到的数据其实就是脏数据

(5)在客户端A执行更新语句 update user set balance = balance - 50 where id = 1; zhangsan 的 balance没有变成350,居然是400,是不是很奇怪,数据不一致啊。如果你这么想就太天真了,在应用程序中,我们会用400-50=350,并不知道其他会话回滚了,要想解决这个问题可以采用读已提交的隔离级别。

读已提交

(1)打开一个客户端A,并设置当前事务模式为 read committed (读已提交),查询表 user 的所有记录

set tx_isolation='read-committed';


(2)在客户端A的事务提交之前,打开另一个客户端B,更新表 user

(3)这时,客户端B的事务还没提交,客户端A不能查询到B已经更新的数据,解决了脏读问题。

(4)客户端B的事务提交

(5)客户端A执行与上一步相同的查询,结果与上一步不一致,即产生了不可重复读的问题。

可重复读

(1)打开一个客户端A,并设置当前的事务模式为 repeatable read ,查询表 user 的所有记录。

set tx_isolation='repeatable-read';


(2)在客户端A的事务提交之前,打开另一个客户端B,更新表 user 并提交。

(3)在客户端A查询表 user 的所有记录,与步骤(1)查询结果一直,没有出现不可重复读的问题。

(4)在客户端A,接着执行 update user set balance = balance - 50 where id = 1 , balance 没有变成 400 - 50 = 350, zhangsan 的 balance 的值用的是步骤(2) 中的 350 来计算的,所以是300,数据的一致性倒是没有被破坏。可重复读的隔离级别下使用了 MVCC(multi-version concurrency control)机制,select 操作不会更新版本号,是快照读(历史版本);insert、update、delete 会更新版本号,是当前读(当前版本)。

我们下篇来讲 MVCC。


(5)重新打开客户端B,插入一条新数据后提交。

(6)在客户端A查询表user 的所有记录,没有查出新增数据,所以没有出现幻读。

(7)验证幻读
在客户端A执行 update user set balance = 8888 where id = 4; ,能更新成功,再次查询到客户端B新增的数据。

串行化

(1)打开一个客户端A,并设置当前事务模式为 serializable ,查询表 user 的初始值

set tx_isolation='serializable';


(2)打开一个客户端B,并设置当前事务模式为 serializable ,插入一条记录报错,表被锁了插入失败,MySQL 中事务隔离级别为 serializable 时会锁表,因此不会出现幻读的情况,这种隔离级别并发性极低,开发中很少会用到。

案例结论

InnoDB 存储引擎由于实现了行级锁定,虽然在锁定机制的实现方面所带来的性能损耗可能比表级锁定会更高一下,但是在整体并发处理能力方面要远远优于 MyISAM 的表级锁定的。当系统并发量最高的时候,InnoDB 的整体性能和 MyISAM 相比就会有比较明显的优势。

但是,InnoDB 的行级锁定同样也有其脆弱的一面,当我们使用不当的时候,可能会让 InnoDB 的整体性能表现不仅不能比 MyISAM 高,甚至可能会更差。

行锁分析

通过检查 innodb_row_lock 状态变量来分析系统上的行锁的竞争情况:

show status like 'innodb_row_lock%';


对各个状态量的说明如下:

  • Innodb_row_lock_current_waits :当前正在等待锁定的数量
  • Innodb_row_lock_time :从系统启动到现在锁定总时间长度
  • Innodb_row_lock_time_avg :每次等待所花平均时间
  • Innodb_row_lock_time_max :从系统启动到现在等待最长的一次所花时间
  • Innodb_row_lock_waits :系统启动后到现在总共等待的次数

对于这5个状态变量,比较重要的主要是:

  • Innodb_row_lock_time_avg (等待平均时长)
  • Innodb_row_lock_waits (等待总次数)
  • Innodb_row_lock_time(等待总时长)

尤其是当等待次数很高,而且每次等待时长也不小的时候,我们就需要分析系统 中为什么会有如此多的等待,然后根据分析结果着手制定优化计划。

死锁

set tx_isolation='repeatable-read';
Session_1执行:select * from user where id=1 for update;
Session_2执行:select * from user where id=2 for update;
Session_1执行:select * from user where id=2 for update;
Session_2执行:select * from user where id=1 for update;

查看近期死锁日志信息:

show engine innodb status\G;

大多数情况mysql可以自动检测死锁并回滚产生死锁的那个事务,但是有些情况 mysql没法自动检测死锁

优化建议

  1. 尽可能让所有数据检索都通过索引来完成,避免无索引行锁升级为表锁;
  2. 合理设计索引,尽量缩小锁的范围;
  3. 尽可能减少检索条件范围,避免间隙锁;
  4. 尽量控制事务大小,减少锁定资源量和时间长度,涉及事务加锁的sql尽量放在事务最后执行;
  5. 尽可能低级别事务隔离。

问答

  1. MySQL 默认级别是 repeatable-read,有什么办法可以解决幻读妈?

间隙锁(Gap Lock)在某些情况下可以解决幻读问题,它是 Innodb 在 可重复读 提交下为解决幻读问题时引入的锁机制。要避免幻读可以用间隙锁在Session_1 下面执行 update user set name = 'hjh' where id > 10 and id <= 20; ,则其他 Session 没法在这个范围锁包含的间隙里插入或修改任何数据。

如:user 表有3条数据, id > 2 and id <=3 会把第三条记录锁住,其他会话对则无法对第三条记录做操作。

  1. 无索引锁会升级为表锁,锁主要是加在索引上,如果对非索引字段更新,行锁可能会变变锁。

客户端A执行: update user set balance = 800 where name = 'zhangsan';

客户端B对该表任一行执行修改、删除操作都会阻塞

InnoDB 的行锁是针对索引加的锁,不是针对记录加的锁。并且该索引不能失效,否则都会从行锁升级为表锁。

  1. 锁定某一行还可以用 local in share mode(共享锁)for update(排它锁) ,例如: select * from test_innodb_lock where a = 2 for update; 这样其他 session 只能读这行数据,修改则会被阻塞,直到锁定行的 session 提交。

部分图片来源于网络,版权归原作者,侵删。

面试官问:MySQL锁与事物隔离级别你知道吗?相关推荐

  1. 事物的级别_面试官问:MySQL锁与事物隔离级别你知道吗?

    本文作者:何建辉(公众号:org_yijiaoqian) 点赞再看,养成习惯,微信搜一搜[一角钱小助手]关注更多原创技术文章. 本文 GitHub org_hejianhui/JavaStudy 已收 ...

  2. MySQL锁、事务隔离级别、MVCC机制详解、间隙锁、死锁等

    一. 简介 1. 锁定义 锁是计算机协调多个进程或线程并发访问某一资源的机制. 在数据库中,除了传统的计算资源(如CPU.RAM.I/O等)的争用以外,数据也是一种供需要用户共享的资源.如何保证数据并 ...

  3. mysql幻读和不可重复读的区别_面试官:MySQL的可重复读级别能解决幻读吗

    Java面试笔试面经.Java技术每天学习一点 Java面试 关注不迷路 作者:宁愿. 来源:https://juejin.im/post/5c9040e95188252d92095a9e 引言 之前 ...

  4. 不可重复读和幻读的区别_面试官:MySQL的可重复读级别能解决幻读吗

    Java面试笔试面经.Java技术每天学习一点 Java面试 关注不迷路 作者:宁愿. 来源:https://juejin.im/post/5c9040e95188252d92095a9e 引言 之前 ...

  5. Java面试必问的MySQL锁与事务隔离级别

    前言 众所周知,事务和锁是mysql中非常重要功能,同时也是面试的重点和难点.本文会详细介绍事务和锁的相关概念及其实现原理,相信大家看完之后,一定会对事务和锁有更加深入的理解. 什么是事务 在维基百科 ...

  6. 面试官:MySQL索引底层数据结构原理与性能调优,你能回答多少?

    哈喽!大家好,我是小奇,一位不靠谱的程序员 小奇打算以轻松幽默的对话方式来分享一些技术,如果你觉得通过小奇的文章学到了东西,那就给小奇一个赞吧 文章持续更新,可以微信搜索[小奇JAVA面试]第一时间阅 ...

  7. 数据库的四种事物隔离级别(基于MySQL的InnoDB存储引擎)

    准备工作 创建一个表,并插入几条基础数据 CREATE TABLE `transaction_isolation` (`id` bigint(10) NOT NULL AUTO_INCREMENT,` ...

  8. MySQL数据库事物隔离级别

    mysql数据库的事物隔离级别主要有四种: Serializable 串行化,一个事物一个事物的执行 Repeatable read 可重复读 , 无论其他事物是否修改并提交了数据,在这个事物中看到的 ...

  9. mysql 消极锁_MySQL支持的事物隔离级别以及消极锁和乐观锁原理和应用场景

    在一样平常开发中,尤其是营业开发,少不了行使 Java 对数据库举行基本的增删改查等数据操作,这也是Java工程师的必备手艺之一.做好数据操作,不仅仅需要对Java语言相关框架的掌握,更需要对种种数据 ...

最新文章

  1. 远程连接windows出现身份验证错误,提示由于CredSSP加密Oracle修正解决方案
  2. 《LeetCode力扣练习》剑指 Offer 29. 顺时针打印矩阵 Java
  3. Java EE WEB工程师培训-JDBC+Servlet+JSP整合开发之14.Servlet请求头信息
  4. 北漂、海漂的你,每个月发薪资支付生活成本后还剩多少钱
  5. 黑马程序员C++学习笔记(第一阶段:基础)
  6. 2019文都计算机网络百度云,2019计算机考研|计算机网络知识:计算机网络体系结构...
  7. JavaScript函数和对象
  8. 美赛数学建模O奖论文下载
  9. 计算机管理规划,2019年计算机软考系统规划与管理师考试大纲
  10. angularJs-基础用法
  11. 打开GTA 5竟要跑19.8亿次if语句!黑客嘲讽R星代码烂,修改后直接省70%加载时间...
  12. java vcard格式_关于vcard 文件数据格式,以备不时之需
  13. 强化学习王者荣耀项目修改
  14. 计算机基础应用在线模拟考试,计算机应用基础模拟试卷一与答案.pdf
  15. seo主要做什么(记录你工作的内容)
  16. POJ1608 Banal Tickets
  17. SAP打开财务和物料的账期(OB52 MMRV MMPV)
  18. recv/send堵塞和非堵塞
  19. 山西大同服务器地理位置,山西地理位置“最好”的城市,被誉“北方锁钥”,不是长治、太原...
  20. python大佬教你爬虫反爬:破解雪碧图反爬

热门文章

  1. 将正式数据库中的表与测试库同步
  2. 用TW8836驱动ST7701S TTL屏调试记录
  3. 自我实现的人的15种心理特征
  4. 基于JAVA政府采购线上招投标平台计算机毕业设计源码+系统+数据库+lw文档+部署
  5. MySQL高级-(存储引擎、索引、锁)
  6. 《没那么简单》-黄小琥
  7. Vue 监听刷新 切屏
  8. amd显卡用黑苹果输出黑屏_微星HD7850显卡DVI接口黑屏,改DSDT无果
  9. 将svg编译成字体图标
  10. 学计算机游戏与动漫好吗,学习计算机动漫与游戏制作前途如何?