腾讯 C++开发 一面

PCG

1. 自我介绍

2. 网络服务中大量 CLOSE_WAIT 和 TIME_WAIT 分别是什么原因,会导致什么问题,怎么解决

答案:CLOSE_WAIT 通常表示对端已经关闭连接,本端收到了 FIN,也回了 ACK,但本端应用层还没有调用 close()。如果大量 CLOSE_WAIT 堆积,一般说明服务端代码没有正确关闭 socket,可能是读到 0 后没有 close,异常分支漏 close,或者连接对象生命周期管理有问题。它会导致 fd 泄漏,最终可能出现 too many open files

TIME_WAIT 一般出现在主动关闭连接的一方。它的作用是保证最后一个 ACK 能被对端收到,并让旧连接中的延迟报文在网络中自然消失。大量 TIME_WAIT 不一定是 bug,但如果短连接很多,会占用端口和连接表资源。

解决上,CLOSE_WAIT 要从代码里查连接关闭路径,保证读到 EOF、异常、超时、协议错误时都能释放 fd。TIME_WAIT 要看业务是否能复用长连接、连接池,减少频繁建连断连;必要时调整系统参数,但不能把系统参数当成根因解决。

代码:

ss -ant | awk '{print $1}' | sort | uniq -c
ss -antp | grep CLOSE-WAIT
lsof -p <pid> | wc -l
cat /proc/sys/fs/file-nr

3. 创建 socket 时常见参数有哪些,SO_REUSEADDRSO_REUSEPORTTCP_NODELAY 分别解决什么问题

答案:创建 TCP socket 一般会经历 socket()bind()listen()accept()socket(AF_INET, SOCK_STREAM, 0) 中,AF_INET 表示 IPv4,SOCK_STREAM 表示 TCP 字节流,第三个参数通常填 0 表示使用默认协议。

SO_REUSEADDR 主要用于允许地址复用,常见场景是服务重启时端口还处于 TIME_WAIT,设置后可以更快 bind 成功。SO_REUSEPORT 可以让多个进程或线程绑定同一个端口,由内核做连接负载分发,适合多进程网络服务。TCP_NODELAY 用来关闭 Nagle 算法,减少小包延迟,适合实时交互场景,但可能增加小包数量。

还要关注 SO_KEEPALIVE、收发缓冲区大小、非阻塞模式、listen backlog,这些参数都会影响服务在高并发下的行为。

代码:

#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <fcntl.h>
#include <unistd.h>

int createServerSocket(int port) {
    int fd = socket(AF_INET, SOCK_STREAM, 0);

    int on = 1;
    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
    setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on));
    setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));

    int flags = fcntl(fd, F_GETFL, 0);
    fcntl(fd, F_SETFL, flags | O_NONBLOCK);

    sockaddr_in addr{};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = INADDR_ANY;

    bind(fd, (sockaddr*)&addr, sizeof(addr));
    listen(fd, 1024);

    return fd;
}

4. TCP 监听队列里半连接队列和全连接队列有什么区别,backlog 参数到底控制什么

答案:TCP 服务端 listen 后,内核会维护连接相关队列。半连接队列存放已经收到 SYN、并回复 SYN+ACK,但三次握手还没完全完成的连接。全连接队列存放三次握手已经完成,但还没被应用层 accept() 取走的连接。

backlog 在不同内核版本里含义有细节差异,但实际排查时要同时关注半连接队列、全连接队列以及系统参数,比如 somaxconntcp_max_syn_backlog。如果应用层 accept 太慢,全连接队列可能溢出;如果遭遇 SYN flood,半连接队列可能被打满。

全连接队列溢出会导致客户端连接失败或重传,服务端可能看不到明显应用日志。排查时不能只看应用层 QPS,还要看内核统计。

代码:

sysctl net.core.somaxconn
sysctl net.ipv4.tcp_max_syn_backlog
ss -lnt
netstat -s | grep -i listen

5. MySQL 中 B 树和 B+ 树有什么区别,为什么 InnoDB 索引常用 B+ 树

