写点什么

腾讯提前批是真难

作者:王中阳Go
  • 2025-08-11
    北京
  • 本文字数:4804 字

    阅读完需:约 16 分钟

腾讯提前批是真难

前面几天分享了拼多多面经,有些朋友觉得挺简单的;今天分享一下腾讯提前批的面经,这个有点难度:

键入一个域名,整体怎么做流转的,要很详细

当你在浏览器输入一个域名(比如www.baidu.com),整个流程大概是这样的:


  1. 本地缓存查询:浏览器先查自己的缓存(比如 Chrome 的 DNS 缓存),看看有没有这个域名对应的 IP。如果有,直接用这个 IP;没有就往下走。

  2. 操作系统缓存查询:浏览器查不到,会问操作系统(比如 Windows 的 hosts 文件或系统 DNS 缓存),如果操作系统有记录,直接返回 IP。

  3. 路由器缓存查询:还没有的话,请求会发到家里或公司的路由器,路由器也有自己的 DNS 缓存,有就直接返回。

  4. DNS 服务器迭代查询:前面都没命中,就会向 ISP(网络服务提供商,比如联通、电信)的本地 DNS 服务器发起请求。本地 DNS 服务器如果有缓存就返回,没有的话就开始 “迭代查询”:

  5. 先问 “根域名服务器”(全球共 13 组):.com这个顶级域名由哪些服务器管理?

  6. 根服务器返回.com顶级域名服务器的 IP。

  7. 本地 DNS 再问.com服务器:baidu.com这个二级域名由哪些服务器管理?

  8. .com服务器返回baidu.com的权威 DNS 服务器 IP。

  9. 本地 DNS 最后问baidu.com的权威 DNS 服务器:www.baidu.com对应的 IP 是什么?权威服务器返回具体 IP(比如180.101.50.242)。

  10. 建立 TCP 连接:浏览器拿到 IP 后,通过 TCP 三次握手和服务器建立连接(如果是 HTTPS,还要多一步 TLS 握手,协商加密方式、交换密钥)。

  11. 发送 HTTP 请求:连接建立后,浏览器发送 HTTP 请求(包括请求行、请求头、请求体),比如GET /index.html HTTP/1.1

  12. 服务器处理请求:服务器收到请求后,解析请求内容,找到对应的资源(比如静态页面、接口数据),处理后生成 HTTP 响应(状态码、响应头、响应体)。

  13. 返回响应并渲染:服务器把响应通过 TCP 连接发回浏览器,浏览器接收后解析 HTML、CSS、JavaScript,渲染页面,展示给用户。

  14. 关闭连接:如果是 HTTP/1.1 的Connection: close,数据传输完就断开 TCP 连接;如果是keep-alive,连接会保持一段时间,方便后续请求复用。

http 协议中,对于粘包问题,我们可以怎么解决?追问:在 http 协议中,怎么判断拆包后组装后是组装完了,而不是一部分

首先得说,HTTP 基于 TCP,而 TCP 本身是 “流式协议”,没有消息边界,所以可能出现粘包(多个请求 / 响应粘在一起)或拆包(一个请求 / 响应被分成多段)。但 HTTP 协议本身设计了机制来解决这个问题:


解决粘包的核心思路:明确告诉接收方 “一个完整的 HTTP 消息有多长”,主要有 3 种方式:


  1. Content-Length:在响应头里加Content-Length: 1024,表示响应体大小是 1024 字节。接收方收到这么多字节后,就知道这个消息结束了。

  2. 分块传输(Transfer-Encoding: chunked):如果响应体大小不确定(比如动态生成的内容),用分块传输。每个块开头会标明当前块的大小(十六进制),最后用一个 “0 长度块”(0\r\n\r\n)表示结束。

  3. Connection: close:HTTP/1.0 默认用这个,服务器处理完请求后直接关闭 TCP 连接。接收方发现连接断了,就知道消息结束了(但效率低,不推荐)。


追问答案:判断组装完成的依据就是上面这几种机制:


  • 如果有Content-Length,接收方累计收到的字节数等于这个值,就说明组装完了;

  • 如果是分块传输,收到 “0 长度块” 后,说明整个消息结束;

  • 如果是Connection: close,TCP 连接关闭时,就认为消息结束。

尝试推导 redis 是怎么做分布式的,如何保证写入相同数据库,即使某些库发生了崩溃,数据仍然存在

