你知道MySQL锁与事物隔离级别吗?


你知道MySQL锁与事物隔离级别吗?

相关免费学习推荐:mysql数据库(视频)

前言

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

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

锁定义

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

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

锁分类

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

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

MySQL的锁

  • 行锁(Record Locks)

  • 间隙锁(Gap Locks)

  • 临键锁(Next-key Locks)

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

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

  • 插入意向锁(Insert Intention Locks)

  • 自增锁(Auto-inc Locks)

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

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

表锁

每次操作锁住整张表。

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

基本操作

示例表,如下:

# 建表SQLCREATE 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;复制代码
image.png

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

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

image.png

案例分析 — 加写锁

LOCK TABLE mylock WRITE;复制代码
image.png

当前 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提交的新增数据,不符合隔离性。

事务隔离级别

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

你知道MySQL锁与事物隔离级别吗?

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

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

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

show variables like 'tx_isolation';复制代码
image.png

设置事务隔离级别

set tx_isolation='REPEATABLE-READ';复制代码

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

行锁与隔离级别案例分析

事务控制语句

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

事务处理方法

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

SONIFY.io SONIFY.io

设计和开发音频优先的产品和数据驱动的解决方案

SONIFY.io 83 查看详情 SONIFY.io
  1. BEGIN, ROLLBACK, COMMIT来实现
    • BEGIN 开始一个事务
    • ROLLBACK 事务回滚
    • COMMIT 事务确认
  1. 直接用 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会阻塞。

image.png
image.png

读未提交

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

set tx_isolation='read-uncommitted';复制代码
image.png

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

image.png

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

image.png

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

image.png

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

image.png

读已提交

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

set tx_isolation='read-committed';复制代码
image.png

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

image.png

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

image.png

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

image.png

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

image.png

可重复读

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

set tx_isolation='REPEATABLE-READ';复制代码
image.png

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

image.png

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

image.png

(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。

image.png

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

image.png

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

image.png

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

串行化

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

set tx_isolation='serializable';复制代码
image.png

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

image.png

案例结论

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

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

行锁分析

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

show status like 'innodb_row_lock%';复制代码
image.png

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

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

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

  • Innodb_row_lock_time_*g (等待平均时长)
  • 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  ,则其他 Session 没法在这个范围锁包含的间隙里插入或修改任何数据。

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

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

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

image.png

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

image.png

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

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

以上就是你知道MySQL锁与事物隔离级别吗?的详细内容,更多请关注其它相关文章!


# 就会  # 福田网站建设系统哪个好  # seo黑猫技巧  # 崇明区搜索引擎网站优化  # 样品市场推广营销  # 近的宝安网站推广  # 修武县网站推广公司有哪些  # 散文好句网站推广文案  # 如何优化推广网站  # 怎样完成网站优化  # 如何给企业电话营销推广  # MySQL  # 则会  # 事务处理  # 不符合  # 你知道  # 多个  # 数据结构  # 镜像  # 死锁  # 客户端  # 锁与事物 


相关栏目: 【 Google疑问12 】 【 Facebook疑问10 】 【 优化推广96088 】 【 技术知识133117 】 【 IDC资讯59369 】 【 网络运营7196 】 【 IT资讯61894


相关推荐: 六级准考证号怎么查_四六级准考证查询入口官网  如何用mysql开发用户注册登录功能_mysql用户注册登录数据库设计  抖音号显示企业机构号是什么意思?企业机构号申请条件是什么?  《健康大兴》注册方法介绍  GBA模拟器手柄按键设置  J*aScript调试技巧_性能分析与内存快照  QQ邮箱PC端登录页面_QQ邮箱网页版登录界面  t3出行如何使用微信支付  《地下城堡4:骑士与破碎编年史》墓穴挑战125攻略  Python类装饰器动态修改方法时的类型提示:Mypy插件实现精确静态分析  win11关机几秒又自己开机 Win11关机自动重启问题修复  《三角洲行动》战斗步枪与机枪类改装代码分享  mysql离线安装后如何启动_mysql离线安装完成后启动服务的方法  国际经济与贸易就业方向解析  美发店速赢秘籍  《花瓣》创建专辑方法  创建快捷方式启动系统保护  sublime如何处理超大文件不卡顿 _sublime打开大日志文件技巧  猫眼电影app如何参与官方的抽奖活动_猫眼电影官方抽奖参与方法  Dagster资产间数据传递与用户配置管理教程  解决CSS background 属性中 cover 关键字的常见误用  Yandex浏览器官方入口_Yandex搜索引擎中文版  谷歌学术论文搜索引擎 谷歌学术官网入口论坛永久链接  《战地6》反作弊已成功拦截240万次作弊 发售第一周98%比赛没有作弊  苹果电脑如何快速查看电池状态 苹果电脑电池信息快捷方法  Linux如何开发轻量级数据服务模块_Linux服务化设计  PHP实现等比数列:构建数组元素基于前一个值递增的方法  菜鸟裹裹怎样获得取件码_菜鸟裹裹获得取件码步骤  Firefox OS应用开发:解决XMLHttpRequest跨域请求阻塞问题  《浙里办》电子发票开具方法  手机自动关机是怎么回事?如何修复?手机异常关机的原因排查与修复技巧  重返未来:1999卡戎全方位攻略  菜鸟驿站的取件码忘了怎么办 手机快速查询指南  《下一站江湖2》大雪山加入方法  创客贴登录页面入口 创客贴网页版最新网址链接  mysql数据库索引类型有哪些_mysql索引类型解析  怎么恢复删除的电脑文件_数据恢复软件使用教程  Win10关闭UAC用户账户控制的方法 Win10降低安全提示等级【技巧】  QQ阅读小说搜索入口地址_QQ阅读小说搜索入口地址搜索在线阅读  XPath动态元素定位:如何精准选择文本内容变化的元素  魔法祈幻界兑换码礼包大全  Golang如何使用crypto/md5生成哈希_Golang MD5哈希生成方法  《雷电模拟器》截图方法介绍  word文档行距怎么调?word文档调行距的操作步骤  处理含命名空间的XML文件 Power Query中的高级技巧  J*a实现任务清单管理_集合框架综合入门练手  《书耽》更换手机号方法  知音漫客官网首页入口_知音漫客热门漫画推荐  小红书网页版首页入口 小红书网页版电脑端官方登录链接  Win10共享文件夹设置方法 Win10局域网文件共享全攻略【教程】 

 2020-10-06

了解您产品搜索量及市场趋势,制定营销计划

同行竞争及网站分析保障您的广告效果

点击免费数据支持

提交您的需求,1小时内享受我们的专业解答。

运城市盐湖区信雨科技有限公司


运城市盐湖区信雨科技有限公司

运城市盐湖区信雨科技有限公司是一家深耕海外推广领域十年的专业服务商,作为谷歌推广与Facebook广告全球合作伙伴,聚焦外贸企业出海痛点,以数字化营销为核心,提供一站式海外营销解决方案。公司凭借十年行业沉淀与平台官方资源加持,打破传统外贸获客壁垒,助力企业高效开拓全球市场,成为中小企业出海的可靠合作伙伴。

 8156699

 13765294890

 8156699@qq.com

Notice

We and selected third parties use cookies or similar technologies for technical purposes and, with your consent, for other purposes as specified in the cookie policy.
You can consent to the use of such technologies by closing this notice, by interacting with any link or button outside of this notice or by continuing to browse otherwise.