Java岗大厂面试百日冲刺 - 日积月累,每日三题【Day7,java开发中常见中间件面试

不敢苟同,相信大家和我一样,都有一个大厂梦,作为一名资深Java选手,深知面试重要性,接下来我准备用100天时间,基于Java岗面试中的高频面试题,以每日3题的形式,带你过一遍热门面试题及恰如其分的解答。当然,我不会太深入,因为我怕记不住!!

因此,不足的地方希望各位在评论区补充疑惑、见解以及面试中遇到的奇葩问法,希望这100天能够让我们有质的飞越,一起冲进大厂!!,让我们一起学(juan)起来!!!

车票

  • 面试题1:

    • 正经回答:先说一下什么是MySQL事务吧
  • 深入追问:

    • 追问1:说一下你对ACID四大特性的理解
  • 追问2:可以从原理上聊一下ACID具体是怎么实现的么?

  • 面试题2:并发场景下事务会存在哪些数据问题?

    • 正经回答:
  • 深入追问:

    • 追问1:那Innodb是如何解决幻读问题的呢?
  • 面试题3:说一下MySQL中你都知道哪些锁?

    • 正经回答:
  • 深入追问:

    • 追问1:那你来谈一谈你对表锁、行锁的理解吧。
  • 追问2:那全局锁是什么时候用的呢?

  • 追问2:那你再说一下按锁级别划分的那几种锁的使用场景和理解吧?

  • 每日小结


本栏目Java开发岗高频面试题主要出自以下各技术栈:Java基础知识集合容器并发编程JVMSpring全家桶MyBatis等ORMapping框架MySQL数据库Redis缓存RabbitMQ消息队列Linux操作技巧等。

面试题1:

====================================================================

正经回答:先说一下什么是MySQL事务吧


简单说,事务就是一组原子性的SQL执行单元。如果数据库引擎能够成功地对数据库应 用该组査询的全部语句,那么就执行该组SQL。如果其中有任何一条语句因为崩溃或其 他原因无法执行,那么所有的语句都不会执行。要么全部执行成功(commit),要么全部执行失败(rollback)

这里引用银行转账的例子,假设银行的数据库有两张表:信用卡(credit)表和储蓄(savings)表。用户陈哈哈要把信用卡里最后100块钱额度转到他 的储蓄账户用来吃饭,那么需要至少三个步骤:

  1. 检査信用卡余额是否髙于100块钱。

  2. 从信用卡账户余额中减去100块钱。

  3. 在储蓄账户余额中增加100块钱。

上述三个步骤必须在同一个事务中执行,任何一个SQL失败,则必须回滚所有的SQL。这里用START TRANSACTION语句开启事务,要么使用COMMIT提交事务将修改的数据持久保留,要么使用ROLLBACK销所有的修改。事务SQL的样本如下:

START TRANSACTION;

– 检查信用卡账户额度

SELECT balance FROM credit WHERE customer_id = ‘chenhh’;

– 信用卡表扣钱

UPDATE credit SET balance = balance - 100.00 WHERE customer_id = ‘chenhh’;

– 储蓄表加钱

UPDATE savings SET balance = balance + 100.00 WHERE customer_id = ‘chenhh’;

COMMIT;

试想一下,如果执行到第四条语句时服务器崩溃了,会发生什么?废话,我被坑了100块钱,中午只能饿肚子!再假如,在执行到第三条语句和第四 条语句之间时,同一时间,另外一个进程,来自商场结账的女朋友,也要信用卡账户的100块,那么结果可能就是银行在不知道这个逻辑的情况下白白给了陈哈哈女朋友100块钱?

在这里插入图片描述

深入追问:


追问1:说一下你对ACID四大特性的理解

该问题来自“MySQL江湖路”专栏中的博文:《数据库ACID四大特性到底为了啥,一文带你看通透》

ACID特性:原子性、一致性、隔离性、持久性

  • 原子性(Atomicity)

单个事务,为一个不可分割的最小工作单元,整个事务中的所有操作要么全部commit成功,要么全部失败rollback,对于一个事务来说,不可能只执行其中的一部分SQL操作,这就是事务的原子性。

  • 一致性(Consistency)

数据库总是从一个一致性的状态转换到另外一个一致性的状态。在前面的例子中, 一致性确保了,即使在执行第三、四条语句之间时系统崩潰,信用卡账户也不会损 失100块,因为事务最终没有提交,所以事务中所做的修改也不会保存到数据库中,保证数据一致性。

  • 隔离性(Isolation)

