干货总结:彻底搞懂MySQL数据库锁机制(上篇)
mhr18 2024-10-04 17:20 29 浏览 0 评论
本文是对数据库锁机制的总结。对于数据库锁的了解,是区分一个程序员,尤其是Java程序员中高级的重要标志。在日常开发中,数据库锁也是我们容易踩坑的地方。如果你的系统没有被高并发大访问量流量洗礼,可能你压根没有遇到过锁问题。可一旦有朝一日你有幸遇到了大流量,恰你不幸没经受住这波幸福的考验,那损失不是一般的小,说不定领导会找你喝茶。好了,说了这些,为了让你明白锁机制的重要性,接下来我们一步步体验MySQL的锁机制之旅。
一、什么是锁
二、锁的分类
三、事务隔离级别
四、MVCC
五、一致性非锁定读与一致性锁定读
六、 InnoDB锁的类型
七、 InnoDB行锁几种算法
八、测试case
九、 特殊case
十、锁的兼容性
十一、死锁
1、什么是死锁
2、不同行互相操作一个例子
3、锁兼容性的一个死锁例子
鉴于篇幅,本文仅涉及提纲一至五部分,其余留给后续系列文章介绍。
一、什么是锁
锁机制是用于管理对共享资源的并发访问。
在数据库中,lock与latch都可以被称为锁。latch 一般称为闩锁(轻量级的锁) 因为其要求锁定的时间非常短, lock的对象是事务,用来锁定的是数据库中的对象,如表、页、行。
latch一般用于锁住事务号,内存等一些资源。而lock一般用于锁住记录,latch基本用户感知不到,lock可能是我们需要经常感知到的,下面介绍的锁为lock锁。
二、 锁的分类
MySQL存在多种不同的引擎,各种不同引擎类型面对的使用场景不一样,不同引擎之间的锁粒度可能有差别。例如InnoDB能支持行级锁,MyISAM支持表级锁,BDB支持页级锁,他们的特定如下:
1: 行级锁具有较高的并发性,比较适合并发度较高的场景,同时也更耗费资源。 但是如果锁住大量的记录,会比页级锁或者表级锁更慢,需要获取大量锁资源。
2:表级锁消耗资源更少,但是并发读较低。比较适合需要经常锁住大量数据的场景。
3:页级锁界于行锁以及表级锁之间,但是在无法解决热点数据的并发问题。
接下来分别创建两张不同引擎的表,分别演示分别在myisam以及InnoDB大量数据插入的情况下,另一个会话更新一条数据的block状态。以及锁类型,模拟过程为:
(1):创建一张myisam(myisam_lock_test)或者是InnoDB引擎的表(innodb_lock_test)。
(2):开启一个会话sessionA,插入一条数据。
(3):使用load指令进行大量数据插入。
(4):开启另一个会话更新步骤二刚插入的数据。
表结构均为:
myisam测试
sessionA:
load data infile '/Users/it2eye/mysql_lock/create/data.txt' into table myisam_lock_test(name);
sessionB:
update myisam_lock_test set name='123' where id=1;
分别执行上述两条语句后,update一直阻塞住,并且等待表锁
InnoDB测试
sessionA:
load data infile '/Users/it2eye/mysql_lock/create/data.txt' into table innodb_lock_test(name);
sessionB:
update innodb_lock_test set name='123' where id=1;
执行语句后,update并不会阻塞并且很快执行完毕
三、事务隔离级别
数据库事务的隔离级别有4种,由低到高分别为:
1、Read uncommitted (读未提交)
读未提交,顾名思义,就是一个事务可以读取另一个未提交事务的数据。事务中的修改,即使没有提交,对其它事务也是可见的。这种现象也被称为脏读。
事例:老板要给程序员发工资,程序员的工资是3.6万/月。但是发工资时老板不小心按错了数字,按成3.9万/月,该钱已经打到程序员的户口,但是事务还没有提交,就在这时,程序员去查看自己这个月的工资,发现比往常多了3千元,以为涨工资了非常高兴。但是老板及时发现了不对,马上回滚差点就提交了的事务,将数字改成3.6万再提交。
分析:实际程序员这个月的工资还是3.6万,但是程序员看到的是3.9万。他看到的是老板还没提交事务时的数据。这就是脏读。
那怎么解决脏读呢?Read committed!读提交,能解决脏读问题。
2、Read committed(读已提交)
读提交,顾名思义,就是一个事务要等另一个事务提交后才能读取数据。一个事务只能看到已提交的事务修改,换句话说,一个事务从开始到提交之前,所做的任何修改对其它事务都是可见的。
事例:程序员拿着信用卡去享受生活(卡里当然是只有3.6万),当他埋单时(程序员事务开启),收费系统事先检测到他的卡里有3.6万,就在这个时候!!程序员的妻子要把钱全部转出充当家用,并提交。当收费系统准备扣款时,再检测卡里的金额,发现已经没钱了(第二次检测金额当然要等待妻子转出金额事务提交完)。程序员就会很郁闷,明明卡里是有钱的…
分析:这就是读提交,若有事务对数据进行更新(UPDATE)操作时,读操作事务要等待这个更新操作事务提交后才能读取数据,可以解决脏读问题。但在这个事例中,出现了一个事务范围内执行两次同样的查询却返回了不同结果,这个级别也叫不可重复读。
那怎么解决可能的不可重复读问题?下面第3种Repeatable read !
3、Repeatable read(可重复读)
重复读,就是在开始读取数据(事务开启)时,不再允许修改操作。该级别保证了在一个事务中多次读取同样的记录的结果是一致的。但还是没解决幻读(Phantom Read)的问题(下面有举例介绍)。该级别是MySQL的默认事务隔离级别。
事例:程序员拿着信用卡去享受生活(卡里当然是只有3.6万),当他埋单时(事务开启,不允许其他事务的UPDATE修改操作),收费系统事先检测到他的卡里有3.6万。这个时候他的妻子不能转出金额了。接下来收费系统就可以扣款了。说明可重复读可以解决不可重复读问题。
什么时候会出现幻读?
事例:程序员某一天去消费,花了2千元,然后他的妻子去查看他今天的消费记录(全表扫描FTS,妻子事务开启),看到确实是花了2千元,就在这个时候,程序员花了1万买了一部电脑,即新增INSERT了一条消费记录,并提交。当妻子打印程序员的消费记录清单时(妻子事务提交),发现花了1.2万元,似乎出现了幻觉,这就是幻读。
现在你明白了,所谓幻读:指的是当某个事务在读取某个范围的记录时,另一个事务又在该范围内插入了新的记录,当之前事务再次读取该范围的记录时,会产生幻行(Phantom Row)。不过MySQL的InnoDB存储引擎可以通过gap锁解决幻读的问题。
分析:可重复读可以解决不可重复读问题。到这里,你应该豁然开朗了,不可重复读对应的是修改,即UPDATE操作。但是可能还会有幻读问题。因为幻读问题对应的是插入INSERT操作,而不是UPDATE操作。
那怎么解决幻读问题?Serializable!
4、Serializable (可串行化)
这是最高的隔离级别。在该级别下,强制事务串行化顺序执行,可以避免脏读、不可重复读与幻读。但是这种事务隔离级别效率低下,比较耗数据库性能,一般不使用。Serializable 会在读取的每一行数据上都加锁,可能导致大量的超时和锁争用的问题。
值得一提的是:大多数数据库默认的事务隔离级别是Read committed,比如Sql Server , Oracle。Mysql的默认隔离级别是Repeatable read。
提交读的幻读问题
会话A
会话B插入数据
会话A出现幻读问题(将满足index_key为1的记录的no_index_key更新为2,但是执行查询发现仍然有一条数据未进行修改,就像发生幻觉一样)
可重复读gap锁解决幻读问题
会话A
会话b执行插入操作被block住后重启事务
执行select * from information_schema.innodb_locks;
可以看到锁类型为x锁,以及gap锁,后面系列文章将详细介绍下InnoDB行锁算法。
四、 MVCC
大部分的MySQL的存储 引擎,比如InnoDB并不是简简单单的使用行锁机制。它们都使用了行锁结合一种提高并发的技术,被称为MVCC(多版本并发控制)。MVCC并不单单应用在MySQL中,其他的数据库如Oracle,PostgreSQL也使用这个技术。
MVCC避免了许多需要加锁的情形以及降低消耗。这取决于它实现的方式,它允许非阻塞读取,在写的操作的时候阻塞必要的记录。
MVCC保存了某一时刻数据的一个快照。意思就是无论事物运行了多久,它们都能看到一致的数据。也就是说在相同的时间下,不同的事物看相同表的数据是不同的。
每个存储引擎实现MVCC的方式不同。有许多种包含了乐观(optimistic)和悲观(pessimistic)的并发控制。下面用InnoDB的行为来举例说明MVCC工作方式。
InnoDB实现MVCC的方法是,它存储了每一行的两个(1)额外的隐藏字段,这两个隐藏字段分别记录了行的创建的时间和删除的时间。在每个事件发生的时候,每行存储版本号,而不是存储事件实际发生的时间。每次事物的开始这个版本号都会增加。自记录时间开始,每个事物都会保存记录的系统版本号。依照事物的 版本来检查每行的版本号。
SELECT
InnoDB检查每行数据,确保他们符合两个标准:
1、InnoDB只查找版本早于当前事务版本的数据行(也就是数据行的版本必须小于等于事务的版本),这确保当前事务读取的行都是事务之前已经存在的,或者是由当前事务创建或修改的行。
2、行的删除操作的版本一定是未定义的或者大于当前事务的版本号。确定了当前事务开始之前,行没有被删除。
符合了以上两点则返回查询结果。
INSERT
InnoDB为每个新增行记录当前系统版本号作为创建ID。
DELETE
InnoDB为每个删除行的记录当前系统版本号作为行的删除ID。
UPDATE
InnoDB复制了一行。这个新行的版本号使用了系统版本号。它也把系统版本号作为了删除行的版本。
对于InnoDB可重复读来说
开启一个事务后,第一次进行查询操作。会创建一个当前快照读的一个数据结构,readView,主要包含三方面的内容,当前活动事务的最小事务号up_limit_id,当前活动事务的最大事务号low_limit_id,以及非自身的其它活动事务id组成的数组,trx_ids[],
注意的是,上述的当前事务版本号是当前活动事务号的版本。
五、 一致性非锁定读与一致性锁定读
一致性非锁定读
指InnoDB存储引擎通过行多版本控制(multi versioning)的方式来读取当前执行时间数据库中行的数据,快照读,读一般不加行级lock锁。
一致性锁定读
需要加锁显式的保证数据的一致性。读需要加lock锁
select **** for update
select **** lock in share mode
insert….where
update *** where…..
我是一名简单可靠、纯洁可爱的分享者,若您喜欢我的文章,欢迎转发,也欢迎关注我。
我会经常分享让你喜欢的互联网技术知识。
相关推荐
- 一文读懂Prometheus架构监控(prometheus监控哪些指标)
-
介绍Prometheus是一个系统监控和警报工具包。它是用Go编写的,由Soundcloud构建,并于2016年作为继Kubernetes之后的第二个托管项目加入云原生计算基金会(C...
- Spring Boot 3.x 新特性详解:从基础到高级实战
-
1.SpringBoot3.x简介与核心特性1.1SpringBoot3.x新特性概览SpringBoot3.x是建立在SpringFramework6.0基础上的重大版...
- 「技术分享」猪八戒基于Quartz分布式调度平台实践
-
点击原文:【技术分享】猪八戒基于Quartz分布式调度平台实践点击关注“八戒技术团队”,阅读更多技术干货1.背景介绍1.1业务场景调度任务是我们日常开发中非常经典的一个场景,我们时常会需要用到一些不...
- 14. 常用框架与工具(使用的框架)
-
本章深入解析Go生态中的核心开发框架与工具链,结合性能调优与工程化实践,提供高效开发方案。14.1Web框架(Gin,Echo)14.1.1Gin高性能实践//中间件链优化router:=...
- SpringBoot整合MyBatis-Plus:从入门到精通
-
一、MyBatis-Plus基础介绍1.1MyBatis-Plus核心概念MyBatis-Plus(简称MP)是一个MyBatis的增强工具,在MyBatis的基础上只做增强不做改变,为简化开发、提...
- Seata源码—5.全局事务的创建与返回处理
-
大纲1.Seata开启分布式事务的流程总结2.Seata生成全局事务ID的雪花算法源码3.生成xid以及对全局事务会话进行持久化的源码4.全局事务会话数据持久化的实现源码5.SeataServer创...
- Java开发200+个学习知识路线-史上最全(框架篇)
-
1.Spring框架深入SpringIOC容器:BeanFactory与ApplicationContextBean生命周期:实例化、属性填充、初始化、销毁依赖注入方式:构造器注入、Setter注...
- OpenResty 入门指南:从基础到动态路由实战
-
一、引言1.1OpenResty简介OpenResty是一款基于Nginx的高性能Web平台,通过集成Lua脚本和丰富的模块,将Nginx从静态反向代理转变为可动态编程的应用平台...
- 你还在为 Spring Boot3 分布式锁实现发愁?一文教你轻松搞定!
-
作为互联网大厂后端开发人员,在项目开发过程中,你有没有遇到过这样的问题:多个服务实例同时访问共享资源,导致数据不一致、业务逻辑混乱?没错,这就是分布式环境下常见的并发问题,而分布式锁就是解决这类问题的...
- 近2万字详解JAVA NIO2文件操作,过瘾
-
原创:小姐姐味道(微信公众号ID:xjjdog),欢迎分享,转载请保留出处。从classpath中读取过文件的人,都知道需要写一些读取流的方法,很是繁琐。最近使用IDEA在打出.这个符号的时候,一行代...
- 学习MVC之租房网站(十二)-缓存和静态页面
-
在上一篇<学习MVC之租房网站(十一)-定时任务和云存储>学习了Quartz的使用、发邮件,并将通过UEditor上传的图片保存到云存储。在项目的最后,再学习优化网站性能的一些技术:缓存和...
- Linux系统下运行c++程序(linux怎么运行c++文件)
-
引言为什么要在Linux下写程序?需要更多关于Linux下c++开发的资料请后台私信【架构】获取分享资料包括:C/C++,Linux,Nginx,ZeroMQ,MySQL,Redis,fastdf...
- 2022正确的java学习顺序(文末送java福利)
-
对于刚学习java的人来说,可能最大的问题是不知道学习方向,每天学了什么第二天就忘了,而课堂的讲解也是很片面的。今天我结合我的学习路线为大家讲解下最基础的学习路线,真心希望能帮到迷茫的小伙伴。(有很多...
- 一个 3 年 Java 程序员 5 家大厂的面试总结(已拿Offer)
-
前言15年毕业到现在也近三年了,最近面试了阿里集团(菜鸟网络,蚂蚁金服),网易,滴滴,点我达,最终收到点我达,网易offer,蚂蚁金服二面挂掉,菜鸟网络一个月了还在流程中...最终有幸去了网易。但是要...
- 多商户商城系统开发全流程解析(多商户商城源码免费下载)
-
在数字化商业浪潮中,多商户商城系统成为众多企业拓展电商业务的关键选择。这类系统允许众多商家在同一平台销售商品,不仅丰富了商品种类,还为消费者带来更多样的购物体验。不过,开发一个多商户商城系统是个复杂的...
你 发表评论:
欢迎- 一周热门
-
-
Redis客户端 Jedis 与 Lettuce
-
高并发架构系列:Redis并发竞争key的解决方案详解
-
redis如何防止并发(redis如何防止高并发)
-
开源推荐:如何实现的一个高性能 Redis 服务器
-
redis安装与调优部署文档(WinServer)
-
Redis 入门 - 安装最全讲解(Windows、Linux、Docker)
-
一文带你了解 Redis 的发布与订阅的底层原理
-
Redis如何应对并发访问(redis控制并发量)
-
oracle数据库查询Sql语句是否使用索引及常见的索引失效的情况
-
Java SE Development Kit 8u441下载地址【windows版本】
-
- 最近发表
- 标签列表
-
- oracle位图索引 (63)
- oracle批量插入数据 (62)
- oracle事务隔离级别 (53)
- oracle 空为0 (50)
- oracle主从同步 (55)
- oracle 乐观锁 (51)
- redis 命令 (78)
- php redis (88)
- redis 存储 (66)
- redis 锁 (69)
- 启动 redis (66)
- redis 时间 (56)
- redis 删除 (67)
- redis内存 (57)
- redis并发 (52)
- redis 主从 (69)
- redis 订阅 (51)
- redis 登录 (54)
- redis 面试 (58)
- 阿里 redis (59)
- redis 搭建 (53)
- redis的缓存 (55)
- lua redis (58)
- redis 连接池 (61)
- redis 限流 (51)