快手今年的年终奖。。。

关系型数据库算法机器学习

picture.image

苏三的免费八股文网站:

www.susan.net.cn

大家好,我是苏三。

最近是春招投递的高峰期时间,很多同学遇到一个问题,大厂部门那么多,如何选择部门来投递呢?

picture.image

我的想法是这样,如果在还没拿到某个大厂 offer 之前,先不着急选部门,哪个部门约你面试,你就面哪个再说,先拿第一个大厂 offer 先拿下来会更好,然后后面再考虑找其他大厂业务比较核心的部门,不然一开始就挑来挑去,最终可能啥都没有。

当然,还有一个方式也可以作为参考,就是看大厂各个事业群年终奖的情况,年终奖发的越多,大概率这个业务是比较核心的。

这次,我们来看看快手今年年终奖的情况,我根据脉脉网的爆料,整理了与后端岗位有关的年终奖信息,年终奖也与个人的绩效挂钩,所以仅供参考:

  • 部门主站,年终奖 28w ,后端开发,职级 e7(老职级对应 k3a)
  • 部门主站,年终奖 11.4w,后端开发,职级 e7(老职级对应 k3a)
  • 部门主站,年终奖 15w,后端开发,职级 e8(老职级对应 k3b)
  • 部门风控,年终奖 15w,后端开发,职级 e8(老职级对应 k3b)
  • 部门基础平台,年终 12w,后端开发,职级 e9(老职级对应 k3c)
  • 部门社科,年终奖 13w,后端开发,职级 e9(老职级对应 k3c)
  • 部门社科,年终奖 25w,后端开发,职级 e10(老职级对应 k4a)

这里也贴一个快手职级体系映射薪酬级别的表格,这个是老的职级:

picture.image

现在快手是 e 系列为职级了,新老职级的对比如下:

picture.image

既然聊到快手,那么快手的面经也是逃不了的picture.image

来看看最近 快手的Java后端开发的面经 ,属于一面,重点考虑了Redis、消息队列、MySQL、网络、SSM、项目场景、算法这些内容。

picture.image

快手一面

Redis有哪些客户端,有什么区别?

在 Java 编程中,我了解到的有 Jedis 和 Redisson 这两种客户端

  • Jedis :一款老牌的 Java Redis 客户端,提供了全面且简洁的 API,能直接对应 Redis 的各种命令,使用方式简单直观,适合对 Redis 命令操作有直接需求的场景,例如,执行 SET 命令可以直接调用 jedis.set(key, value) 方法,但它没有提供分布式锁的实现,在分布式场景下功能有所欠缺
  • Redisson :基于 Redis 实现的分布式和可扩展的 Java 数据结构集合,它不仅提供了与 Redis 交互的基本功能,还封装了丰富的分布式对象和服务,如分布式锁、分布式集合等,让开发者可以方便地在分布式系统中使用这些功能,而不需要自己去实现复杂的分布式逻辑。

我的项目中,因为有用到 redis 分布式锁,所以选择了 Redisson。

为什么选择RabbitMQ,kafka了解过吗,使用场景?

Kafka、ActiveMQ、RabbitMQ、RocketMQ来进行不同维度对比。

特性ActiveMQRabbitMQRocketMQKafka
单机吞吐量
万级
万级
10 万级
10 万级
时效性
毫秒级
微秒级
毫秒级
毫秒级
可用性
高(主从)
高(主从)
非常高(分布式)
非常高(分布式)
消息重复
至少一次
至少一次
至少一次 最多一次
至少一次最多一次
消息顺序性
有序
有序
有序
分区有序
支持主题数
千级
百万级
千级
百级,多了性能严重下滑
消息回溯
不支持
不支持
支持(按时间回溯)
支持(按offset回溯)
管理界面
普通
普通
完善
普通

选型的时候,我们需要根据业务场景,结合上述特性来进行选型。

比如你要支持天猫双十一类超大型的秒杀活动,这种一锤子买卖,那管理界面、消息回溯啥的不重要。

我们需要看什么?看吞吐量!所以优先选Kafka和RocketMQ这种更高吞吐的。

