大家好,我是二哥呀。
上次发了
美团的开奖信息
后,留言区有读者说,小红书那里爆料说美团今年的薪资开得非常高,30-40k,而我这里“只有” 25k,有点不知道该信哪一个了。
那另外一位热心的读者就回复了,30-40 是校招开发岗的顶薪,也就是常说的 SSP,25k 比较常见,21-23 算是大白菜。
甚至很多小伙伴感慨到,说好的开水团呢?怎么 SSP 开到了令人咋舌的 33k*15.5,外加 8w 的签字费和总价值 30w 的股票。
这不就是传说中的“有鹅选团,无鹅延毕”,哦不,“有团选团,无团延毕”嘛(😂)
星球里也有不少球友拿到过美团的 offer,团子秋招开启的早,开奖的早,挺适合作为第一批冲击大厂 offer 的目标公司。
相信看到这里的很多小伙伴对美团已经心动了,那接下来,我们就以
Java 面试指南
中收录的同学 15 美团点评后端技术面试为例来看看,明年暑期实习或者秋招冲秋招的时候该怎么准备。
背八股就认准三分恶的面渣逆袭
2、三分恶面渣逆袭在线版:https://javabetter.cn/sidebar/sanfene/nixi.html
同学 15 美团点评后端技术面试
问了一下mysql的锁和MVCC
按锁粒度划分的话,MySQL 的锁有:
表锁:开销小,加锁快;锁定力度大,发生锁冲突概率高,并发度最低;不会出现死锁。
行锁:开销大,加锁慢;会出现死锁;锁定粒度小,发生锁冲突的概率低,并发度高。
页锁:开销和加锁速度介于表锁和行锁之间;会出现死锁;锁定粒度介于表锁和行锁之间,并发度一般。
MVCC 是多版本并发控制(Multi-Version Concurrency Control)的简称,主要用来解决数据库并发问题。
在支持 MVCC 的数据库中,当多个用户同时访问数据时,每个用户都可以看到一个在某一时间点之前的数据库快照,并且能够无阻塞地执行查询和修改操作,而不会相互干扰。
在传统的锁机制中,如果一个事务正在写数据,那么其他事务必须等待写事务完成才能读数据,MVCC 允许读操作访问数据的一个旧版本快照,同时写操作创建一个新的版本,这样读写操作就可以并行进行,不必等待对方完成。
在 MySQL 中,特别是 InnoDB 存储引擎,MVCC 是通过版本链和 ReadView 机制来实现的。
bufferpool
Buffer Pool 是 InnoDB 存储引擎中的一个内存缓冲区,它会将数据以页(page)的单位保存在内存中,当查询请求需要读取数据时,优先从 Buffer Pool 获取数据,避免直接访问磁盘。
图片来源于网络
也就是说,即便我们只访问了一行数据的一个字段,InnoDB 也会将整个数据页加载到 Buffer Pool 中,以便后续的查询。
修改数据时,也会先在缓存页面中修改。当数据页被修改后,会在 Buffer Pool 中变为脏页。
脏页不会立刻写回到磁盘。
InnoDB 会定期将这些脏页刷新到磁盘,保证数据的一致性。通常采用改良的 LRU 算法来管理缓存页,也就是将最近最少使用的数据移出缓存,为新数据腾出空间。
极客时间:改良的 LRU 算法
Buffer Pool 能够显著减少对磁盘的访问,从而提升数据库的读写性能。
在调优方面,我们可以设置合理的 Buffer Pool 大小(通常为物理内存的 70%),并配置多个 Buffer Pool 实例(通过 innodb_buffer_pool_instances)来提升并发能力。此外,还可以通过调整刷新策略参数,比如 innodb_flush_log_at_trx_commit,来平衡性能和数据持久性。
索引的数据结构
MySQL 的默认存储引擎是 InnoDB,它采用的是 B+树索引,B+树是一种自平衡的多路查找树,和红黑树、二叉平衡树不同,B+树的每个节点可以有 m 个子节点,而红黑树和二叉平衡树都只有 2 个。
William Johnson:b+树
和 B 树不同,B+树的非叶子节点只存储键值,不存储数据,而叶子节点存储了所有的数据,并且构成了一个有序链表。
这样做的好处是,非叶子节点上由于没有存储数据,就可以存储更多的键值对,再加上叶子节点构成了一个有序链表,范围查询时就可以直接通过叶子节点间的指针顺序访问整个查询范围内的所有记录,而无需对树进行多次遍历。查询的效率会更高。
sql中使用like,如果遵循最左前缀匹配,查询是不是一定会用到索引
既然遵循最左前缀匹配,说明一定是联合索引,那么查询是一定会用到索引的。
但如果查询条件中的 like 通配符 % 出现在搜索字符串的开始位置,如
age = 18 and name LIKE '%xxx'
,MySQL 会先使用联合索引 age_name 找到 age 符合条件的所有行,然后再进行 name 字段的过滤。
二哥的java 进阶之路:联合索引前缀通配符
type: ref
表示使用索引查找匹配某个值的所有行。
二哥的java 进阶之路:6 行数据
rows: 3
表示预计扫描 3 行。
filtered: 16.67
表示在前面的 rows 中,大约有 16.67% 的行满足 WHERE 条件。
如果是后缀通配符,如
age = 18 and name LIKE 'xxx%'
,MySQL 会直接使用联合索引 age_name 找到所有符合条件的行。
二哥的java 进阶之路:联合索引后缀通配符
type 为 range,表示 MySQL 使用了索引范围扫描,
filtered 为 100.00%
,表示在扫描的行中,所有的行都满足 WHERE 条件。
问了一下volatile,讲了一下JMM和volatile怎么实现有序性和可见性
当一个变量被声明为 volatile 时,Java 内存模型会确保所有线程看到该变量时的值是一致的。
深入浅出 Java 多线程:Java内存模型
也就是说,当线程对 volatile 变量进行写操作时,JMM 会在写入这个变量之后插入一个 Store-Barrier(写屏障)指令,这个指令会强制将本地内存中的变量值刷新到主内存中。
三分恶面渣逆袭:volatile写插入内存屏障后生成的指令序列示意图
当线程对 volatile 变量进行读操作时,JMM 会插入一个 Load-Barrier(读屏障)指令,这个指令会强制让本地内存中的变量值失效,从而重新从主内存中读取最新的值。
三分恶面渣逆袭:volatile写插入内存屏障后生成的指令序列示意图
例如,我们声明一个 volatile 变量 x:
volatile int x = 0
线程 A 对 x 写入后会将其最新的值刷新到主内存中,线程 B 读取 x 时由于本地内存中的 x 失效了,就会从主内存中读取最新的值,内存可见性达成!
三分恶面渣逆袭:volatile内存可见性
volatile 怎么保证有序性的呢?
在程序执行期间,为了提高性能,编译器和处理器会对指令进行重排序。但涉及到 volatile 变量时,它们必须遵循一定的规则:
写 volatile 变量的操作之前的操作不会被编译器重排序到写操作之后。
读 volatile 变量的操作之后的操作不会被编译器重排序到读操作之前。
这意味着 volatile 变量的写操作总是发生在任何后续读操作之前。
讲一下知道的垃圾回收器
JVM 的垃圾收集器主要分为两大类:分代收集器和分区收集器,分代收集器的代表是 CMS,分区收集器的代表是 G1。
三分恶面渣逆袭:HotSpot虚拟机垃圾收集器
CMS 是第一个关注 GC 停顿时间(STW 的时间)的垃圾收集器,JDK 1.5 时引入,JDK9 被标记弃用,JDK14 被移除。
G1(Garbage-First Garbage Collector)在 JDK 1.7 时引入,在 JDK 9 时取代 CMS 成为了默认的垃圾收集器。
问知不知道ZGC回收器(不知道)