【2024校招】2023-9-17 度小满信贷系统平台部一面

1 自我介绍

2 秒杀系统

2.1 底层的数据怎么设计的

秒杀订单表,秒杀商品/商品抢购,普通商品表,普通订单表,用户表,订单撤回/取消订单,订单到期未回退

2.2 实现了哪些功能

用户下单功能,商品页面,搜索功能,商品详情,购物车,用户登录和鉴权

2.3 你是怎么做鉴权的,详细讲一下使用jwt的流程

答:使用jwt,登录的时候会发送给鉴权中心,密码正确则会颁发给用户一个jwt token,用户后面的每一次请求都会携带这个token,其他的应用服务器收到一个请求后就会首先检查这个token是否有效,如果有效则允许用户请求打进来

2.4 你的用户订单的状态机是怎么设计的呢,假设一条订单的初始值为0,表示用户下单了,1表示已经支付了,2表示…

答:假设一条订单的初始值为0,表示用户下单但是仍然未支付,1表示成功支付了,待发货中,2表示用户主动取消订单了,3表示过期没有支付所以取消订单,4是终态,不会再变化了。

2.5 你什么时候插入的订单记录?

答:消费者主动从kafka消息队列中拉取下单消息,进行消费的时候,会进行下单操作。

2.6 基于你刚刚设计的状态机,用户的哪一个行为会导致状态机由哪一个状态流转到哪一个状态,这个由用户导致的行为一般体现在后端系统中的哪一步

答:

(1)用户点击下单按钮会生成一笔订单,此时订单的状态是0,消费者主动从kafka消息队列中拉取下单消息,进行消费的时候,会进行下单操作,此时变成初始状态0

(2)用户主动调用取消订单的接口,会使得状态由0变为2

(3)用户调用支付接口,状态由0变为1

(4)用户过了三十分钟没有支付,延时消息会到期被消费,将订单状态由0变为3

(5)用户收到货之后点击收货按钮,就会变为终态

2.7 假设现在又新增了几个状态,退换货完成,退换货中,退款中,退款完成,交易关闭这几个状态,想一下连同新增的状态,各个状态之间怎么流转,在什么时间点流转。

答:考虑到新增的状态,我们可以定义以下状态值:

5 - 退换货中
6 - 退换货完成
7 - 退款中
8 - 退款完成
9 - 交易关闭

基于以上的状态定义,以下是各状态间的可能流转以及在什么时间点流转:

  1. 用户点击下单按钮
    用户行为:点击下单按钮
    状态流转:初始 -> 0 (未支付)

  2. 用户取消订单
    用户行为:主动调用取消订单的接口
    状态流转:0 (未支付) -> 2 (用户主动取消)

  3. 用户支付
    用户行为:调用支付接口
    状态流转:0 (未支付) -> 1 (已支付待发货)

  4. 用户超时未支付
    系统行为:处理延时消息
    状态流转:0 (未支付) -> 3 (过期未支付)

  5. 用户确认收货
    用户行为:点击收货按钮
    状态流转:1 (已支付待发货) -> 4 (交易完成)

  6. 用户申请退换货
    用户行为:点击申请退换货
    状态流转:4 (交易完成) -> 5 (退换货中)

  7. 退换货完成
    系统行为:确认退换货请求已处理
    状态流转:5 (退换货中) -> 6 (退换货完成)

  8. 用户申请退款
    用户行为:点击申请退款
    状态流转:4 (交易完成) 或 6 (退换货完成) -> 7 (退款中)

  9. 退款完成
    系统行为:确认退款已处理
    状态流转:7 (退款中) -> 8 (退款完成)

  10. 交易关闭
    系统或用户行为:在任何非终态下,由于某些原因(如长时间不活跃、交易异常等)交易关闭
    状态流转:任意状态 -> 9 (交易关闭)

以上流转可能因业务需求的具体性而有所不同,但大致能够涵盖大部分场景。

3 kafka

3.1 讲一下kafka的零拷贝

Kafka零拷贝