比如做一个公司的中台,对外提供能力,那可能会有很多主题接入,这时候主题个数又是很重要的考量,像Kafka这样百级的,就不太符合要求,可以根据情况考虑千级的RocketMQ,甚至百万级的RabbitMQ。

又比如是一个金融类业务,那么重点考虑的就是稳定性、安全性,分布式部署的Kafka和Rocket就更有优势。

特别说一下时效性,RabbitMQ以微秒的时效作为招牌,但实际上毫秒和微秒,在绝大多数情况下,都没有感知的区别,加上网络带来的波动,这一点在生产过程中,反而不会作为重要的考量。

其它的特性,如消息确认、消息回溯,也经常作为考量的场景,管理界面的话试公司而定了,反正我呆过的地方,都不看重这个,毕竟都有自己的运维体系。

如何避免消费端重复消费?

避免消费端重复消费的方法有:

  • 给每个消息添加唯一的标识,例如一个全局唯一的 ID。消费端在处理消息时,先检查该消息的 ID 是否已经被处理过。可以使用数据库表、缓存等存储已经处理过的消息 ID,当收到新消息时,查询存储中是否存在该 ID,若存在则直接丢弃,若不存在则处理消息并将 ID 存入存储。
  • 让消费端的业务处理具有幂等性,即多次执行相同的操作结果与执行一次相同。例如,对于数据库插入操作,可以使用唯一索引来避免重复插入相同的数据;对于更新操作,确保更新的条件是基于唯一标识而不是其他易变的条件,这样即使多次执行更新,也不会出现数据不一致的情况。

什么情况下会出现重复收到消息?

当消费端与消息队列之间的网络出现故障,如网络中断、延迟过高或数据包丢失等情况,可能导致消息的确认信息未能及时发送到消息队列。消息队列在未收到确认信息时,会认为消息没有被成功消费,从而重新发送消息,导致消费端重复收到消息。

TCP 粘包半包是什么,怎么解决的?

粘包 指发送方在多次发送数据的过程中,数据包在同一个数据流中传输给了接收端,导致接收端无法正确分割数据包。例如,客户端连续发送两个数据包 “ABC” 和 “DEF”,服务端可能一次性收到 “ABCDEF”,这就像是两个包粘在了一起。产生原因主要有以下两点:

  • 发送方原因 :发送方每次写入数据小于套接字(Socket)缓冲区大小,TCP 会将多次写入缓冲区的数据一次发送出去。例如,发送方先写入 “ABC”,此时缓冲区未满,接着又写入 “DEF”,然后 TCP 将 “ABCDEF” 一起发送到接收端。
  • 接收方原因 :接收方读取套接字(Socket)缓冲区数据不够及时。当接收方的应用层没有及时读取接收缓冲区中的数据,新的数据又不断到来,就可能导致多个数据包被缓存,接收方一次读取时就会得到多个粘在一起的包。

半包 指发送方发送的数据大于发送缓冲区,接收端一次接收的数据不是完整的数据。比如,客户端发送一个较大的数据包 “ABCDEFG”,由于数据包大小超过了 TCP 缓存容量,它会被分成多个包发送,服务端第一次可能只收到 “ABC”,这就是半包现象。半包产生的原因主要有以下方面:

  • 发送方原因 :发送方每次写入数据大于套接字(Socket)缓冲区大小,数据包不得不被分割成多个小包进行发送。

针对 TCP 粘包和沾包一般有三种解决的方式。

  • 固定长度的消息;
  • 特殊字符作为边界;
  • 自定义消息结构。

固定长度的消息

这种是最简单方法,即每个用户消息都是固定长度的,比如规定一个消息的长度是 64 个字节,当接收方接满 64 个字节,就认为这个内容是一个完整且有效的消息。

但是这种方式灵活性不高,实际中很少用。

特殊字符作为边界

我们可以在两个用户消息之间插入一个特殊的字符串,这样接收方在接收数据时,读到了这个特殊字符,就把认为已经读完一个完整的消息。

HTTP 是一个非常好的例子。

picture.image

HTTP 通过设置回车符、换行符作为 HTTP 报文协议的边界。

