面试官:: 那一般数据库有哪些锁? 一般怎么使用?
此刻,用我那呆若木鸡的眼神看向面试官,内心实属尴尬+害怕,数据库不就是共享和互斥锁吗?
这样看来,是我太嫩。此处必有坑。殊不知此刻我内心已把你拿捏,定斩不饶。
吒吒辉: 数据库的锁根据不同划分方式有很多种说法,在业务访问上有以下两种:
- 排他锁
在访问共享资源之前对其进行加锁,在访问完成后进行解锁操作。 加锁成功后,任何其它线程请求来获取锁都会被阻塞,直到当前线自行释放锁。
线程3状态:就绪、阻塞、执行
如解锁时,有一个以上的线程阻塞(资源已释放),那么所有尝试获取该锁的线程都被CPU认为就绪状态, 如果第一个就绪状态的线程又执行加锁操作,那么其他的线程又会进入就绪状态。 在这种方式下,只能有一个线程访问被互斥锁保护的资源
故此,MySQL的SQL语句加了互斥锁后,只有接受到请求并获取锁的线程才能够访问和修改数据。 因为互斥锁是针对线程访问控制而不是请求本身。
- 共享锁
被加锁资源是可被共享的,但仅限于读请求。它的写请求只能被获取到锁的请求独占。 也就是加了共享锁的数据,只能够当前线程修改,其它线程只能读数据,并不能修改。
吒吒辉: 在 SQL 请求上可分为读、写锁。但本质还是对应对共享锁和排它锁。
面试官: 那 SQL 请求上不加锁怎么访问? 为啥说它们属于共享锁和排他锁? 这之间有何联系?
吒吒辉: 除加锁读外,还有一种不加锁读的情况。这种方式称为快照读,读请求加锁称为共享读。
针对请求加共享、排它锁的原因在于,读请求天生是幂等性的,不论你读多少次数据不会发生变化,所以给读请求加上锁就应该为共享锁。 不然怎么保证它的特点呢?
而写请求,本身就需对数据进行修改,所以就需要排它锁来保证数据修改的一致性。
吒吒辉: 如果按照锁的颗粒度划分看,就有表锁和行锁
- 表锁:
是MySQL中最基本的锁策略,并且是开销最小的策略。并发处理较少。表锁由MySQL服务或存储引擎管理。多数情况由服务层管理,具体看SQL操作。
例如:服务器会为诸如 ALTER TABLE 之类的语句使用表锁
,而忽略存储引擎的锁。
加锁机制:
它会锁定整张表。一个用户在对表进行写操作(插入、删除、更新等)前,需要先获得写锁,这会阻塞其他用户对该表的所有读写操作。只有没有写锁时,其他用户才能获取到读锁。
- 行锁:
锁定当前访问行的数据,并发处理能力很强。但锁开销最大。具体视行数据多少决定。由innoDB存储引擎支持。
- 页级锁:
页级锁是 MySQL 中锁定粒度介于行级锁和表级锁中间的一种锁。表级锁速度快,但冲突多,行级冲突少,但速度慢。因此,采取了折衷的页级锁,一次锁定相邻的一组记录。由BDB 存储引擎管理页级锁。
面试官: 为啥是表锁开销小,而不是行锁呢? 毕竟表锁锁定是整张表
吒吒辉: 表锁锁定的是表没错,但它不是把表里面所有的数据行都上锁,相当于是封锁了表的入口,这样它只是需要判断每个请求是否可以获取到表的锁,没有就不锁定。
而行锁是针对表的每一行数据,数据量一多,锁定内容就多,故开销大。 但因它颗粒度小,锁定行不会影响到别的行。所以并发就高。而如果表锁在一个入口就卡死了,那整体请求处理肯定就会下降。
面试官: 我记得行锁里面有几种不同的实现方式,你知道吗?
您可真贴心啊,替我考虑这么多,大佬都是这么心比针细? 我要是说不知道,你老是不是又准备给出穿小鞋啦。强忍内心啃人的冲动
ps:读懂图,说明你有故事
吒吒辉: innodb虽支持行锁,但锁实现的算法却和SQL的查询形式有关系:
- Record Lock(记录锁):单个行记录上的锁。也就是我们日常认为的行锁。由
where =
的形式触发
- Gap Lock(间隙锁):间隙锁,锁定一个范围,但不包括记录本身(它锁住了某个范围内的多个行,包括根本不存在的数据)。
GAP锁的目的,是为了防止事务插入而导致幻读的情况。该锁只会在隔离级别是RR或者以上的级别内存在。间隙锁的目的是为了让其他事务无法在间隙中新增数据。 SQL里面用 where >、>=等范围条件触发,但会根据锁定的范围内,是否包含了表中真实存在的记录进行变化,如果存在真实记录就会进化为 临建锁。反之就为间隙所。
- Next-Key Lock(临键锁):它是记录锁和间隙锁的结合,锁定一个范围,并且锁定记录本身。对于行的查询,都是采用该方法,主要目的是解决幻读的问题。next-key 锁是InnoDB默认的。是一个左开右闭的规则
- IS锁:意向共享锁、Intention Shared Lock。当事务准备在某条记录上加S(读)锁时,需要先在表级别加一个IS锁。
- IX锁:意向排它锁、Intention Exclusive Lock。当事务准备在某条记录上加X(写)锁时,需要先在表级别加一个IX锁。
面试官: 那这个东西是怎么实现的?
t(id PK, name KEY, sex, flag);
表中有四条记录:
1, zhazhahui, m, A
3, nezha, m, A
5, lisi, m, A
9, wangwu, f, B
- 记录锁
select * from t where id=1 for update;
锁定 id =1的记录
- 间隙锁
select * from t where id > 3 and id < 9 ;
锁定(3,5],(5,9)范围的值,因为当前访问3到9的范围记录,就需要锁定表里面已经存在的数据来解决幻读和不可重复读的问题
- 临建锁
select * from t where id >=9 ;
会锁定 [9,+∞) 。查询会先选中 9 号记录,所以锁定范围就以9开始到正无穷数据。
面试官: 那意向排它、共享锁呢?是怎么个内容
吒吒辉: 意向排它锁和意向共享锁,是针对当前SQL请求访问数据行时,会提前进行申请访问,如果最终行锁未命中就会退化为该类型的表锁。
面试官: 那有这个意向排它锁有什么好处呢?
吒吒辉: 可提前做预判,每次尝试获取行锁之前会检查是否有表锁,如果存在就不会继续申请行锁,从而减少锁的开销。从而整个表就退化为表锁。
面试官: 那你动手给我演示下每个场景
嗯。。。(瞳孔放大2倍)我这不说的很明白吗?
难道故意和作对,这是干嘛啊。欺负人嘛不是
只见那面试官忽然翘起来二郎腿,还有节拍的抖动着腿,看向我。一看就是抖音整多了
哎,没办法 官大以及压死人。打碎了牙齿自己咽。你给我看细细看好了,最好眼睛都别眨
吒吒辉: 因为锁就是解决事务并发的问题,所以记录锁就不演示了,直接游荡在间隙和临建锁里面。
建立语句:
CREATE TABLE t1
(
id
int(10) NOT NULL AUTO_INCREMENT,
name
varchar(64) COLLATE utf8mb4_general_ci DEFAULT NULL,
age
tinyint(3) unsigned DEFAULT NULL,
PRIMARY KEY (id
)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
表数据:
间隙锁:
- 关闭 MySQL 默认的事务自动提交机制。关闭前:
- 关闭后:
加锁:
直接插入 >8 的数据就阻塞,都会上锁。为的就解决插入新数据而导致幻读。
【啊!幻读不知道呀。下篇文章给大家安排上】
面试官: 你这条件不是>=8吗? 那等于8呢? 被吃辣?
吒吒辉: 别着急嘛,这不还没说完吗。为什么不指定8呢?
因为 >=8 的条件会从间隙锁升级为临建锁,因为你条件里面包含了 8 这个真实存在的数据。所以会把它锁起来。如下:
所以,最终的行锁会和SQL语句的条件触发有关系,一旦范围查询包含了数据库里面真实存在数据,就会升级为临建锁。不要问我为什么? 看前面的定义
面试官独白:这小伙多少看来还有有点货,不错。此刻面试官露出一丝笑容。殊不知他内心又开酝酿起了新的想法。就等我入瓮
面试官: 那什么场景下行锁不会生效呢?锁 锁定的又是什么?
此刻,我呆了,这都什么跟什么啊。不带这么玩的吧。天杀的,净使坏
锁的触发机制
吒吒辉:
innodb的行锁是根据索引触发,如果没有相关的索引,那行锁将会退化成表锁(即锁定整个表里的行)。
而 锁 锁定的是索引即索引树里面的数据库字段的值。
- id为主键索引字段。
- 给 age 字段上锁
- age 字段没索引,退化成表锁。直接查询将失败。
有索引,用索引字段查询可得数据,其余字段查询将失败。因为获取不到行锁,只能等待。而锁定的是索引,故此其它用其它索引值查询能拿查询数据
- 索引字段上锁
- 索引当前字段锁定,用其余索引字段可查询
- 不是索引字段都差不到。
总结
面试难免让人焦虑不安。经历过的人都懂的。但是如果你提前预测面试官要问你的问题并想出得体的回答方式,就会容易很多。
此外,都说“面试造火箭,工作拧螺丝”,那对于准备面试的朋友,你只需懂一个字:刷!
给我刷刷刷刷,使劲儿刷刷刷刷刷!今天既是来谈面试的,那就必须得来整点面试真题,这不花了我整28天,做了份“Java一线大厂高岗面试题解析合集:JAVA基础-中级-高级面试+SSM框架+分布式+性能调优+微服务+并发编程+网络+设计模式+数据结构与算法等”
且除了单纯的刷题,也得需准备一本【JAVA进阶核心知识手册】:JVM、JAVA集合、JAVA多线程并发、JAVA基础、Spring 原理、微服务、Netty与RPC、网络、日志、Zookeeper、Kafka、RabbitMQ、Hbase、MongoDB、Cassandra、设计模式、负载均衡、数据库、一致性算法、JAVA算法、数据结构、加密算法、分布式缓存、Hadoop、Spark、Storm、YARN、机器学习、云计算,用来查漏补缺最好不过。
VM、JAVA集合、JAVA多线程并发、JAVA基础、Spring 原理、微服务、Netty与RPC、网络、日志、Zookeeper、Kafka、RabbitMQ、Hbase、MongoDB、Cassandra、设计模式、负载均衡、数据库、一致性算法、JAVA算法、数据结构、加密算法、分布式缓存、Hadoop、Spark、Storm、YARN、机器学习、云计算,用来查漏补缺最好不过。
[外链图片转存中…(img-GgbhIf4v-1714427362841)]