写在 2022 年末的字节跳动面试复盘

背景

朋友经常跟我说要注意大环境,多看看身边优秀的人活得怎么样,不是说总要比来比去,但是也不能沉溺在舒适圈里,所以偶尔,偶尔会有一些跳出合同跟新球队聊聊加盟的想法。当然,想法在过去很长一段时间都只是想法,直到秋天的几轮裁员,心里才跟自己说:要不面面看?

然而现实很残酷,下半年(特别是 Q4)通常都是电商的发力时间段,让本就不充裕的准备时间更是捉襟见肘,直到面试前两周,征询了一些朋友的建议,决定开摆,看看日常的积累能抵多少的八股文。

面试

由于快到年末了,大家都很忙,所以 HR 微信约了下班后的一面时间。

一面(1 小时 20 分钟)

  • 自我介绍
  • 追踪完整的推广链路(进行分佣)是怎么做的呢?
    • 这里主要是业务介绍;
  • 推广的链接里面会带用户的信息吗?
  • 短链接是自己做的吗,具体是怎么映射长链和跳转的?
  • 这个推广的提成是按照订单维度来算的吗?
  • 这种模式会存在商家作弊吗?
    • 存在风险;
    • 由风控团队处理。具体发生在数据采集到数仓之后,由他们团队处理完最后再把结果给回来;
  • 这个短链的长度是固定的吗,是怎么设计的?
    • 不是固定的,但是都在 i ~ j 个字符长度之间;
    • 尽管明文的长度会有浮动,但是二进制的长度是固定的,相当于我们生成了唯一的二进制数,再通过 base62 编码回英文 + 数字的形式;
    • 二进制数则是分成了几段:有 x 位是版本号,z 位是雪花算法;雪花算法中则包含了 z1 位机器号,z2 位时间戳,z3 位自增数;
  • 跳转的时候用的是 301 还是 302 跳转?为什么?
    • 302(想回答的是 301,具体看下面);
    • 不会,但应该是依靠语义上的选择,我们想表明这个 Page 是永久重定向;
  • 短链数据为什么从 TiDB 迁移到 MySQL?
    • (公司层面)全局部署资源的问题、资源共用的问题;
    • (业务层面)性能问题;
  • 短链迁移到 MySQL 做的分库分表依据是什么?
    • 从使用的视角考虑,用户访问的短链提供的信息只有短链 ID,因此查询条件只有短链 ID,分库分表自然也依据短链 ID 来做;
    • 最早设计的方案是用短链 ID 做哈希,全部均匀打散到不同库表,但是发现不能适应批量创建短链的场景,因为需要非常散列的库表操作;
    • 为了利用上批量写入,依照短链中的时间戳做哈希,再分散到不同的库表去,使得同一秒内的数据可以落入相同的表内;
  • 双写的时候怎么保证 TiDB 和 MySQL 数据一致?
    • 没有办法严格保证;
    • 双写过程会分成几个阶段,首先 TiDB 写入成功即代表成功,MySQL 也会写入,但仅作为稳定性检测,写入失败会告警,但不影响返回;
    • 当确认 MySQL 写入稳定无异常后,进入下一个阶段,仅 TiDB 和 MySQL 均成功时才返回给用户,因此用户拿到的数据一定同时存在 TiDB 和 MySQL;
    • 用户访问的时候做双读,以某一个数据源为返回依据,另一个数据源仅作为对比用,对比不正确则告警(增量数据对比);
    • 对于存量的老数据,因为缺少用户访问,所以不能通过双读对比,而是用数仓做离线对比;
  • 短链有典型的冷热数据特点,怎么处理的?
    • 老数据不是永久可用的,过了有效期会被清理,仅在离线数仓可见;
    • 普通数据有三级缓存机制,查询路径依次为:本地内存、Redis、MySQL,每级查询的结果都要向上回填(不管是查到还是查不到),并介绍各级目前的命中率情况;
  • 实时核对系统是怎么做的呢?
    • 比如说有两个 DB,我想比对他们的变更情况,例如 A DB 的一条数据状态变了,B DB 的一条数据状态也变了,那我就监听这两个 DB 的 binlog,下发到 Kafka,核对系统消费两个 Kafka 的信息;
    • 先被消费到的信息会存放再 Redis 和延迟队列;
    • 后被消费到的信息会尝试在 Redis 里面找一下,如果找到,则拉出来核对(、如果不一致则告警)并删除;如果找不到,要么是已经核对完了,要么是已经超时了,分情况处理;
  • 如果两个系统间的链路太长,数据变更时间很长或者数据丢了,那核对系统要以哪边的数据为准(去修改另一个系统)呢?
    • 核对系统仅做核对和告警,不进行数据回填、回刷等修数据操作;
  • OpenTracing 这个是基架做的吗,为什么业务团队也要去搭呢?
    • 服务间 RPC 调用基架能覆盖到,业务团队希望看到所有的调用,包括外部 HTTP、调用 Redis、调用 DB 等都想看到各自的耗时,因此对 Redis、DB 的 Client 又补全了对应的 Tracing 上报插件;
    • 基础架构的视角和业务应用的视角不同,业务需要有自己的视角,例如中间件看到的调用耗时跟业务看到的调用耗时差异很大,那就可以去做分析推测,是不是中间存在网络的延迟导致;
  • 加入了这么多上报会不会对性能有影响?
    • OpenTracing 的上报是基于 UDP 的,开销比基于 TCP 的 HTTP 上报小;
    • 这个话题还可以拓展到 Grafana(答错了,其实是 Prometheus)的上报,它不主动上报,而是把采集到的 Metrics 放在内存,同时提供接口供宿主机进行固定间隔的拉取;
    • 业务观测到的性能损失微乎其微;
  • Tracing 有一定的采样率,如果调用链路中某个服务有报错,它并不一定能被采样到。怎样能使报错的调用都被采样到呢?
    • 如果知道完整的调用链路,知道调用链路入口,可以在入口处做隐藏的 debug 开关,例如在 HTTP header 或参数中加特殊标记,保证这次请求一定能被采样;
    • 如果只关心调用链路中的部分服务,也可以指定特定服务的全量采样,这样即使上下游都只有部分采样,也可以 100% 看到某个服务的所有调用,只是缺失了上下游信息;
    • 最后针对错误和慢查询,还可以做尾部采样,在服务上报和最终聚合之间加入中间层。我们知道 100% 上报的主要压力是来自于存储的磁盘,那其实 100% 上报并不代表需要 100% 存储,中间层进行内存过滤,仅保留包含错误的 Trace;
  • binlog 有哪几种模式?
    • 3 种模式,直白地说就是仅记录变更数据、记录整行数据和两者混合;
    • 用 canal 的话一般需要设置成 ROW 模式;
  • 为什么需要不同模式?
    • 不了解,但是可以猜一下;
    • STATEMENT 模式实际上是数据变更的语句,给下游做重放的时候需要时间;
    • ROW 模式包含了所有的数据,可能下游能更快完成变更;
    • (以上猜测都是错的)
  • 主机房执行了一个事务,同步到从机房,它的事务性能保证吗?
    • binlog 中会带有事务 ID;
    • 重放过程中是逐句重放的可能会出现不一致,但是最终会到达一致状态;
  • 什么是覆盖索引?
    • 覆盖索引是个逻辑上的概念;
    • 如果一个查询能从某个索引的 B+ 树上拿到所有的数据不需要回表等操作,这个索引就称为这个查询的覆盖索引;
  • 了解索引下推吗?什么情况下会下推到引擎去处理?
    • 通过某个索引没办法按顺序地覆盖所有的查询条件,但是仍然可以利用索引内存在的字段(尽管不是有序的,需要扫描)去进一步过滤;
    • 举例:idx(a,b,c,d),查询条件为 a=? and b=? and d=?,发生下推减少回表数量;
  • 什么场景下索引会失效?
    • 场景有很多,但是如果我是一个引擎,我关注的不是什么情况会失效,而是走什么路径所花费的随机 I/O 和顺序 I/O 最少,如果走某个索引花费的随机 I/O 比从聚簇索引(顺序)查(成本)都还要高,那还不如直接去全表扫描;
    • 典型例子:捞超过全表 30% 的数据;
  • 有没有具体一点的例子?
    • 还是刚刚提到的例子,比如说我要按照 update_time 去做范围查询,捞很多的数据,即使 update_time 有索引,也会选择全表扫描;
  • WHERE id NOT IN (?, ?, ?) 会走索引吗?
    • 还是要看成本;
    • 举例:id 字段只包含 3 个值,1、2、3,3 只有几行,而 1、2 各有 100w 行,如果查询条件是 NOT IN (1, 2) 会走索引,如果查询条件是 NOT IN (3) 不会走索引;
  • Redis 使用的过程中有碰到过一些热 Key、大 Key 的问题吗?
    • 有,能自行定位到的都业务自行处理了;
    • 如果不知道是哪个 Key 导致的,则 rdbdump 导出之后做(离线)分析,看看哪个 Key(哪些 Key 前缀)占的空间特别大,再将他们做哈希、打散等处理;
  • rdb 导出会不会太慢了,比如说里面有一两亿的数据,里面会包含了一些大 Key,怎么样更快找出来?
    • 数据导出会从从节点上导出,不会很慢;
    • 如果觉得导出还是太慢,可以看看 Redis 是否有提供什么命令直接找,印象中有命令能找出 Value 大于特定值的 Key;
  • 如果有一些热点 Key,比如某个链接被明星分享了,访问就会很频繁,怎么办?
    • Redis 存放的就该是热点数据,所以觉得这个应该是服务它的使用场景的;
    • 如果热点数据里面的 Value 太大了,因为它是单线程操作的,可能会导致一些压力,可以尝试使用 Redis 6 的多线程来缓解网络 I/O 上的压力;
  • 如果定位到了热 Key 之后怎么防止它把单个节点打爆呢?
    • 减少 Value 的体积然后用 Redis 做索引,把实际的内容放到其他存储去;
    • (答非所问)
  • 用 RAND(5) 实现 RAND(7)?
    • 眼熟,但是不会;
  • A 和 B 从一堆棋子中轮流取,每次需要取走 1-5 个棋子,A 先取,如何必胜?
    • 眼熟,讨论了一会;
  • 翻转链表中的第 m 至 n 的节点
    • 不难;

