核心内容摘要
《王者小乔“颜艺”大赏:翻白眼、流眼泪、流口水的惊艳表现》
介绍一下什么是索引?拿书举例索引相当于目录计算机科学方面类比可以得出索引是帮助 mysql 高效获取数据的数据结构mysql 的 InnoDB 存储引擎主要使用的是 B树 数据结构作为索引构建出属于表的索引书走索引查询可以减少磁盘 I/O 次数加快查询次数MySQL都有哪些索引?索引类型?(聚集索引和二级索引)按数据结构分InnoDb 存储引擎主要使用的是 B树索引少数情况(比如说大量等值查询)下 InnoDb 会创建自适应 hash 索引按物理存储类型分聚集索引非聚集索引(二级索引)聚集索引叶子节点存储的是整行数据一张表只能有一个如果非聚集索引拿不到数据也就是无法形成覆盖索引那就需要拿着非聚集索引的叶子节点的主键信息取聚集索引里面回表查询举一个项目中的例子使用MySQL做的查询依据什么去做的查询?sql 语句前半部分要查询出哪些信息sql 语句后半部分依据字段索引信息先 where 再 order by实习的时候处理工单根据用户问题去数据库里用 mysql 查询信息主要是根据订单号去(order_no 全局唯
查询这个订单号肯定是有索引的然后联表作进一步处理还是基于这个订单号满足最左前缀避免失效查多了的时候可以用 explain 看一下查询全表覆盖率走索引情况等多了解比如说商品信息价格联合索引是(category_idprice)那查询的时候先定分类在按照价格排序有遇到过一些慢查询的场景吗?知道什么是慢查询吗?慢查询就是 sql 执行时间超过一个定义的 long_query_time 默认是 10 秒没走索引查询数据量过大笛卡尔积过大导致扫行过多select * 返回数据量太大explain关键字你通常会关注哪些输出信息作为判断。
type查询类型。
从好到坏system const eq_ref ref range index ALL。
一般要求达到 ref 或 range 级别绝对不能是 ALL全表扫描。
key实际使用的索引。
如果是 NULL 说明没用索引。
rows预计扫描的行数越少越好。
Extra关注一些额外字段Using index好覆盖索引不需要回表。
Using filesort坏需要外部排序。
Using temporary坏用了临时表。
有遇到过选错索引的情况吗?有别的索引失效的场景吗?未满足最左前缀想一下 B树结构 非叶子节点呈折半查找叶子节点呈已排序好的链表模、型、数、空模糊查询问题 like %%类型转换问题 字符串不加引号数学函数运算有的条件没有索引如 OR 连接条件中某个字段没索引介绍一下MySQL的事务的隔离级别。
读未提交读已经提交可重复读幻读幻读是一个什么样的场景?幻读场景事务A先读取所有 age10 的人共5个事务B插入了一个 age15 的人并提交。
事务A再次读取发现变成了6个。
像产生了幻觉一样。
重点是“多出来或少了一行数据”。
可重复读的隔离级别可以解决幻读那他底层是怎么解决幻读的呢?快照读 (普通 Select)通过 MVCC (多版本并发控制) 解决。
读取事务开始时的快照版本而不会统计未来事务生成的数据。
当前读 (Select for update, Insert, Update)快照读的时候不能解决修改问题因为不能去修改历史快照通过 Next-Key Lock (临键锁) 解决。
锁住记录本身间隙防止别的事务插入数据。
你项目中用到的是哪个隔离级别为什么这么选择?我使用的是默认的不可重复读保证数据一致性防止脏读、不可重复读且MySQL对RR优化得很好。
如果现在让你选择一个隔离级别你会参考哪些条件去选择隔离级别?有些互联网大厂如阿里会改成 RC (Read Committed)。
理由是RC 的锁粒度更小只有行锁没有间隙锁并发度更高且死锁概率低。
为了解决主从复制数据不一致问题需要将 binlog 格式设置为 ROW。
介绍一下单例模式。
保证一个类只有一个实例并且提供全局访问点第一次判断如果已经有实例了直接返回避免不必要的同步提升性能。
第二次判断防止并发创建。
假设A和B同时通过了第一次判断A抢到了锁创建了对象A释放锁。
B拿到锁进入同步块如果不判断B会再创建一个对象单例就破了。
手写一下单例模式说明主要步骤即可。
判断单例是否为空抢锁上锁判断单例是否为空创建单例public class Singleton { // volatile 禁止指令重排序防止拿到半初始化对象 private static volatile Singleton instance; private Singleton() {} // 私有构造 public static Singleton getInstance() { if (instance null) { // 第一次检查 synchronized (Singleton.class) { if (instance null) { // 第二次检查 instance new Singleton(); } } } return instance; } }说到Bean的两种状态单例和非单例那么这两种方式对比一下?Singleton (默认)IOC容器启动时创建整个应用生命周期只有一个。
优点节省内存减少GC。
缺点有线程安全问题如果有状态的话。
Prototype每次获取Bean都创建一个新对象。
单例适合哪些场景?一般什么类需要去做一个单例?什么类做单例无状态的类Service, Dao, Util配置类。
线程安全单例本身不安全但因为Service/Dao通常没有成员变量状态所以使用起来是安全的。
写一个线程安全的单例模式的伪代码;为什么要判断两次是否为空呢?
判断单例是否为空
抢锁上锁
判断单例是否为空
创建单例public class Singleton { // volatile 禁止指令重排序防止拿到半初始化对象 private static volatile Singleton instance; private Singleton() {} // 私有构造 public static Singleton getInstance() { if (instance null) { // 第一次检查 synchronized (Singleton.class) { if (instance null) { // 第二次检查 instance new Singleton(); } } } return instance; } }线程 B 可能在 线程 A 释放锁后线程 B 可能在步骤 12 之间直接拿到锁创建商户缓存介绍一下场景要缓存哪些信息?场景方面商户详情页、店铺信息这是读多写少的场景。
内容方面商户的基础信息名字、地址、评分、热门商品列表。
介绍缓存穿透、缓存雪崩、缓存击穿。
击穿热点Key过期大量并发请求瞬间击穿Redis打到DB。
解法互斥锁setnx、逻辑过期永不过期后台异步更新。
穿透查不存在的数据Redis没DB也没请求直打DB。
解法布隆过滤器、缓存空对象。
雪崩大量Key同时过期 或 Redis宕机。
解法过期时间加随机值、Redis集群高可用。
电商项目优惠卷秒杀优惠券存在哪里?存在哪里Redis的 String (存库存) 和 Set (存已购买用户ID去重)。
流程Lua脚本保证原子性扣减库存判断用户是否买过。
秒杀优化的阻塞队列是通过什么实现的?面试官可能问的是你代码里的 BlockingQueue。
实现Java内存中的队列。
为什么用实现异步下单。
秒杀瞬间只要Redis扣库成功就返回“抢购中”后台线程慢慢从队列取任务去写数据库。
削峰填谷。
不足/优化内存队列断电会丢数据且容量有限。
生产环境应该用 RabbitMQ / Kafka。
它们支持持久化、确认机制、高吞吐。
异步下单场景那你怎么通知用户下单成功?(直接成功并且指出不足说出有思考下单失败应该怎么办)直接方案前端轮询Polling后端接口查询状态。
失败怎么办如果是DB写失败概率小需要补偿机制重试。
如果彻底失败需要回滚Redis库存增加库存移除用户记录并通知用户“下单失败”。
介绍一下点赞排行是个什么功能?为什么使用zset呢?(面试官好像不理解为什么要使用zset)用 zset 是为了排序zset 是排序的 key 可以排序的 hashmap实现完全是为了简单只要 redis 的 zset 就能实现。
了解过zset的底层实现吗?(跳表)如果往zset中添加一个元素这个过程是怎么样的?跳表 一种加了索引的链表并没有红黑树复杂结构一种多层的链表。
最底层包含所有元素上面每一层都是下面一层的索引。
添加过程找到插入位置类似二分查找的路径。
插入底层链表。
随机抛硬币决定该节点是否提拔到上一层索引。
层层往上直到不提拔。
为什么不用红黑树跳表实现简单范围查询Range效率比红黑树高红黑树需要中序遍历跳表直接链表往后走。
feed流实现关注推送关注推送功能是怎么实现的?(推模式)发布订阅模型推模式 (Push / 写扩散)实现大V发微博直接写到所有粉丝的收件箱Redis List/ZSet。
优点粉丝读取快拿出来就是做好的时间线。
缺点大V粉丝千万级发一条要写千万次写入延迟极高。
有了解过拉模式吗?对比一下推拉模式有哪些优缺点?模式 (Pull / 读扩散)实现大V只发到自己的发件箱。
粉丝看Feed时临时去关注的人的发件箱里拉取并排序。
优点写入极快。
缺点粉丝关注的人多时聚合查询慢。
讲一下推拉模式该怎么选择普通用户用推模式粉丝少写扩散成本可控保证读性能与实时性。
头部大 V 用拉模式避免写风暴用户刷新时实时拉取大 V 最新内容。
新用户冷启动先用拉模式获取历史内容积累行为后切换到推模式。
热点内容特殊处理热点内容先推至活跃用户长尾用户拉取兼顾实时性与成本。
读写分离推模式用 Redis 存个人 Feed拉模式用 MySQLES 存发布者内容池。
缓存策略推模式缓存个人 Feed 队列拉模式缓存关注列表与发布者最新内容切片。
限流熔断对大 V 写扩散做 QPS 限制读扩散时设置超时与降级如返回部分内容。
算法手撕:(定时20min)带TTL的LRULRU的核心是HashMap 双向链表。
Map存Key-Node链表维护顺序最近用的在头最久没用的在尾。
带TTL过期时间需要在 Node 类中增加 expireTime 字段。
惰性删除不开启线程定时删而是每次 get(key) 时判断当前时间是否超过 expireTime。
如果过期视为不存在执行删除逻辑。
import java.util.HashMap; import java.util.Map; class LRUCacheWithTTL { // 双向链表节点 class Node { int key, value; long expireTime; // 过期时间戳 Node prev, next; public Node(int k, int v, long ttl) { this.key k; this.value v; // 计算绝对过期时间 this.expireTime System.currentTimeMillis() ttl; } } private int capacity; private MapInteger, Node map; private Node head, tail; // 虚拟头尾节点 public LRUCacheWithTTL(int capacity) { this.capacity capacity; this.map new HashMap(); head new Node(-1, -1,