有一点要注意,这个作为边界点的特殊字符,如果刚好消息内容里有这个特殊字符,我们要对这个字符转义,避免被接收方当作消息的边界点而解析到无效的数据。

自定义消息结构

我们可以自定义一个消息结构,由包头和数据组成,其中包头包是固定大小的,而且包头里有一个字段来说明紧随其后的数据有多大。

比如这个消息结构体,首先 4 个字节大小的变量来表示数据长度,真正的数据则在后面。


        
        
            

          struct { 
          
   

 
              u\_int32\_t message\_length; 
          
   

 
              char message\_data[]; 
          
   

 
          } message;
          
   

 
        
      

当接收方接收到包头的大小(比如 4 个字节)后,就解析包头的内容,于是就可以知道数据的长度,然后接下来就继续读取数据,直到读满数据的长度,就可以组装成一个完整到用户消息来处理了。

限流算法中漏桶和令牌桶使用场景是什么?

漏桶限流算法

漏桶限流算法是模拟水流过一个有漏洞的桶进而限流的思路,如图。

picture.image

水龙头的水先流入漏桶,再通过漏桶底部的孔流出。如果流入的水量太大,底部的孔来不及流出,就会导致水桶太满溢出去。

从系统的角度来看,我们不知道什么时候会有请求来,也不知道请求会以多大的速率来,这就给系统的安全性埋下了隐患。但是如果加了一层漏斗算法限流之后,就能够保证请求以恒定的速率流出。在系统看来,请求永远是以平滑的传输速率过来,从而起到了保护系统的作用。

使用漏桶限流算法,缺点有两个:

  • 即使系统资源很空闲,多个请求同时到达时,漏桶也是慢慢地一个接一个地去处理请求,这其实并不符合人们的期望,因为这样就是在浪费计算资源。
  • 不能解决流量突发的问题,假设漏斗速率是2个/秒,然后突然来了10个请求,受限于漏斗的容量,只有5个请求被接受,另外5个被拒绝。你可能会说,漏斗速率是2个/秒,然后瞬间接受了5个请求,这不就解决了流量突发的问题吗?不,这5个请求只是被接受了,但是没有马上被处理,处理的速度仍然是我们设定的2个/秒,所以没有解决流量突发的问题

令牌桶限流算法

令牌桶是另一种桶限流算法,模拟一个特定大小的桶,然后向桶中以特定的速度放入令牌(token),请求到达后,必须从桶中取出一个令牌才能继续处理。如果桶中已经没有令牌了,那么当前请求就被限流。如果桶中的令牌放满了,令牌桶也会溢出。

放令牌的动作是持续不断进行的,如果桶中令牌数达到上限,则丢弃令牌,因此桶中可能一直持有大量的可用令牌。此时请求进来可以直接拿到令牌执行。比如设置 qps 为 100,那么限流器初始化完成 1 秒后,桶中就已经有 100 个令牌了,如果此前还没有请求过来,这时突然来了 100 个请求,该限流器可以抵挡瞬时的 100 个请求。由此可见,只有桶中没有令牌时,请求才会进行等待,最终表现的效果即为以一定的速率执行。令牌桶的示意图如下:

picture.image

令牌桶限流算法综合效果比较好,能在最大程度利用系统资源处理请求的基础上,实现限流的目标,建议通常场景中优先使用该算法。

Springboot怎么做到导入就可以直接使用的?

这个主要依赖于自动配置、起步依赖和条件注解等特性。

起步依赖

起步依赖是一种特殊的 Maven 或 Gradle 依赖,它将项目所需的一系列依赖打包在一起。例如, spring-boot-starter-web 这个起步依赖就包含了 Spring Web MVC、Tomcat 等构建 Web 应用所需的核心依赖。

开发者只需在项目中添加一个起步依赖,Maven 或 Gradle 就会自动下载并管理与之关联的所有依赖,避免了手动添加大量依赖的繁琐过程。

比如,在 pom.xml 中添加 spring-boot-starter-web 依赖:


        
        
            

          <dependency>
          
   

 
              <groupId>org.springframework.boot</groupId>
          
   

 
              <artifactId>spring-boot-starter-web</artifactId>
          
   

 
          </dependency>
          
   

 
        
      

自动配置