通常来说,一个事务所做的修改在最终提交以前,对其他事务是不可见的。在前面 的例子中,当执行完第三条语句、第四条语句还未开始时,此时有另外一个账户查询余额SQL开始运行,则其看到的信用卡账户的余额并没有被减去100元。后面我们讨论隔离级别(Isolation level)的时候,会发现为什么我们要说事务通常来说是不可见的

  • 持久性(Durability)

一旦事务提交,则其所做的修改就会永久保存到数据库中。此时即使系统崩溃,修改的数据也不会丢失。

事务的ACID特性可以确保银行不会弄丢你的钱。而在应用逻辑中,要实现这一点非常难, 甚至可以说是不可能完成的任务。一个兼容ACID的数据库系统,需要做很多复杂但可能用户并没有觉察到的工作,才能确保ACID的实现。

追问2:可以从原理上聊一下ACID具体是怎么实现的么?

对MySQL来说,逻辑备份日志(binlog)、重做日志(redolog)、回滚日志(undolog)、锁技术 + MVCC就是MySQL实现事务的基础

  • 原子性:通过undolog来实现。

  • 持久性:通过binlog、redolog来实现。

  • 隔离性:通过(读写锁+MVCC)来实现。

  • 一致性:MySQL通过原子性,持久性,隔离性最终实现(或者说定义)数据一致性。

1、原子性原理

事务通常是以BEGIN TRANSACTION 开始,以 COMMIT 或 ROLLBACK 结束。

  • COMMIT 表示提交,即提交事务的所有操作并持久化到数据库中。

  • ROLLBACK表示回滚,即在事务中运行的过程中发生了某种故障,事务不能继续执行,系统将事务中对数据库所有已完成的操作全部撤销,回滚到事务开始时的状态,这里的操作指对数据库的更新操作,已执行的查询操作不用管。这时候也就需要用到 undolog 来进行回滚。

undolog:

  1. 每条数据变更(INSERT/UPDATE/DELETE/REPLACE)等操作都会生成一条undolog记录,在SQL执行前先于数据持久化到磁盘。

  2. 当事务需要回滚时,MySQL会根据回滚日志对事务中已执行的SQL做逆向操作,比如 DELETE 掉一行数据的逆向操作就是再把这行数据 INSERT回去,其他操作同理。

2、持久性原理

先了解一下MySQL的数据存储机制,MySQL的表数据是存放在磁盘上的,因此想要存取的时候都要经历磁盘 IO,然而即使是使用 SSD 磁盘 IO 也是非常消耗性能的。为此,为了提升性能 InnoDB 提供了缓冲池(Buffer Pool),Buffer Pool 中包含了磁盘数据页的映射,可以当做缓存来使用:

  • 读数据:会首先从缓冲池中读取,如果缓冲池中没有,则从磁盘读取在放入缓冲池;

  • 写数据:会首先写入缓冲池,缓冲池中的数据会定期同步到磁盘中;

我们知道,MySQL表数据是持久化到磁盘中的,但如果所有操作都去操作磁盘,等并发上来了,那处理速度谁都吃不消,因此引入了缓冲池(Buffer Pool)的概念,Buffer Pool 中包含了磁盘中部分数据页的映射,可以当做缓存来用;这样当修改表数据时,我们把操作记录先写到Buffer Pool中,并标记事务已完成,等MySQL空闲时,再把更新操作持久化到磁盘里(你可能会问,到底什么时候执行持久化呢?1、MySQL线程低于高水位;2、当有其他查询、更新语句操作该数据页时),从而大大缓解了MySQL并发压力。

但是它也带来了新的问题,当MySQL系统宕机,断电时Buffer Pool数据不就丢了?

因为我们的数据已经提交了,但此时是在缓冲池里头,还没来得及在磁盘持久化,所以我们急需一种机制需要存一下已提交事务的数据,为恢复数据使用。

于是 redo log + binlog的经典组合就登场了,这里不在扩展赘述。可参考《听我讲完redo log、binlog原理,面试官老脸一红》

3、隔离性原理

隔离性是事务ACID特性里最复杂的一个。在SQL标准里定义了四种隔离级别,每一种级别都规定一个事务中的修改,哪些是事务之间可见的,哪些是不可见的。

级别越低的隔离级别可以执行越高的并发,但同时实现复杂度以及开销也越大。

搞懂MySQL事务隔离级别请参考《上个厕所的功夫,搞懂MySQL事务隔离级别》

Mysql 隔离级别有以下四种(级别由低到高):

| 隔离级别 | 效果 |

| :-- | :-- |

| 读未提交(RU) | 一个事务还没提交时,它做的变更就能被别的事务看到。(别的事务指同一时间进行的增删改查操作) |