Redis 的分布式方案,核心是 “分片” 和 “高可用”,保证数据一致性和可靠性:


  1. 分布式架构基础

  2. 分片(Sharding):把数据分散到多个 Redis 实例,避免单实例压力过大。比如用 “哈希槽”(Redis Cluster 默认 16384 个槽),每个 key 通过CRC16(key) % 16384计算属于哪个槽,每个实例负责一部分槽。

  3. 主从复制:每个主节点(Master)有多个从节点(Slave),从节点通过SYNC命令复制主节点的数据,实现读写分离(读请求可以走从节点)。

  4. 保证写入相同数据库(数据一致性)

  5. 客户端写入时,会根据 key 的哈希槽找到对应的主节点,直接写入主节点,再由主节点异步同步给从节点(默认)。如果需要强一致性,可以用WAIT命令,等待至少 N 个从节点确认收到数据后再返回。

  6. 崩溃后数据仍存在

  7. 持久化:通过 RDB(定时快照)和 AOF( Append Only File,记录每一条写命令)把内存数据存到磁盘,崩溃后重启可以恢复。

  8. 哨兵(Sentinel):监控主从节点状态,当主节点崩溃,哨兵会从从节点中选举新的主节点,自动完成故障转移,保证服务不中断。

  9. 集群模式:Redis Cluster 中每个槽至少有 1 个主节点和 1 个从节点,主节点挂了,从节点会晋升为主节点,数据不会丢失(只要不是所有副本都挂了)。

raft 协议里面为什么是 n/2+1 认为 ok

Raft 是分布式系统里的共识算法,核心是通过 “多数派”(n/2+1)来保证一致性,原因很简单:避免 “分裂脑”(split brain),确保只有一个合法的领导者(Leader)


举个例子:如果集群有 3 个节点,n/2+1=2。只要有 2 个节点同意,就能达成共识。这时候即使 1 个节点故障,剩下 2 个还能正常工作;如果有 2 个节点分别认为自己是 Leader,它们都需要争取多数派支持,但 3 个节点里最多只有 1 个能拿到 2 票(多数),另一个最多拿 1 票,所以不会出现两个 Leader 同时合法的情况。


再比如 5 个节点,需要 3 票同意。即使 2 个节点故障,剩下 3 个还能工作;且任何情况下,最多只有一个节点能拿到 3 票,保证一致性。如果用 “半数”(n/2),3 个节点只要 2 票,但 2 票可能出现两个节点各拿 2 票的情况(比如网络分区时),导致分裂脑。所以 n/2+1 是最小的 “多数派”,既能容忍部分节点故障,又能避免冲突。

a 函数调用 b 函数,汇编角度怎么发生的

从汇编角度看,函数调用本质是 “栈操作 + 跳转”,步骤大概是这样(以 x86 架构为例):


  1. 准备参数:把 b 函数需要的参数,按调用约定(比如 cdecl 是从右到左)压入栈中(push arg3; push arg2; push arg1)。

  2. 调用 b 函数:执行 call b 指令,这个指令会做两件事:

  3. 把当前指令的下一条地址(返回地址,也就是 a 函数调用 b 之后要执行的代码位置)压入栈中;

  4. 跳转到 b 函数的入口地址(即 b 函数的第一条指令)。

  5. b 函数的 “序言”(Prologue):进入 b 函数后,先保存当前栈帧,为局部变量腾空间:

  6. push ebp:把 a 函数的栈基址(ebp 寄存器)压入栈,保存上下文;

  7. mov ebp, esp:把当前栈顶(esp 寄存器)的值赋给 ebp,作为 b 函数的栈基址;

  8. sub esp, N:从栈顶往下挪 N 个字节,给 b 函数的局部变量分配空间。

  9. 执行 b 函数逻辑:执行 b 函数的具体代码,可能会读写局部变量(通过 ebp 偏移访问,比如mov eax, [ebp-4])、调用其他函数等。

  10. b 函数的 “尾声”(Epilogue):执行完后,恢复栈帧,准备返回:

  11. mov esp, ebp:把栈顶恢复到 b 函数的栈基址,释放局部变量空间;

  12. pop ebp:把之前保存的 a 函数的 ebp 弹回寄存器,恢复 a 函数的栈帧;

  13. 返回 a 函数:执行ret指令,从栈顶弹出之前保存的 “返回地址”,然后跳转到这个地址,继续执行 a 函数剩下的代码。

redis push 命令怎么做幂等

Redis 的lpushrpush这些 push 命令默认不是幂等的 —— 重复执行会多次往列表里加元素(比如lpush list 1执行两次,列表会有两个 1)。要实现幂等,核心是让重复执行的命令产生相同的结果,可以这样做:


  1. 给元素加唯一标识:比如每个要 push 的元素带一个唯一 ID(如 UUID),push 前先查列表里有没有这个 ID(用lrange遍历或lpos查找),有就不推,没有再推。但遍历列表效率低,适合短列表。

  2. 用 Lua 脚本原子执行:把 “检查是否存在 + push” 做成一个 Lua 脚本(Redis 保证脚本原子性),比如:


   if redis.call('lpos', KEYS[1], ARGV[1]) == nil then     return redis.call('lpush', KEYS[1], ARGV[1])   else     return 0   end
复制代码


这样重复调用脚本,只会 push 一次元素。


  1. 业务层去重:客户端自己记录已经 push 过的元素 ID,发送前先检查本地记录,避免重复发送。

  2. 用集合(Set)代替列表:如果业务允许,用SADD(集合的添加命令,本身是幂等的,重复加同一个元素只会存一次),但集合是无序的,适合不需要顺序的场景。