3.2 那为什么将从提供者接收的消息进行刷盘,为什么不直接用sendfile呢,还要用mmap

结论:为什么写入不直接用 sendfile 呢,因为 sendfile 不支持中途对文件做变更,mmap 支持在写入磁盘之前,直接在 page Cache 中修改文件。

sendfile 和 mmap 是零拷贝的两种不同实现,在 kafka 中也对应两种不同场景。针对 producer 往 broker 上写入消息,使用的是 mmap,consumer 从 broker 上拉取数据,使用的是 sendfile。

producer 往 broker 发消息:对应消息文件从网卡–>磁盘。这个文件在写入过程中是会动态“变化”的(试想一下本身是写的场景,因为要处理消息丢失等问题),假设使用 sendfile 的方式就是直接把文件从网卡 copy 到 socket buffer 之后,然后直接落到磁盘,中间是【不能有变更操作的】,这显然不符合写入的要求。

再说 consumer 从 broker 拉消息:对应消息文件从磁盘–>网卡。这个过程中使用的是 sendfile。这个过程中【只涉及到读操作,对消息这个文件是不需要有任何变更操作的】,将文件从磁盘 copy 到 readBuffer 之后,直接发送到网卡了,这中间没有经过 socketBuffer。

3.3 kafka为什么要将消息进行分区

答:(1)数据隔离(2)保证同一个分区内的消息的有序性(3)可以提升并行读写速度,提升吞吐量

gpt4的答案:

Kafka 将消息进行分区的主要原因有以下几点:

  1. 可扩展性(Scalability)

    • 水平扩展:分区允许 Kafka 主题进行水平数据扩展,即通过增加更多的节点,来增加系统整体的处理能力。
    • 多副本:每个分区可以有多个副本,这意味着每个分区的数据可以复制到多个 Kafka broker 上,确保数据的高可用性。
  2. 并行处理

    • 并行读写:多个分区意味着可以有多个消费者同时读取数据,每个消费者消费不同的分区,这大大加快了消息的消费速度。
    • 增加吞吐量:在生产者端,可以同时将多条消息发送到多个分区,从而增加总体的吞吐量。
  3. 数据的有序性

    • Kafka 保证同一分区内的消息是有序的。这意味着,如果你想要确保某一类消息的顺序性,你可以将它们发送到同一个分区。但需要注意的是,跨分区的消息顺序性不被保证。
  4. 负载均衡

    • 分区有助于更好地在 Broker 和 Consumer 之间进行负载均衡。如果某个 Broker 或 Consumer 出现性能瓶颈,你可以通过重新分配分区来进行负载均衡。
  5. 持久性与容错性

    • 分区还有助于数据的持久性和容错性。每个分区都可以设置副本数,当主分区出现故障时,其中的副本可以被选为新的主分区,从而保证数据的可用性。
  6. 日志段的管理

    • Kafka 使用分区来管理日志的大小。每个分区实际上是一组有序的日志文件,每个文件的大小可以配置。当日志达到一定大小或者达到一定的保存期限后,可以轻松地删除或者压缩。
  7. 分区键(Partition Key)

    • 生产者在发送消息时可以指定一个键,Kafka 使用这个键来决定将消息发送到哪个分区。这为某些应用场景提供了便利,比如确保相同键的消息都进入到相同的分区,从而保证它们的顺序性。

综上所述,Kafka 的分区设计使得 Kafka 能够提供高吞吐量、扩展性、并行处理能力以及高可用性等优点。

4 java基础

4.1 hashMap的数据结构

4.2 hashMap线程不安全的,怎么解决

讲了讲concurrentHashMap是如何解决线程安全问题的

4.3 线程池怎么实现,怎么运行

自定义线程池,任务来了之后怎么加入线程池,,怎么流转

4.4 假设机器都放得下,是否线程池可以设置的无限大?

答:

(1)非核心线程数不能设置的无限大,因为线程切换非常耗费cpu资源,实际上得不偿失