答案:B 树的非叶子节点和叶子节点都可以存数据,而 B+ 树通常只有叶子节点存完整数据,非叶子节点主要存 key 和子节点指针。B+ 树的叶子节点之间还有链表连接,非常适合范围查询,比如 betweenorder by、分页扫描。

InnoDB 使用 B+ 树,一个重要原因是磁盘 IO 友好。非叶子节点不存完整行数据,可以容纳更多 key,树高更低,查询时磁盘访问次数更少。另外叶子节点有序链表让范围查询效率更高。如果用普通二叉树或红黑树,树高太高,不适合磁盘页访问模型。

聚簇索引的叶子节点存整行数据,二级索引叶子节点存主键值,所以二级索引查到主键后,如果查询列不在索引里,还需要回表。

6. InnoDB 聚簇索引和二级索引有什么区别,为什么建议主键量自增

答案:InnoDB 的表数据本身按照主键组织,主键索引就是聚簇索引。聚簇索引叶子节点存放完整行数据。二级索引的叶子节点不直接存完整行,而是存索引列和主键值。如果通过二级索引查找非覆盖字段,需要先查二级索引拿到主键,再回到聚簇索引查整行,这就是回表。

主键建议尽量短、稳定、递增。短是因为所有二级索引叶子节点都会存主键,主键太大会放大索引体积。稳定是因为主键变化会导致数据位置变化。递增是因为随机主键会导致页分裂和页内碎片,影响插入性能。不过自增主键也有热点写入问题,在极高并发写入场景下需要结合业务分库分表或其他 ID 生成策略。

7. MySQL 联合索引的最左前缀原则是什么,哪些情况会导致索引失效

答案:联合索引比如 (a, b, c),查询条件要从最左边的 a 开始连续使用,才能较好利用索引。如果直接查 bc,通常无法完整利用这个联合索引。遇到范围查询时,范围字段后面的字段一般不能继续用于有序定位,只能做过滤。

常见索引失效情况包括:对索引列做函数计算、隐式类型转换、前置模糊匹配、使用不符合索引顺序的条件、低选择度字段单独建索引、or 两侧索引不一致等。但“索引失效”不是绝对说法,最终还要看优化器选择,可能因为数据量小、选择度差,优化器认为全表扫描更便宜。

代码:

CREATE INDEX idx_user_scene_time ON t_event(user_id, scene_id, create_time);

-- 可以较好利用 user_id、scene_id、create_time
SELECT * FROM t_event
WHERE user_id = 1001
  AND scene_id = 8
  AND create_time >= '2025-01-01';

-- 不符合最左前缀,通常无法有效使用该联合索引
SELECT * FROM t_event
WHERE scene_id = 8;

-- 对索引列做函数,可能导致无法走索引
SELECT * FROM t_event
WHERE DATE(create_time) = '2025-01-01';

8. MySQL 慢查询怎么排查,EXPLAIN 里重点看哪些字段

答案:慢查询排查一般先打开慢查询日志,确认 SQL、耗时、扫描行数和执行频率。然后用 EXPLAIN 看执行计划,重点关注 typekeyrowsExtratype 越接近 const/ref/range 通常越好,如果是 ALL 就是全表扫描。key 表示实际使用的索引,rows 是优化器估算扫描行数,Extra 里如果出现 Using filesortUsing temporary,要重点关注排序和临时表成本。

慢查询不一定只是没索引,也可能是索引选择度太差、返回数据太多、分页 offset 太大、锁等待、buffer pool 命中率低、SQL 写法导致无法走索引。优化时不能只靠感觉加索引,要结合数据分布和执行计划。

代码:

EXPLAIN SELECT id, title
FROM t_article
WHERE author_id = 100
  AND status = 1
ORDER BY publish_time DESC
LIMIT 20;

SHOW VARIABLES LIKE 'slow_query_log';
SHOW VARIABLES LIKE 'long_query_time';
SHOW PROCESSLIST;

9. MySQL 中 MVCC 是怎么实现的,快照读和当前读有什么区别

答案:InnoDB 的 MVCC 依赖隐藏字段、undo log 和 ReadView。每行记录里会有事务 ID 和回滚指针,回滚指针指向 undo log 中的历史版本。快照读执行时会生成 ReadView,根据活跃事务列表、最小活跃事务 ID、下一个事务 ID 等信息,判断某个版本对当前事务是否可见。如果当前版本不可见,就沿着 undo log 找更老的版本。