两张一亿条的 excel 表,主键相同,怎么合并写入磁盘

一亿条数据太大,内存肯定装不下,核心思路是流式处理 + 外部排序,步骤如下:


  1. 格式转换:Excel 文件本身不适合处理超大数据,先转成 CSV(纯文本,读写快),用工具(如 Python 的 pandas、Java 的 POI)按行读取,避免加载全部数据。

  2. 分块排序:

  3. 把每张表分成多个小块(比如每个块 100 万条),加载到内存后按主键排序,然后写入临时文件(比如表 1 的临时块tmp1_1.csvtmp1_2.csv,表 2 的tmp2_1.csvtmp2_2.csv)。

  4. 合并排序后的块:

  5. 对表 1 的所有临时块做 “归并排序”,得到一个全局按主键排序的表 1(sorted1.csv);同理处理表 2 得到sorted2.csv

  6. 双指针合并:

  7. 同时打开 sorted1.csv 和 sorted2.csv,用两个指针分别读取记录,比较主键:

  8. 主键相同:合并两行数据(比如取表 1 的某些字段 + 表 2 的某些字段),写入结果文件;

  9. 主键不同:把小的那个主键的记录写入结果文件,移动对应指针;

  10. 这样从头到尾流式处理,不需要加载全部数据到内存。

  11. 写入磁盘:结果文件按批次写入(比如每 10 万条刷一次盘),避免频繁 IO,最后可以转成 Excel 或保留 CSV(看需求)。

pagecache 是什么,好处和坏处?如何绕过 pagecahce 直接写入磁盘

PageCache(页缓存) 是操作系统在内存中开辟的一块区域,用来缓存磁盘上的文件数据。程序读写文件时,先和 PageCache 交互,再由操作系统异步把 PageCache 的数据刷到磁盘。


好处


  • 速度快:内存读写比磁盘快 1000 倍以上,重复读写同一数据时,直接从 PageCache 取,减少磁盘 IO;

  • 优化 IO:操作系统会合并多个小写操作(比如多次写 1KB,合并成一次写 4KB),减少磁盘寻道次数;

  • 顺序读写友好:PageCache 会预读相邻数据(比如读了第 1 页,自动预读第 2、3 页),适合日志、视频等顺序访问场景。


坏处


  • 占用内存:如果缓存太多,会挤掉应用程序的内存,导致频繁 GC 或换页(swap);

  • 数据丢失风险:数据存在 PageCache 中但没刷到磁盘时,突然断电会丢失;

  • 写延迟:异步刷盘可能导致数据在内存中滞留,需要主动调用fsync才能保证持久化。


绕过 PageCache 的方法


  • Linux 下打开文件时用O_DIRECT标志(直接 IO),比如open("file.txt", O_WRONLY | O_DIRECT),数据直接写入磁盘,不经过 PageCache;

  • 数据库常用这种方式(比如 MySQL 的innodb_flush_method=O_DIRECT),避免 PageCache 和数据库自身缓存(如 InnoDB Buffer Pool)重复缓存数据;

  • 注意:直接 IO 要求数据对齐(比如按扇区大小对齐),否则会报错,且性能不一定更好(少了缓存优化),适合对数据一致性要求极高的场景(如金融交易)。

设计模式中的开放关闭原则是什么

开放关闭原则(OCP,Open-Closed Principle)是设计模式的核心原则之一,简单说就是:软件实体(类、模块、函数等)应该对扩展开放,对修改关闭


意思是,当需要新增功能时,应该通过 “扩展已有代码”(比如加新类、新方法)来实现,而不是修改原来已经写好且稳定运行的代码。


举个例子:如果有个Shape类,原来支持圆形(Circle)和矩形(Rectangle)的面积计算。现在要加三角形(Triangle),按照 OCP,应该新增一个Triangle类继承Shape,实现自己的getArea()方法,而不是去修改Shape或已有的CircleRectangle类。


这样做的好处是:减少修改旧代码带来的风险(比如改坏原来的功能),提高代码复用性和可维护性,尤其适合大型项目。

算法:(1)字符串转 16 进制,并且 16 进制转字符串 (2)实现 lru,并且 key.size << value.size

欢迎关注 ❤

我们搞了一个免费的面试真题共享群,互通有无,一起刷题进步。


没准能让你能刷到自己意向公司的最新面试题呢。


感兴趣的朋友们可以加我微信:wangzhongyang1993,备注:面试群。

发布于: 刚刚阅读数: 4
用户头像

王中阳Go

关注

靠敲代码在北京买房的程序员 2022-10-09 加入

【微信】wangzhongyang1993【公众号】程序员升职加薪之旅【成就】InfoQ专家博主👍掘金签约作者👍B站&掘金&CSDN&思否等全平台账号:王中阳Go

评论

发布
暂无评论
腾讯提前批是真难_程序员_王中阳Go_InfoQ写作社区