面试基本信息
- 公司: 腾讯IEG(互动娱乐事业群)
- 职位: 运营开发工程师(日常实习)
- 面试轮次: 二面(技术面)
- 面试形式: 线上视频面试(腾讯会议)
- 面试时长: 约50分钟
- 面试官: 技术leader,比较友善专业
面试问题与回答
问题1:水杯量水(逻辑思维题)
面试官问题:
“你手上有两个水杯,都是没有刻度的,一个三升,还有一个是五升。你还有大量的水,怎么用最快的方式量出四升的水?”
我的回答:
“首先先往这个三毫升的水里面装满,三毫升的杯子里面装满水,然后再倒到这个五毫升的水里面,然后再把这个就是现在5毫升的杯子里面有三毫升的水了。然后再再把这个三毫升的装满。装满以后再把这个3毫升的杯子里面水倒满,倒到5毫升的杯子里面倒满。然后5毫升的杯子满了,那3毫升的杯子里面就只剩一毫升的水了。然后再把5毫升的杯子里面水全都倒掉,然后再把这个三毫升杯子里面的那个一毫升的水给倒到5毫升的杯子里面,最后再把这个3毫升的杯子装满,然后装满然后再倒到5毫升的杯子里面,这样就可以实现了。”
标准/建议回答:
这个问题回答得还不错。具体步骤是:
- 先把3升杯子装满,倒入5升杯子(5升杯子里有3升水)
- 再把3升杯子装满,继续倒入5升杯子直到满(3升杯子里剩1升水)
- 把5升杯子的水全部倒掉,把3升杯子里的1升水倒入5升杯子
- 再把3升杯子装满,倒入5升杯子(1+3=4升)
这样就得到了4升水。
总结反思:
- 思路正确,步骤也对
- 表达稍微有些啰嗦,可以更简洁些
问题2:博弈论问题(苹果抢夺游戏)
面试官问题:
“在一个课桌上有100个苹果。我和你轮流拿,每人每次拿的苹果数量是1到5。你来设计一个策略,保证第100个苹果,也就是最后一个苹果一定是你能拿到的。”
我的回答:
“我要确保我拿完以后,最后剩下的苹果数模六是等于一的…比如说我有N个苹果,然后我第一次拿四个,然后之后对方无论拿多少个,无论拿多少个,比如说对方拿七个,那我都一定拿6减7个。这样就一定能保证我会最后获胜。”
标准/建议回答:
这个是经典的博弈论问题。正确的策略是:
首先分析获胜条件:要想拿到最后一个苹果,就要保证轮到对方拿的时候,剩余苹果数是6的倍数。
因为每次最多拿5个,最少拿1个,所以如果剩6个苹果轮到对方,无论他拿几个(1-5个),我都能通过拿对应数量的苹果(5-1个)来保证拿到最后一个。
具体策略:
- 100 ÷ 6 = 16…4,所以第一次我拿4个,剩96个
- 之后无论对方拿几个(1-5个),我都拿对应的数让两人这轮总共拿6个
- 这样每轮结束后,剩余苹果数都是6的倍数
- 最终轮到对方时剩6个,我必胜
总结反思:
- 思路方向对了,但一开始说成了"模5等于1"
- 经过面试官提示逐步找到了正确答案
- 这类题目需要从终局往前推理
问题3:编程题(最长连续递增子序列)
面试官问题:
“写个算法求出字符串的最长连续递增子序列,要把这个子序列输出出来,字符串是A到Z的小写字母。”
我的回答:
“我印象中是用DP,然后如果说发现他这一个右边那个元素,就是小于前面的一个元素了,那就是需要回溯一下,然后继续重新进行那个DP。”
标准/建议回答:
其实这题用DP可能把问题复杂化了,有更简单的思路:
1 | package main |
思路就是:设置一个最大长度记录器,遍历字符串,如果下一个字符比前一个大就继续,否则重新开始计数,并更新最大值。
总结反思:
- 方向想复杂了,其实是比较基础的算法题
- 现场没有写出代码,说明基础算法需要加强
- 应该多练习这类字符串处理题目
问题4:项目经验(抖音商城系统)
面试官问题:
“您这边有做过比较有价值的一个项目,能够两分钟给我介绍一下背景,还有你在里面做的一些事情?”
我的回答:
“比较有技术含量的应该是这个抖音商城。这个项目最大的挑战是Casbin权限管理框架的缓存一致性问题。我定义了两个Casbin对象,但是这两个对象并没有真的去同步他们的缓存数据。最后解决的方法就是把整个项目中Casbin只允许初始化一次,使用单例模式解决了这个问题。”
标准/建议回答:
项目介绍可以更结构化一些:
-
项目背景:这是抖音青训营的团队项目,基于微服务架构开发的电商系统,我主要负责整体架构设计和技术选型。
-
技术架构:使用Go语言+微服务架构,集成了服务发现、配置中心、消息队列、分布式缓存等组件,还实现了CICD自动化部署。
-
技术难点:主要是Casbin权限管理的缓存一致性问题。Casbin在内存中维护策略缓存,当我创建多个Casbin实例时,出现了缓存不同步的问题。最终通过单例模式确保全局只有一个Casbin实例,解决了缓存一致性问题。
-
其他亮点:还实现了Redis+MySQL的分级存储、Canal+MQ的数据同步、基于Redis Lua脚本的分布式锁等。
总结反思:
- 项目经验还算丰富,但表达可以更有条理
- 对技术细节的理解还不够深入
问题5:高并发场景(库存超卖问题)
面试官问题:
“在高并发情况下,比如说库存是100,但是高并发大家一起过来买,有可能会出现超卖的情况。这种问题你当时在设计的时候怎么解决?”
我的回答:
“我们使用了Redis的Lua脚本,然后去给库存去进行加锁…使用了Redis里面的SET NX命令进行加锁,因为Redis本身是单线程的,所以一旦对库存进行加锁以后,别的请求其实是会被拒绝的。”
标准/建议回答:
超卖问题确实是高并发场景下的经典问题,解决方案有几种:
-
Redis原子操作:使用Redis的DECR命令,因为Redis是单线程的,可以保证原子性。先检查库存是否充足,再减库存。
-
Lua脚本:把检查库存和减库存的逻辑写在一个Lua脚本里,Redis会原子性地执行整个脚本:
Lua脚本:
1 | if redis.call('get', 'stock') >= tonumber(ARGV[1]) then |
Go语言调用示例:
1 | package main |
-
数据库层面:使用乐观锁(版本号)或悲观锁(SELECT FOR UPDATE)。
-
分布式锁:使用Redis实现分布式锁,但性能会有损失。
其中Redis + Lua脚本是比较好的方案,既保证了原子性,性能也不错。
总结反思:
- 知道用Redis和Lua脚本的思路是对的
- 但对具体实现细节不够清楚
- 需要深入理解Redis原子操作的原理
问题6:监控告警系统
面试官问题:
“你们做了一些监控和告警的功能,监控哪些指标?监控日志是监控的什么指标?”
我的回答:
“我们是直接接入了OpenTelemetry,监控QPS指标,但QPS我们没有做告警。还有监控日志,日志监控只是为了去排查问题…我们告警主要是黑名单这一块,使用Redis进行QPS限制,如果一个用户一秒内请求超过十次,就会调用黑名单功能禁用这个用户。”
标准/建议回答:
监控告警系统通常包括以下几个方面:
-
应用层监控:
- QPS/TPS:每秒请求数/事务数
- 响应时间:平均响应时间、P99响应时间
- 错误率:4xx、5xx错误比例
- 接口可用性
-
系统层监控:
- CPU使用率、内存使用率
- 磁盘IO、网络IO
- 数据库连接数
-
业务层监控:
- 用户访问量、转化率
- 业务操作成功率
- 资金安全相关指标
-
日志监控:
- 错误日志数量和频率
- 关键业务操作日志
- 安全相关日志(登录失败、异常访问等)
告警策略应该基于阈值设置,比如错误率超过5%、响应时间超过1秒等。
总结反思:
- 对监控的理解比较浅层,主要停留在工具使用层面
- 缺乏对监控体系的系统性理解
- 应该学习更多监控告警的最佳实践
面试整体感受
- 难度评价: 适中,主要考察基础能力和项目经验
- 面试官风格: 比较友善,会给提示引导思考
- 题目类型: 逻辑思维+编程+项目经验,比较全面
- 准备建议: 需要加强基础算法和深入理解项目技术细节
面试结果
- 当场反馈: 面试官表示技术项目还是很扎实的
- 后续流程: 表示还在招聘中,需要等对比结果
- 个人感受: 整体发挥一般,算法题没答出来比较遗憾
经验总结
做得好的地方
- 项目经验比较丰富,技术栈涉及面广
- 逻辑思维题思路清晰,能在提示下找到答案
- 对微服务、分布式有一定理解
需要改进的地方
- 基础算法能力需要加强,编程题完全没写出来
- 对项目中技术细节的理解不够深入
- 表达可以更有条理,回答更简洁
准备建议
- 算法刷题:LeetCode中等难度题目要熟练掌握
- 项目深挖:对简历中的项目要能深入到源码层面
- 基础巩固:Redis、MySQL、微服务等核心技术要深入理解
- 系统设计:多了解高并发、分布式系统的经典问题和解决方案
知识点复习
- 水杯量水等经典逻辑题
- 博弈论基础问题
- 字符串处理算法(滑动窗口、双指针)
- Redis原子操作和Lua脚本
- 分布式系统中的缓存一致性问题
- 监控告警系统设计
- 高并发场景下的常见问题(超卖、缓存击穿等)