普通 select 通常是快照读,不加锁,读的是符合可见性规则的历史版本。select ... for updateup

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

C++ 常考面试题总结 文章被收录于专栏

本专栏系统梳理C++方向, 大中厂高频高频面试考点 , 内容皆来自真实面试经历,从基础语法、内存管理、STL与设计模式,到操作系统与项目实战,结合真实面试题深度解析,帮助开发者高效查漏补缺,提升技术理解与面试通过率,打造扎实的C++工程能力.

全部评论

相关推荐

05-25 11:12
已编辑
中国海洋大学 C++
bg:双非本984.5硕c++岗位:客户端开发(后端挂完两次后转战客户端)5.20一面:上来给半小时时间写两道算法(客户端也要写这么多算法??):1.LRU:写get和put操作。鼠鼠我忘了迭代器怎么写了,加上没时间了然后写了个o(n)解法,最后回答有没有更好的解法解释了下正解。2.计算a+b:但只能用位运算。鼠鼠用最笨的方法将数字拆成数组然后依次判断,完全不知道可以用循环异或解QAQ。最后提问如何计算负数的时候炸了(没答出来*1)然后就是八股:1.虚函数是做什么用的?虚函数是每个类有一个还是每个对象有一个?虚析构函数作用?2.Lambda捕获机制?中括号里面的&amp;和=是什么作用?3.智能指针有什么?解释每个作用?描述shared_ptr底层逻辑?4.为什么说tcp可靠,udp不可靠?http内部怎么实现请求和回包的?(没答出来*2)(他直接打断我吟唱三次握手说是http不是tcp,没理解想问什么http协议有什么字段?5.事务四个特性?解释每个特性。之后就是项目拷打,我用qt写了个小游戏带个私货:觉得我的文章对你有用请在github点个star谢谢喵【演示视频:https://www.bilibili.com/video/BV15nXCBVEp5/?spm_id_from=333.1387.homepage.video_card.click&amp;vd_source=6d58f9d1e033f108043d07da57ce4bca】【GitHub:https://github.com/zhongzhounb/Machi_Koro_AI】6.了解信号槽机制吗?你了解信号槽底层机制吗?(没答出来*3)(不太熟练,我回答Call&nbsp;back模式不知道是否正确信号槽用了哪种设计模式呢?(没答出来*4)(真不知道了,难道信号槽不就是一个设计模式?7.了解句柄吗?什么类型可以设成句柄?8.消息循环机制了解吗?怎么工作的?qt事件分发机制?(没答出来*5)(我说是通过id过滤不知道是否正确9.客户端与服务端怎么交互的?如果断线会补重吗?(没答出来*6)(不知道什么是补重,他问断线重连我就回答出来了QAQ之后主动提出演示下我的项目以提高略微的通过率。10.前端效果还可以,素材是怎么来的(复杂图片素材通过扫描实体卡牌+ai抠图+古法补色,简单图片靠ai生产,视频靠文字生图+图生视频)11.项目难点(我回答了前端自适应以及后端卡牌命令堆叠,具体可以看我的readme12.前端代码怎么写的(我最怕问的问题,我实际的操作是:先写后端暴露前端接口,然后vibe&nbsp;coding前端再人工调参。我就这样回答的,就怕他问第13个问题13.那就是前端都是用ai写的咯?(这我真不知道怎么回答了,说是说明不会前端技术,说不用ai又是与时代脱节,有牛友知道怎么回答好吗QAQ反问环节:1.如果来贵公司实习,我需要做什么工作?(感觉这个都得问,不然面试官觉得意向不大,至少我是这么认为的2.我回答的怎么样,可否给我个建议?(一搬公司要求不会给,但给了就血赚,对复盘有很大帮助3.就您个人而言,您更看中应聘者的什么能力(拿笔记本记一下,迭代进化自我面试个性5.25感谢信:又是挂的不明不白的一天
点赞 评论 收藏
分享
评论
1
9
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务