(2)队列也不能无界,对调用者的体验不好,如果线程池的任务队列很大,新来的任务在末尾,那么可能需要让调用方等待很久才能执行,响应时间过长

5 redis

5.1 redis的单线程模式是怎么实现的?

答:从socket套接字缓冲区中取得数据到执行任务然后到写回发送缓冲区中的这么过程是单线程

5.2 redis是怎么保证数据不丢失的

答:aof,rdb以及同步给从节点

5.3 redis是如何组合使用aof,rdb以及多副本的

答:一般会先把数据写入到缓冲池中,然后将缓冲池中的数据定期或者按照规则同步到本地磁盘中,另外还会定期生成rdb快照,进行和本地持久化

5.4 如果redis宕机了怎么办

答:一般可以每秒刷一次盘,这样宕机了也可以重启加载rdb快照进行快速恢复,然后执行aof中的命令恢复到最接近完整的状态

5.5 每秒1次进行刷盘操作,有什么危害

答:刷的太频繁了,可能会降低redis的整体性能

5.6 怎么样能避免丢失大量的数据

答:(1)还是得合理配置持久化的时间点,尽量做到数据一致性和性能的均衡(2)如果丢失了大量的数据,看一下从节点的日志是否完好,如果可以就使用从节点的日志,或者接收新leader节点的数据补充丢失的大量数据

6 mysql

6.1 mysql有哪些锁

答:行,间隙锁,临键锁,表锁

6.2 mysql的常用的有哪些锁

答:myISAM常用表锁,其他类型常被innodb使用

6.3 innodb支持的四种隔离级别

答:

6.4 数据库的默认隔离级别

答:

6.5 解释一下幻读

答:

6.6 建立索引会考虑的哪些因素

答:查询频率,索引选择性好/区分度高,经常用于排序的字段

6.7 索引对数据库的负面影响

答:新增、删除和修改记录时候需要修改索引,会影响执行的性能

6.8 如果磁盘满了,删除大表中一半数据(业务上不需要了),能解决问题吗(重要)

答:

我的答案:可能会也可能不会,因为可能会存在页内碎片问题,如果innodb没有对删除数据后的数据页进行相关的合并和整理操作,则即使删除了其中的数据也不会解决问题,也就是说这张表所占用的数据页数不变。这跟jvm的元空间内存不足的问题很像。

gpt4:

如果MySQL的磁盘已满,删除数据是一种方式来释放空间。但仅仅从应用层面删除数据并不意味着磁盘空间会立即得到释放。这取决于MySQL的存储引擎,例如InnoDB。如果使用InnoDB引擎,空间不会立即返回给操作系统,而是被标记为可用并由MySQL重新使用。为了真正释放空间,你可能需要进行一些其他操作,如优化表(OPTIMIZE TABLE)或者重新导出、导入数据库。

因此,删除一年的记录可能可以解决问题,但并不是所有情况都可以。另外,如果数据增长的速度迅速,你很快可能会再次遇到空间问题。最佳做法是监控磁盘空间并根据需要增加。

6.9 面试官给我的提示是更新索引会不会导致这种即使删除了磁盘一半的数据,仍然无法解决磁盘满的问题?(面试官试图引导我)

答:面试官的提示指向了一个很重要的方面:索引。

在MySQL中,当数据被删除时,相关的索引项通常不会立即被删除或收缩。这意味着,即使你从表中删除了很多数据,由于相关的索引结构没有变小,磁盘空间的实际使用可能不会显著减少。

具体来说,如果一个InnoDB表有很多的索引,这些索引可能占据的空间与数据本身同样多,甚至更多。当你删除表中的数据时,虽然数据页可能被标记为可重新使用,但这并不意味着相关的索引页也是这样。你可能需要重新构建或优化这些索引来真正释放空间。