| 读提交(RC) | 一个事务提交(commit)之后,它做的变更才会被其他事务看到。 |

| 可重复读(RR) | 一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。

当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的。 |

| 串行(xíng)化(S) | 正如物理书上写的,串行是单线路,顾名思义在MySQL中同一时刻只允许单个事务执行,“写”会加“写锁”,“读”会加“读锁”。

当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。 |

搞懂了隔离级别以及实现原理其实就可以理解ACID里的隔离性了。前面说过原子性,隔离性,持久性的目的都是为了要做到一致性,但隔离型跟其他两个有所区别,原子性和持久性是为了要实现数据的正确、可用,比如要做到宕机后的恢复、事务的回滚等,保证数据是正确可用的!

那么隔离性是要做到什么呢?

隔离性要管理的是:多个并发读写请求(事务)过来时的执行顺序。像交警在马路口儿指挥交通一样,当并发处理多个DML更新操作时,如何让事务操作他该看到的数据,出现多个事务处理同一条数据时,让事务该排队的排队,别插队捣乱,保证数据和事务的相对隔离,这就是隔离性要干的事儿。

在这里插入图片描述

所以,从隔离性的实现原理上,我们可以看出这是一场数据的可靠性与性能之间的权衡。

4、一致性原理

一致性,我们要保障的是数据一致性,数据库中的增删改操作,使数据库不断从一个一致性的状态转移到另一个一致性的状态

事务该回滚的回滚,该提交的提交,提交后该持久化磁盘的持久化磁盘,该写缓冲池的写缓冲池+写日志;对于数据可见性,通过四种隔离级别进行控制,使得库表中的有效数据范围可控,保证业务数据的正确性的前提下,进而提高并发程度,支撑服务高QPS的稳定运行,保证数据的一致性,这就是咱们叨叨叨说的清楚想不明白的数据库ACID四大特性。


在这里插入图片描述

课间休息,欣赏一下来自咱们SQL大腿群同学的搬砖工地,坐标:杭州


面试题2:并发场景下事务会存在哪些数据问题?

=====================================================================================

正经回答:


并发场景下MySQL事务可能会出现脏读、幻读、不可重复读问题;

  • 脏读(Drity Read):某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。

  • 不可重复读(Non-repeatable read):在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新了原有的数据。

  • 幻读(Phantom Read):在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。

深入追问:


追问1:那Innodb是如何解决幻读问题的呢?

先说结论,MySQL 存储引擎 InnoDB 在可重复读(RR)隔离级别下是解决了幻读问题的。

方法是通过next-key lock在当前读事务开启时,1.给涉及到的行加写锁(行锁)防止写操作;2.给涉及到的行两端加间隙锁(Gap Lock)防止新增行写入;从而解决了幻读问题。

幻读出现的场景:

  • 幻读出现在可重复读(RR)隔离级别下,普通的SELECT查询就是快照读,是不会看到别的事务插入的数据的。因此,幻读在“当前读”下才会出现。(当前读会生成行锁,但行锁只能锁定存在的行,针对新插入的操作没有限定)
  • 上面 session B 的修改结果,被 session A 之后的 select 语句用“当前读”看到,不能称为幻读。幻读仅专指“新插入的行”。
  • 因为这三个查询都是加了 for update,都是当前读。而当前读的规则,就是要能读到所有已经提交的记录的最新值。并且,session B 和 sessionC 的两条语句,执行后就会提交,所以 Q2 和 Q3 就是应该看到这两个事务的操作效果,而且也看到了,这跟事务的可见性规则并不矛盾。

幻读场景实例:

测试表数据如下:

mysql> select * from LOL;

±—±-------------±-------------±------+

| id | hero_title | hero_name | price |

±—±-------------±-------------±------+

| 1 | 刀锋之影 | 泰隆 | 6300 |

| 2 | 迅捷斥候 | 提莫 | 6300 |

| 3 | 光辉女郎 | 拉克丝 | 1350 |

| 4 | 发条魔灵 | 奥莉安娜 | 6300 |

| 5 | 至高之拳 | 李青 | 6300 |

| 6 | 无极剑圣 | 易 | 450 |

| 7 | 疾风剑豪 | 亚索 | 6300 |

±—±-------------±-------------±------+

7 rows in set (0.00 sec)

下面是一个出现幻读情况的示例流程:

| 时刻T | Session A | Session B | Session C |

| — | :-- | — | — |

| T1 | begin;

– Query1

select * from LOL where price=450 for update;

Result:(6,‘无极剑圣’,450) | | |

| T2 | | update LOL set price=450 where hero_title = ‘疾风剑豪’; | |

| T3 | – Query2