Spring Boot 的自动配置机制会根据类路径下的依赖和开发者的配置,自动创建和配置应用所需的 Bean。它通过 @EnableAutoConfiguration 注解启用,该注解会触发 Spring Boot 去查找 META - INF/spring.factories 文件。

spring.factories 文件中定义了一系列自动配置类,Spring Boot 会根据当前项目的依赖情况,选择合适的自动配置类进行加载。例如,如果项目中包含 spring-boot-starter-web 依赖,Spring Boot 会加载 WebMvcAutoConfiguration 类,该类会自动配置 Spring MVC 的相关组件,如 DispatcherServlet、视图解析器等。

开发者可以通过自定义配置来覆盖自动配置的默认行为。如果开发者在 application.propertiesapplication.yml 中定义了特定的配置,或者在代码中定义了同名的 Bean,Spring Boot 会优先使用开发者的配置。

条件注解

条件注解用于控制 Bean 的创建和加载,只有在满足特定条件时,才会创建相应的 Bean。Spring Boot 的自动配置类中广泛使用了条件注解,如 @ConditionalOnClass@ConditionalOnMissingBean 等。

比如, @ConditionalOnClass 表示只有当类路径中存在指定的类时,才会创建该 Bean。例如,在 WebMvcAutoConfiguration 类中,可能会有如下代码:


        
        
            

          
 @Configuration
 
          
   

 
          
 @ConditionalOnClass
 
          ({ Servlet
          
 .
 
 class
 
 , 
 
 DispatcherServlet
 
 .
 
 class
 
 , 
 
 WebMvcConfigurer
 
 .
 
 class
 
  })
 
   

 
 
 public
 
  
 
 class
 
  
 
 WebMvcAutoConfiguration
 
  
 
          {
          
   

 
              
          
 // 配置相关的 Bean
 
          
   

 
          }
          
   

 
        
      

这段代码表示只有当类路径中存在 ServletDispatcherServletWebMvcConfigurer 类时,才会加载 WebMvcAutoConfiguration 类中的配置。

mysql默认隔离级别是什么?

可重复读隔离级别

可重复读能够解决幻读问题吗?

可重复读隔离级别下虽然很大程度上避免了幻读,但是还是没有能完全解决幻读。

  • InnoDB 使用 MVCC 来实现在同一时间点不同事务的数据并发访问。在可重复读隔离级别下,MVCC 通过为每个事务分配唯一的事务 ID 和时间戳来跟踪每个数据行的版本。对于快照读(普通的查询语句),只会读取已提交的数据版本,并忽略未提交的或已回滚的事务的数据版本。这样,即使其他事务对数据行进行了插入操作,当前事务仍然能读取到事务开始时的数据,避免了幻读。例如,事务 A 在开始时查询了一批数据,在事务执行过程中,事务 B 插入了新的数据,但事务 A 再次查询时,由于 MVCC 的作用,仍然会读到事务开始时的数据,不会看到事务 B 插入的新数据。
  • 在可重复读隔离级别下,当执行锁定读语句的时候,它会在读取数据行的同时,也会锁定数据行之前和之后的间隙,防止其他事务在该间隙中插入新的数据行。例如,事务 A 执行 select * from table where a = 40 for update ,此时 InnoDB 不仅会锁住值为 40 的这条记录,还会锁 (20, 40)(40, 60) 这两个间隙。如果事务 B 尝试插入 2141 ,就会因为获取不到锁而失败,从而避免了幻读。

举一个可重复读下出现幻读的例子?

我举例一个可重复读隔离级别发生幻读现象的场景。以这张表作为例子:

picture.image

事务 A 执行查询 id = 5 的记录,此时表中是没有该记录的,所以查询不出来。


        
        
            

          
 # 事务 A
 
          
   

 
          mysql> 
          
 begin
 
          ;
          
   

 
          Query OK, 0 rows affected (0.00 sec)
          
   

 
          
   

 
          mysql> 
          
 select
 
           * 
          
 from
 
           t\_stu 
          
 where
 
           
          
 id
 
           = 
          
 5
 
          ;
          
   

 
          Empty 
          
 set
 
           (
          
 0.01
 
           sec)
          
   

 
        
      