这里有几种方法可以解决这个问题:

  1. OPTIMIZE TABLE: 这是最直接的方法。此命令将重建表和索引,释放未使用的空间。但是这个操作对于大型表可能非常慢。

  2. DROP INDEX and CREATE INDEX: 如果你知道哪些索引占据了大量空间,你可以选择删除并重新创建它们。

  3. 重导表: 导出表的数据,删除表,然后重新导入数据。这在某种程度上与OPTIMIZE TABLE相似,但你会有更多的控制,例如可以选择不重新导入某些不再需要的数据或索引。

不论选择哪种方法,都应当在低峰时间进行,因为这些操作可能会消耗大量的I/O,影响到其他运行中的查询或操作。

所以,面试官的提示是很有道理的。即使删除了大量数据,仍然需要考虑索引占据的空间,特别是在一个索引重的数据库或表中。

6.10 首先如果是主键索引,叶子节点指向的是行记录是真实行记录的数据副本吗,还是指同一份数据?

  • 在InnoDB中,表的数据实际上是按照主键索引的顺序存储的,这种结构被称为聚簇索引。
  • 聚簇索引的叶子节点包含了数据行本身,而非数据的副本。也就是说,当你查询基于主键的数据时,你直接获取到的是数据行,没有额外的跳转。
  • 这也意味着在InnoDB表中,每个表只能有一个聚簇索引(即主键索引)。

6.11 主键索引(聚簇索引)的叶子节点直接指向数据行本身,而非数据的副本,覆盖索引的叶子节点呢?

当我们谈到覆盖索引时,实际上我们是在描述一个查询使用的索引,而不是描述索引本身的类型。覆盖索引查询是指查询所需的所有数据都包含在索引中,无需访问数据表的实际行。对于InnoDB,非聚簇索引(普通索引)的叶子节点通常存储主键的值,但如果一个查询只需要索引中的列,那么叶子节点就已经包含了所有必要的信息,使得该查询成为一个覆盖索引查询。

6.12 在MySQL中,当数据被删除时,相关的索引项通常不会立即被删除或收缩。这意味着,即使你从表中删除了很多数据,由于相关的索引结构没有变小,磁盘空间的实际使用可能不会显著减少。为什么索引项不会立即收缩,这里的索引项是指非叶节点的主键吗?还是叶子节点的记录不会收缩?如果不立即对查找、新增或者删除难道没有影响吗?

  1. 为什么索引项不会立即收缩:

    • 索引结构,如B+树,被设计为在数据插入和删除时自平衡。当数据从一个B+树结构的节点中删除时,节点可能不会立即被回收,但它可能在后续的插入操作中被重新使用。
    • 如果立即收缩或重新平衡索引,那么每次删除操作都可能导致大量的磁盘I/O操作,这将大大降低性能。因此,为了效率,很多数据库管理系统选择在一定程度上延迟索引的收缩和重建。
    • “索引项”可以指叶子节点中的条目,也可以指非叶节点中的条目。在大多数情况下,当我们谈论索引结构不收缩时,我们是指整个索引,包括叶子节点和非叶节点。
    • 当然,长期不进行索引的维护可能会影响查询、插入和删除的性能,因为索引可能变得过于膨胀,有很多未使用的或半满的页面。这就是为什么定期进行数据库维护,如优化表和重建索引,是很重要的。

在实践中,数据库管理员通常会监视索引的健康状况,并根据需要进行维护,以确保性能处于最佳状态。

6.13 索引项不会立即收缩,那对于聚簇索引来说,叶子节点中的真实的记录呢,会不会被物理删除掉?

对于InnoDB的聚簇索引(即主键索引),叶子节点中直接包含了表的数据行。当你从表中删除一行数据时,对应的索引条目确实会从聚簇索引中删除。但这并不意味着磁盘上对应的空间会立即被释放回操作系统。

具体行为如下:

  1. 逻辑删除: 当你执行DELETE操作时,InnoDB首先会逻辑地删除记录,这意味着它会标记记录为已删除,但实际上记录还留在页中。

  2. 空间利用: 虽然这些标记为已删除的记录仍然占据物理空间,但当新的记录插入到这个页时,这些空间可以被重新利用。

  3. 物理空间释放: 即使一个数据页变得完全空闲(例如,你删除了该页上的所有行),它也不会立即被释放回文件系统或操作系统。相反,它会留在表空间文件中并被标记为可重新使用。只有在特定的维护操作(如表的OPTIMIZE操作)中,这些空页才可能被物理地释放。

