2023.7.26晚上7点·百·度·提前批一面

岗位:后端开发

时长:60 分钟左右

  1. 自我介绍
  2. 研究生项目介绍 (java + 实验室项目)
  3. 项目深挖基本都是 Java 项目
  4. 一般而言:面试官一般都会问你项目中的技术
  5. MySQL 选的什么引擎,用的什么索引,好处在哪?
  6. 最左端匹配原则
  7. Redis 数据结构
  8. Redis 三种缓存问题
  9. Kafka 怎么保证数据一致性?
  10. 项目中如何使用 ElasticSearch?
  11. 手撕算法:反转链表
  12. 手撕算法:实现一个加法,但不能用 + 号

问题: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,此时请求会直接访问到数据库,并且查不到数据,没法写缓存,所以下次请求同样会访问到数据库上
  • 此时,缓存起不到作用,请求每次都会走到数据库,流量大时数据库可能会被打挂。此时缓存就好像被 "穿透" 了一样,起不到任何作用

解决缓存穿透问题的方案:

  1. 参数校验
  2. 缓存空值
  3. 布隆过滤器

缓存雪崩问题:

  • 大量的热点 key 设置了相同的过期时间,导致缓存在同一时刻全部失效,造成瞬时数据库请求量大、压力骤增,引起雪崩,甚至导致数据库被打挂
  • 解决方案:给不同的 key 的 TTL 添加随机值
  • 还有一种情况会导致缓存雪崩,那就是 Redis 挂了,一般情况,大部分请求都是打到 Redis 上,一旦 Redis 挂了,那所有的请求都会打到 MySQL 中了。
  • 解决方案:利用 Redis 集群,提高 Redis 的可用性

缓存击穿问题:

  • 就是一个缓存重建业务较复杂的热点 key 突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击
  • 缓存重建业务较复杂:当在 Redis 中查询不到对应的缓存数据,需要查询数据库,并且要经过复杂的计算得到缓存数据,比如需要关联好几张表,然后计算到一个缓存数据,然后将缓存数据写入到 Redis,这个过程可能需要比较多的时间
  • 当一个热点 key 失效,导致大量的线程打到数据库,好像击穿了 Redis 缓存一样

针对缓存击穿问题,常见的两种解决方案是:

  1. 互斥锁,对缓存重建业务加互斥锁,保证只有一个线程执行缓存重建业务,没有获取到互斥锁的线程不断的重试即可,不会打到 MySQL 数据库中
  2. 逻辑过期,在 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;
}
全部评论

相关推荐

评论
11
64
分享
牛客网
牛客企业服务