然后事务 B 插入一条 id = 5 的记录,并且提交了事务。


        
        
            

          
 # 事务 B
 
          
   

 
          mysql> 
          
 begin
 
          ;
          
   

 
          Query OK, 0 rows affected (0.00 sec)
          
   

 
          
   

 
          mysql> 
          
 insert
 
           
          
 into
 
           t\_stu 
          
 values
 
          (
          
 5
 
          , 
          
 '小美'
 
          , 
          
 18
 
          );
          
   

 
          Query OK, 1 row affected (0.00 sec)
          
   

 
          
   

 
          mysql> 
          
 commit
 
          ;
          
   

 
          Query OK, 0 rows affected (0.00 sec)
          
   

 
        
      

此时, 事务 A 更新 id = 5 这条记录,对没错,事务 A 看不到 id = 5 这条记录,但是他去更新了这条记录,这场景确实很违和,然后再次查询 id = 5 的记录,事务 A 就能看到事务 B 插入的纪录了,幻读就是发生在这种违和的场景


        
        
            

          
 # 事务 A
 
          
   

 
          mysql> 
          
 update
 
           t\_stu 
          
 set
 
          
 name
 
           = 
          
 '小林coding'
 
          
 where
 
          
 id
 
           = 
          
 5
 
          ;
          
   

 
          Query OK, 1 row affected (0.01 sec)
          
   

 
          Rows matched: 1  Changed: 1  Warnings: 0
          
   

 
          
   

 
          mysql> 
          
 select
 
           * 
          
 from
 
           t\_stu 
          
 where
 
          
 id
 
           = 
          
 5
 
          ;
          
   

 
          +
          
 ----+--------------+------+
 
          
   

 
          | id | name         | age  |
          
   

 
          +
          
 ----+--------------+------+
 
          
   

 
          |  5 | 小林coding   |   18 |
          
   

 
          +
          
 ----+--------------+------+
 
          
   

 
          1 row in 
          
 set
 
           (
          
 0.00
 
           sec)
          
   

 
        
      

整个发生幻读的时序图如下:

picture.image

在可重复读隔离级别下,事务 A 第一次执行普通的 select 语句时生成了一个 ReadView,之后事务 B 向表中新插入了一条 id = 5 的记录并提交。接着,事务 A 对 id = 5 这条记录进行了更新操作,在这个时刻,这条新记录的 trx_id 隐藏列的值就变成了事务 A 的事务 id,之后事务 A 再使用普通 select 语句去查询这条记录时就可以看到这条记录了,于是就发生了幻读。

因为这种特殊现象的存在,所以我们认为 MySQL Innodb 中的 MVCC 并不能完全避免幻读现象

mysql有哪些日志?都有什么作用?

  • redo log 重做日志,是 Innodb 存储引擎层生成的日志,实现了事务中的 持久性 ,主要 用于掉电等故障恢复
  • undo log 回滚日志,是 Innodb 存储引擎层生成的日志,实现了事务中的 原子性 ,主要 用于事务回滚和 MVCC
  • bin log 二进制日志,是 Server 层生成的日志,主要 用于数据备份和主从复制
  • relay log 中继日志,用于主从复制场景下,slave通过io线程拷贝master的bin log后本地生成的日志
  • 慢查询日志,用于记录执行时间过长的sql,需要设置阈值后手动开启

插入一行数据,分别什么时候写入这三个日志?


        
        
            

                  INSERT 事务
          
   

 

 
                     │ 
          
 1
 
          . 生成 Undo Log(记录反向操作)
          
   

 

 
            更新 Buffer Pool 数据页
          
   

 

 
                     │ 
          
 2
 
          . 写入 Redo Log Buffer(物理变更)
          
   

 

 
              「事务提交 Prepare 阶段」
          
   

 

 
                     │ 
          
 3
 
          . Redo Log 刷盘(根据参数配置)
          
   

 

 
              「事务提交 Commit 阶段」
          
   

 

 
                     │ 
          
 4
 
          . 写入 Binlog 并刷盘(根据参数配置)
          
   

 

 
                    完成
          
   

 
        
      