反问环节

  • 团队里面大约有多少个仓库、部署了多少服务(大致规模)?
  • 这些项目会做单元测试吗,或者说平时的自测、代码质量保障是通过什么方式做的?
    • 工具类的库会写单测;
    • 自动化测试会由流量回放平台,QA 执行常用的用例;
  • 整个流量回放都是 QA 负责的吗?
    • 后端需要在代码里面做配合;
  • 一个业务迭代版本是怎么安排时间发布的呢,比如说是每周固定发布还是说测试完成后的几天发布?
    • 按双月来排期,产品拟定优先级, 研发扣除 oncall 等时间分配任务;
  • oncall 那周安排多少时间精力做 oncall 和其它问题?
    • 80% 时间在 oncall,剩下的时间会看情况修复一下线上和其他的小问题;
  • 前面很多问题都没有答对,面试官的建议?

第二天收到 HR 的电话约了二面时间。

二面(1 小时 5 分钟)

  • 说一下短链接业务的背景、上下文?
  • 为什么要用短链接,短链接到底解决什么问题?
  • 有没有看过这些短链带来的点击或者成交量有多少?
  • 造成到达率只有 50% 不到(商品页浏览数/短链点击数)原因是什么呢?
  • 项目的机房部署是怎么样的?
  • 东南亚到美洲的机房时延大概是多少?
  • 项目如果跨机房部署,会遇到什么技术问题呢?
  • 在项目中是主导设计还是主力开发?
  • 这个系统是你从 0 到 1 做起来的吗?
  • 能说一下项目的系统架构吗?
  • 短链从用户分享、短链生成到用户点击在系统里面的工作过程?
  • 301 跟 302 跳转有什么区别,具体会发生什么事情?
  • 301 或 302,以及还有一种链接不会发生变化的跳转,具体在 HTTP 协议中是什么命令?
  • 在做短链系统里面哪些地方是技术难点?
    • 短链 ID 如何设计;
    • 短链系统是流量入口,因此重点关注高可用;
    • 访问数据实时和离线的分析;
  • 分库分表是怎么做的,为什么需要分库分表?
    • 数据增长较快;
    • 单点的访问模式,保持较小的 B+ 树有利于访问性能;
  • 数据增长快,为什么不考虑冷热数据分离,定期归档?
  • 业务场景里面不太可能有统计,实际上都是 KV 查询,压力应该不大,需要分表吗?
  • TiDB 迁移到 MySQL 后耗时大幅降低是做了什么优化?
  • 这种场景里面写入耗时长可否容忍?
  • 在这个大量 KV 数据读写的场景,为什么不考虑直接使用 KV 存储或者 Redis 来支撑?
  • 为何不考虑数据仓库提供 HBase 来支持读写,节约掉 MySQL / KV 存储同步到数仓的过程?
  • 能说一下 LevelDB 的存储结构吗?
  • 实现 Redis 跳跃表
  • 为什么考虑换工作?
  • 未来 4 年的规划?

反问环节

  • 在字节的技术人对于公司的基础架构的满意程度如何?
    • 该有的都有;
  • 外出参加技术会议、交流在字节里机会是否丰富?
  • 前面很多问题都没有答对,面试官的建议?

2022-12-28 16:14 +0800