select * from LOL where price=450 for update;

Result:(6,‘无极剑圣’,450),(7,‘疾风剑豪’,450) | | |

| T4 | | | insert into LOL values(10,‘雪人骑士’,‘努努’,‘450’); |

| T5 | – Query3

select * from LOL where price=450 for update;

Result:(6,‘无极剑圣’,450),(7,‘疾风剑豪’,450),(10,‘雪人骑士’,450) | | |

| T6 | commit; | | |

可以看到,session A 里执行了三次查询,分别是 Q1、Q2 和 Q3。它们的 SQL 语句相同,都是 select * from LOL where price=450 for update。这个语句的意思你应该很清楚了,查所有 price=450 的行,而且使用的是当前读,并且加上写锁。现在,我们来看一下这三条 SQL 语句,分别会返回什么结果。

  1. Q1 只返回 “无极剑圣” 这一行;

  2. 在 T2 时刻,session B 把 “疾风剑豪” 这一行的 price 值改成了 450,因此 T3 时刻 Q2 查出来的是 “无极剑圣” 和 “疾风剑豪” 这两行;

  3. 在 T4 时刻,session C 又插入一行 (1

《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》

【docs.qq.com/doc/DSmxTbFJ1cmN1R2dB】 完整内容开源分享

0,‘雪人骑士’,‘努努’,‘450’),因此 T5 时刻 Q3 查出来 price = 450 的是"无极剑圣" 、“疾风剑豪” 和 “雪人骑士” 这三行。

其中,Q3 读到 (10,‘雪人骑士’,450) 这一行的现象,被称为“幻读”。也就是说,幻读指的是一个事务在前后两次查询同一个范围的时候,后一次查询看到了前一次查询没有看到的行。

解决幻读原理分析

如果你看到了这里,那么我会默认你了解了脏读 、不可重复读与可重复读。如果还不清楚可以先参阅《上个厕所的功夫,搞懂MySQL事务隔离级别》

场景如上,场景隔离级别为RR,当前读。

一、原理解读

那么幻读能仅通过行锁解决么?答案是否定的,如上面示例,首先说明一下,select xx for update(当前读)是将所有条件涉及到的(符合where条件)行加上行锁。但是,就算我在select xx for update 事务开启时将所有的行都加上行锁。那么也锁不住Session C新增的行,因为在我给数据加锁的时刻,压根就还没有新增的那行,自然也不会给新增行加上锁。

所以要解决幻读,就必须得解决新增行的问题。

现在你应该明白了,产生幻读的原因是:行锁只能锁住行,但是新插入记录这个动作,要更新的是记录之间的“间隙”。因此,为了解决幻读问题,InnoDB 只好引入新的锁,也就是间隙锁 (Gap Lock)。顾名思义,间隙锁,锁的就是两个值之间的空隙。比如文章开头的表 LOL,初始化插入了 7 个记录,这就产生了 8 个间隙。

二、next-key lock

这样,当你执行 select * from LOL where hero_title = ‘疾风剑豪’ for update 的时候,就不止是给数据库中已有的 7 个记录加上了行锁,还同时加了 8 个间隙锁。这样就确保了无法再插入新的记录,也就是Session C在T4新增(10,‘雪人骑士’,‘努努’,‘450’) 行时,由于ID大于7,被间隙锁(7,+∞)锁住。

在一行行扫描的过程中,不仅将给行加上了行锁,还给行两边的空隙,也加上了间隙锁。MySQL将行锁 + 间隙锁组合统称为 next-key lock,通过 next-key lock 解决了幻读问题。

注意:

next-key lock的确是解决了幻读问题,但是next-key lock在并发情况下也经常会造成死锁。死锁检测和处理也会花费时间,一定程度上影响到并发量。



面试题3:说一下MySQL中你都知道哪些锁?

=====================================================================================

该问题取自“MySQL江湖路”专栏中的博文:《面试让HR都能听懂的MySQL锁机制,欢声笑语中搞懂MySQL锁》

正经回答:


在这里插入图片描述

  1. 按锁粒度从大到小分类:表锁页锁行锁;以及特殊场景下使用的全局锁

  2. 如果按锁级别分类则有:共享(读)锁排他(写)锁意向共享(读)锁意向排他(写)锁

  3. 以及Innodb引擎为解决幻读等并发场景下事务存在的数据问题,引入的Record Lock(行记录锁)Gap Lock(间隙锁)Next-key Lock(Record Lock + Gap Lock结合)等;

  4. 还有就是我们面向编程的两种锁思想:悲观锁、乐观锁。

你可能感兴趣的:(程序员,面试,java,后端)