Redo Log

  • 事务执行中 :当执行插入操作时,相关的修改记录会先写入 Redo Log Buffer。
  • 事务提交时 :InnoDB 存储引擎会先将 Redo Log 标记为 “准备提交”(prepare)状态,然后将 Redo Log Buffer 中的内容写入到文件系统的 Page Cache 中,在适当的时候,系统会调用 fsync 将 Page Cache 中的 Redo Log 数据持久化到磁盘中的redolog文件;也有可能日志直接从 Buffer 持久化到了磁盘。可以通过参数 innodb\_flush\_log\_at\_trx\_commit 配置 Redo Log 的写入策略,设置为 1(默认)表示每次事务提交时都将 Redo Log 持久化到磁盘,以保证数据的安全性。

Undo Log

  • 插入数据时 :执行插入操作时,InnoDB 会将插入记录的反向操作(逻辑上可以理解为 “删除” 这个插入记录的操作)记录在 Undo Log 中,用于事务回滚。如果是 InnoDB 引擎,还会根据 MVCC(多版本并发控制)机制,可能会记录一些与版本控制相关的信息。
  • 事务提交时 :在事务提交时,Undo Log 不会被立即删除,而是标记为可清理,后续由 Purge 线程根据情况判断是否可以真正释放 Undo Log 占用的空间。

Binlog

  • 事务执行过程中 :在事务执行插入操作时,相关的 SQL 语句会先被记录到 Binlog Cache 中,Binlog Cache 是每个线程内独立的空间。
  • 事务提交时 :可以通过参数 sync\_binlog 控制 Binlog 的持久化时机, sync\_binlog = 1 表示每次提交事务都要发生 fsync,将 Binlog 从 Page Cache 中真正持久化到磁盘; sync\_binlog = n 表示每次事务都会 write,但是 n 次事务提交会执行 fsync 进行持久化。

线上项目,没有日志,没后台数据的情况下怎么找出问题?

  • 根据用户反馈的信息,尝试在测试环境或本地环境中重现问题。通过模拟用户的操作步骤,可能会发现一些在生产环境中难以察觉的问题。如果能够重现问题,就可以更方便地进行调试和分析。
  • 如果问题是在某个版本发布后出现的,对比当前线上版本与之前正常工作的版本之间的代码差异。查看是否有引入新的功能、修改了配置或依赖关系等,这些变化可能导致了问题的出现。
  • 可以通过生成Heap Dump或者线程转储来分析。例如,使用jstack、jmap这些JDK工具。或者使用Arthas的监控命令,实时查看方法执行情况。还要考虑到网络问题,比如是否有外部服务调用失败,或者资源耗尽的情况,比如数据库连接池满了。这时候可能需要检查系统资源,如CPU、内存、磁盘IO、网络流量等,使用top、netstat、ss等命令。

算法

  • 算法:lc415字符串相加

最后欢迎

加入苏三的星球

,你将获得:AI开发项目课程、苏三AI项目、

商城微服务实战、秒杀系统实战

商城系统实战、秒杀系统实战、代码生成工具、系统设计、性能优化、技术选型、底层原理、Spring源码解读、工作经验分享、痛点问题

、面试八股文

等多个优质专栏。

还有1V1答疑、修改简历、职业规划、送书活动、技术交流。

扫描下方二维码,即可加入星球:

picture.image

目前星球已经更新了 5200+ 篇优质内容,还在持续爆肝中.....

星球已经被 官方推荐了3次 ,收到了小伙伴们的一致好评。戳我加入学习,已有 1600+ 小伙伴加入学习。

picture.image

苏三的免费八股文网站:

www.susan.net.cn

picture.image

0
0
0
0
关于作者
关于作者

文章

0

获赞

0

收藏

0

相关资源
如何利用云原生构建 AIGC 业务基石
AIGC即AI Generated Content,是指利用人工智能技术来生成内容,AIGC也被认为是继UGC、PGC之后的新型内容生产方式,AI绘画、AI写作等都属于AIGC的分支。而 AIGC 业务的部署也面临着异构资源管理、机器学习流程管理等问题,本次分享将和大家分享如何使用云原生技术构建 AIGC 业务。
相关产品
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论