2023.7.26晚上7点·百·度·提前批一面
岗位:后端开发
时长:60 分钟左右
- 自我介绍
- 研究生项目介绍 (java + 实验室项目)
- 项目深挖基本都是 Java 项目
- 一般而言:面试官一般都会问你项目中的技术
- MySQL 选的什么引擎,用的什么索引,好处在哪?
- 最左端匹配原则
- Redis 数据结构
- Redis 三种缓存问题
- Kafka 怎么保证数据一致性?
- 项目中如何使用 ElasticSearch?
- 手撕算法:反转链表
- 手撕算法:实现一个加法,但不能用 + 号
问题:MySQL 选的什么引擎,用的什么索引,好处在哪?
可以这样回答引擎选择:我们项目绝大部分场景下使用的是 InnoDB 存储引擎
然后可以稍微解释:
MySQL 常用引擎包括 InnoDB、MyISAM、Memory 等,每一种引擎各有各的优缺点
InnoDB:
- 优点:支持事务,具有较高的并发性能和数据完整性,支持行级锁和外键约束
- 缺点:相比于其他引擎,存储空间占用较大
MyISAM:
- 优点:简单、高效,适用于读密集型应用,占用较小的磁盘空间
- 缺点:不支持事务和行级锁,对于写密集型应用性能差
Memory:
- 优点:数据存储在内存,读写速度极快,适用于缓存表和临时表
- 缺点:数据库重启会丢失数据,不支持事务和持久化
我们项目中需要用到事务、有些写密集型的场景、而且数据需要落到磁盘,
所以我们大部分场景使用的是 InnoDB 这个引擎
接下来,回答索引相关的问题
在项目中,我们会使用主键索引、唯一索引、普通索引、联合索引等
- 主键索引也是一种唯一索引,它俩都不允许有重复值
- 与主键索引不同,唯一索引可以包含空值
- 普通索引也称为非唯一索引,也就是允许重复值
- 联合索引是对多个列进行索引
索引的好处是可以加速查询,按照索引查询是非常快的,
因为在 InnoDB 中,上面的每个索引都会被组织成 B+ 树
所以说在使用索引的时候,还需要注意在插入和更新大量行的时候
可能会出现性能瓶颈,因为,需要不断的维护索引树
问题:最左端匹配原则
最左匹配原则是指:如果一个索引包含多个列,
则只有在查询条件中包含了索引的最左侧前缀列时,索引才能被利用
例如:假设有一个包含列 A、B 和 C 的索引,查询条件为 WHERE A=1 AND B=2 AND C=3,
那么此时索引可以被充分利用。但如果查询条件为 WHERE B=2 AND C=3,
则此时只能利用索引的 B 和 C 列,而不能利用 A 列。
最左前缀匹配原则的优点是可以最大限度地利用索引,提高查询效率。但也需要注意,如果索引列过多或查询条件不符合最左前缀匹配原则,可能会导致索引无法被利用,从而影响查询性能。
因此,在设计索引时需要根据实际情况选择最适合的索引策略。
问题:Redis 数据结构
Redis 的 key 是字符串类型的,value 有 5 种常用的数据类型
分别是:字符串、列表、集合、有序集合、散列
其中:
- 字符串有 int、raw、embstr 三种编码实现,int 表示的是整数,其他两种编码方式则是由 SDS 实现
- 列表:在 Redis 3.2 版本之前,列表对象的编码可以是 ziplist 或者 linkedlist,从 redis 3.2 版本后,列表的底层编码都被设置为 quicklist (linkedlist + ziplist)
- 集合:集合对象的编码可以是 intset 或者 hashtable
- 有序集合:有序集合的编码可以是 listpack 或者 skiplist
- 散列:哈希对象的编码可以是 listpack 或者 hashtable
问题:Redis 三种缓存问题
Redis 的三种缓存问题分别是:缓存穿透、缓存雪崩、缓存击穿
缓存穿透问题:
- 访问一个缓存和数据库都不存在的 key,此时请求会直接访问到数据库,并且查不到数据,没法写缓存,所以下次请求同样会访问到数据库上
- 此时,缓存起不到作用,请求每次都会走到数据库,流量大时数据库可能会被打挂。此时缓存就好像被 "穿透" 了一样,起不到任何作用
解决缓存穿透问题的方案:
- 参数校验
- 缓存空值
- 布隆过滤器
缓存雪崩问题:
- 大量的热点 key 设置了相同的过期时间,导致缓存在同一时刻全部失效,造成瞬时数据库请求量大、压力骤增,引起雪崩,甚至导致数据库被打挂
- 解决方案:给不同的 key 的 TTL 添加随机值
- 还有一种情况会导致缓存雪崩,那就是 Redis 挂了,一般情况,大部分请求都是打到 Redis 上,一旦 Redis 挂了,那所有的请求都会打到 MySQL 中了。
- 解决方案:利用 Redis 集群,提高 Redis 的可用性
缓存击穿问题:
- 就是一个缓存重建业务较复杂的热点 key 突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击
- 缓存重建业务较复杂:当在 Redis 中查询不到对应的缓存数据,需要查询数据库,并且要经过复杂的计算得到缓存数据,比如需要关联好几张表,然后计算到一个缓存数据,然后将缓存数据写入到 Redis,这个过程可能需要比较多的时间
- 当一个热点 key 失效,导致大量的线程打到数据库,好像击穿了 Redis 缓存一样
针对缓存击穿问题,常见的两种解决方案是:
- 互斥锁,对缓存重建业务加互斥锁,保证只有一个线程执行缓存重建业务,没有获取到互斥锁的线程不断的重试即可,不会打到 MySQL 数据库中
- 逻辑过期,在 key 对应的 value 中加一个过期时间,当查询缓存的时候,比较这个过期时间,如果过期的话,则去抢互斥锁,抢到锁的线程会创建一个新的线程执行缓存重建业务,并返回过期的 value,没有抢到锁的线程,不要重试,直接返回过期的 value 即可
两种方案的对比:
互斥锁:
- 优点:没有额外的内存消耗、保证数据一致性、实现简单
- 缺点:线程需要重试等待,影响性能、可能有死锁的风险
逻辑过期:
- 优点:线程无需重试等待,性能较好
- 缺点:不能保证数据一致性、有额外内存消耗、实现稍微复杂
问题:Kafka 怎么保证数据一致性?
Kafka通过多种机制来保证数据一致性:
1. 分区和副本
Kafka 将 Topic 中的数据分为多个分区,每个分区可以有多个副本。
其中一个副本被指定为领导者(leader),其他副本为追随者(follower)。
生产者将消息发送到某个分区的领导者,领导者将消息复制到其他追随者。
如果领导者出现故障,Kafka 会自动选择一个新的领导者来替代它。
这样可以确保即使出现故障,也不会丢失数据。
2. ISR 机制
Kafka 引入了 ISR(In-Sync Replicas)机制,用于确保副本之间的数据一致性。
Kafka 只有在所有副本都接收到消息并确认后,才会向生产者发送确认消息。
只要某个副本没有及时接收到消息,它就会被移出 ISR 列表,直到它赶上其他副本的进度为止。
这样可以确保副本之间的数据一致性。
3. 事务支持
Kafka 从 0.11 版本开始支持事务,可以将多个消息作为一个事务处理。
这样可以确保多个消息的原子性和一致性,即要么全部提交,要么全部回滚。
这对于一些需要保证数据一致性的场景非常有用,比如银行交易等。
可以看出,Kafka 通过分区和副本、ISR 机制和事务支持等多种机制来保证数据一致性。
这些机制不仅可以防止数据丢失和数据不一致,还可以提高 Kafka 的可靠性和容错性。
问题:项目中如何使用 ElasticSearch?
Elasticsearch 是一款开源的搜索引擎,
它可以用于构建全文搜索、日志分析、业务监控、数据挖掘等应用。
下面是一些在项目中使用 Elasticsearch 的常见方法:
1. 集成 Elasticsearch API
集成 Elasticsearch API 是使用 Elasticsearch 最基本的方法。通过 Elasticsearch 提供的 REST API,我们可以对索引、文档进行增删改查等操作。这种方式可以在任何编程语言中使用,但需要对 Elasticsearch API 有一定的了解和熟悉。
2. 使用 Elasticsearch 客户端库
Elasticsearch 提供了许多客户端库,可以轻松地在 Java、Python、Ruby 等编程语言中使用 Elasticsearch。使用客户端库可以更方便地进行索引、文档操作,同时也可以提供更好的错误处理和调试功能。
3. 使用 Elasticsearch 集成的框架
Elasticsearch 集成了一些流行的开源框架,如 Logstash、Kibana、Beats 等,可以方便地进行日志收集、数据可视化等操作。这些框架提供了丰富的功能,如数据清洗、可视化、告警等,可以帮助我们更好地理解和分析数据。
问题:手撕算法:反转链表
public ListNode reverseList(ListNode head) { ListNode prev = null; ListNode curr = head; while (curr != null) { ListNode next = curr.next; curr.next = prev; prev = curr; curr = next; } return prev; }
问题:手撕算法:实现一个加法,但不能用 + 号
public int getSum(int a, int b) { while (b != 0) { int carry = (a & b) << 1; a = a ^ b; b = carry; } return a; }