这种策略的一个主要原因是性能。频繁地进行磁盘空间的回收和再分配可能会导致大量的磁盘I/O和碎片化,这对性能不利。相反,通过重用已分配的空间,InnoDB可以更有效地处理新的数据插入操作。

6.14 解释一下什么是慢查询,如何治理

答:

我的答案:

(1)走索引

(2)建立分区表

(3)走从库

(4)走mysql或者redis缓存

GPT4:

慢查询

慢查询是指在数据库中执行时间超过某个阈值的查询。这个阈值是可配置的,例如,如果你设定了一个查询执行时间超过2秒就被认为是慢查询,那么所有执行时间超过2秒的查询都会被记录在慢查询日志中。

慢查询可能的原因

  1. 查询不使用合适的索引。
  2. 数据库表结构设计不佳,如缺乏必要的索引。
  3. 查询语句本身编写得不够高效。
  4. 大量的数据需要被检索和处理。
  5. 数据库负载过高或硬件资源限制。
  6. 数据库配置不恰当,例如缓冲区设置不合理。

如何治理慢查询

  1. 开启慢查询日志:首先,确保你的MySQL服务器已经开启了慢查询日志。这会帮助你跟踪那些执行时间超过阈值的查询。

  2. 使用性能分析工具:使用如EXPLAIN命令,来查看查询是如何在数据库中执行的。这可以帮助你识别查询为何会慢,例如它是否正在进行全表扫描或是否使用了合适的索引。

  3. 优化查询

    • 重写低效的查询。
    • 使用索引覆盖来减少数据访问。
    • 减少返回的数据量。
    • 考虑将计算放在应用层,而不是数据库层。
  4. 优化表结构:添加或删除索引,根据查询的需求进行调整。有时,使用如DENORMALIZATION的技术来提高查询性能也是合适的。

  5. 调整数据库配置:根据服务器的硬件和负载,调整MySQL的配置参数,例如innodb_buffer_pool_size,以适应你的工作负载。

  6. 硬件升级:增加RAM、使用更快的磁盘、升级到更快的CPU,或考虑使用SSD。

  7. 考虑分区:对于特别大的表,使用分区技术可以提高某些类型查询的性能。

  8. 使用缓存:考虑在应用层或数据库层使用缓存,例如使用Redis或Memcached,以减少对数据库的请求。

  9. 监控和定期审查:持续监控数据库的性能,并定期审查慢查询日志。数据库和应用的工作负载可能会随时间变化,所以持续的监控和优化是必要的。

治理慢查询是一个持续的过程。随着数据量的增长和应用需求的变化,新的慢查询可能会出现,因此需要定期的监控和调整。

6.15 什么是mysql的分区表,在治理慢查询上可以发挥什么作用

答:分区相当于是按照某一个字段给大表进行逻辑划分,本来mysql是为一张表只建立一个B+树聚簇索引的,有了分区后,每一个分区内都有对应的聚簇索引,比如现在一张表每一年会新增100w的量,五年就是500w,现在按照年份进行分区,则新建5个分区,每一个分区内都有一个索引了这一年份的聚簇索引树,当一个sql语句按照日期查询时,首先会判断自己在哪一个分区中,然后拿到对应分区内的B+树索引,再进行查找。这样做的好处是,原本不分区时的查找复杂度是log(x,500w),现在是log(x,100w),如果分区再多一些,则查找的复杂度会进一步降低。

6.16 为什么有redis缓存还要设置mysql缓存呢

答:因为redis的缓存是可能会过期或者失效,最终慢查询还是可能会走mysql,因此在mysql的上层服务器进行缓存也是有必要的。

你可能感兴趣的:(面经,java后端,度小满)