go面经(2)

发布时间:2026/7/9 14:41:36

go面经(2) 本文面经来自牛客大佬得物go后端校招二面面经1、协程和进程2、goroutine过多的问题3、客户端到服务端的完整链路4、负载均衡的策略5、缓存和数据库一致性的问题6、先删缓存再删数据库会有什么问题7、消息队列的用处和可能出现的问题8、设计一个热门话题榜单支持实时更新与c端高并发怎么设计这么系统9、刷榜问题怎么解决10、反问回答1、协程和进程协程Go 语言层面的轻量级 “用户态线程”由 Go 运行时runtime调度而非操作系统内核调度占用资源极少程初始栈仅 2KB。进程操作系统分配资源的基本单位独立内存CPU磁盘IO等是一次程序运行的实例。再补充一下线程操作系统调度的基本单位依附于进程一个进程可包含多个线程线程共享进程的内存空间2、goroutine过多的问题Go 虽然支持创建海量 Goroutine但并非越多越好当 Goroutine 数量超出合理范围比如单机百万级会引发一系列性能和稳定性问题。简单来说就是协程虽然资源占比小但数量过多资源占比就大了。过多会出现的问题内存耗尽虽然协程初始栈为2KB但会动态扩容而且数量过多的话总占比上去了调度开销暴增性能下降Go 运行时的调度器P需要不断切换 GoroutineG切换本身有成本虽然是用户态但量大积少成多系统资源耗尽协程阻塞的时候系统会创建新的M系统性线程来绑定P调度器文件句柄泄漏若每个 Goroutine 都创建网络连接 / 打开文件且未及时关闭会耗尽系统的ulimit -n默认 1024/655353、客户端到服务端的完整链路逻辑先 DNS 找 IP再 TCP 握手然后发 HTTP后端处理完返回。导航与寻址DNS CDN首先是浏览器解析域名。它会先查本地缓存如果没有就发起递归查询。在这个阶段通常会涉及CDN内容分发网络。如果请求的是静态资源CDN 会根据负载均衡和地理位置直接从边缘节点返回数据避免请求打到源站建立通信TCP / TLS / HTTP3拿到 IP 后客户端发起连接。如果是传统的 HTTP/1.1 或 2会经历TCP 三次握手如果是 HTTPS还会进行TLS 握手来交换密钥。不过现在的趋势是HTTP/3 (QUIC)它基于 UDP通过单次往返就能建立安全连接极大地优化了弱网下的表现。门户与调度负载均衡与网关请求进入机房后首先迎接它的是负载均衡如 Nginx 或 LVS。它负责把请求分发给健康的服务器。接着会经过API 网关在这里完成鉴权、限流、日志记录和协议转换比如从 HTTP 转成内部的 gRPC。业务工厂服务端处理后端应用接收请求后进入业务逻辑层。通常会涉及Redis 缓存查询以减轻 DB 压力和数据库读写。如果是微服务架构可能还会通过RPC调用其他服务。处理完成后封装成标准的 HTTP 响应返回。4、负载均衡的策略首先聊聊负载均衡是什么简单来说负载均衡Load Balancing就是一个“流量调度员”。用实例来说想象你开了一家火爆的奶茶店起初只有一个店员服务器他能应付得来。后来生意越来越好排队的人太多了店员忙到冒烟顾客等得想打人。于是你又雇了 3 个店员。负载均衡器Load Balancer就是站在门口分发号码牌的那个人他会根据每个店员的忙碌程度把新来的顾客指引到最空闲的柜台前。那么负载均衡是为了什么他的核心如下高可用性High Availability如果其中一台服务器“宕机”了负载均衡器会自动绕过它把流量分给剩下的正常服务器保证业务不中断。可扩展性Scalability当流量暴涨时你只需要多塞几台服务器到后端负载均衡器就能立刻把压力平摊出去。性能优化避免出现“一端忙死一端闲死”的情况让每台服务器都在最佳状态运行。策略1. 轮询Round Robin依次分发1、2、3、1、2、3…适用服务器配置相同、请求耗时差不多缺点某台机器慢会堆积请求2. 加权轮询Weighted Round Robin给性能好的机器权重更高例权重 3:1 → 前 3 台给 A第 4 台给 B适用服务器配置不一样3. 随机Random随机选一台简单、高效请求越多越均匀适用高并发、简单场景4. 加权随机Weighted Random性能好的机器被随机到的概率更高5. 最小连接数Least Connections发给当前连接最少的机器适用请求处理时间差异大长连接、慢接口最智能、最常用之一6. 最小响应时间Least Response Time按响应时间 连接数综合选择响应越快优先分发适用对延迟敏感的业务7. IP Hash源地址哈希同一个 IP → 永远发给同一台服务器作用保持会话Session 粘连适用需要登录态、有本地缓存的服务5、缓存和数据库一致性的问题这题的核心问题为缓存和数据库的数据更新不同步导致读取到脏数据。其实一致性分为两种情况最终一致性99% 业务场景允许短时间内缓存和数据库数据不一致但最终会同步如电商、社交、资讯强一致性极少数场景缓存和数据库数据必须实时一致如金融交易、支付、核心账户。对于最终一致性方案 1Cache Aside旁路缓存更新先更新数据库 → 再删除缓存而非更新缓存读取缓存命中 → 返回缓存未命中 → 读数据库 → 写入缓存 → 返回这个方案当“删缓存后、更数据库前”的情况时会出现脏读方案 2延迟双删 —— 解决 Cache Aside 的并发脏读在方案1的基础上加短暂延迟重试更新数据库后延迟 100ms 再删一次缓存覆盖线程 B 提前写回的脏数据1. 先删除缓存 2. 更新数据库 3. 延迟N毫秒如100ms再次删除缓存方案 3Canal基于 MySQL binlog—— 最终一致性最优解更新更新数据库 → MySQL写binlog → Canal监听 → 异步删缓存读取缓存命中 → 返回缓存未命中 → 读数据库 → 写入缓存 → 返回对强一致性方案1加分布式锁更新加分布式锁 → 更新数据库 → 删除缓存 → 释放锁读取加分布式锁 → 缓存命中则返回 → 缓存未命中则读库写缓存 → 释放锁缺点锁竞争导致性能大幅下降可能出现死锁需设置锁超时。方案 2缓存只存热点且设置极短过期时间1. 缓存过期时间设为1~5秒 2. 读取时优先读缓存缓存失效则读数据库并写缓存 3. 更新时直接更新数据库不操作缓存6、先删缓存再删数据库会有什么问题无论 “先删缓存再删数据库” 还是 “先删缓存再更新数据库”都会导致严重的脏数据问题是绝对的错误操作。先删缓存 → 再更新数据库线程A更新操作删除缓存user:1 → 线程A更新数据库zhangsan → lisi耗时100ms 线程A更新数据库的100ms内线程B发起读取线程B读取操作缓存未命中 → 线程B读取数据库还没更新完仍是zhangsan → 线程B把旧数据zhangsan写回缓存 ↓线程A数据库更新完成lisi最终结果脏数据永久存在先删缓存 → 再删数据库线程A删除缓存user:1 → 线程A删除数据库中user:1的数据线程B缓存未命中 → 线程B读取数据库user:1已被删除 → 线程B误以为“数据不存在”不会写缓存 ↓线程A删库操作失败如网络异常数据库中user:1仍存在7、消息队列的用处和可能出现的问题先讲讲MQ的核心功能也是用处解耦最核心就是把绑起来的系统拆开例如电商系统用户付完款了系统还要干好几件事情扣库存发优惠卷加积分短信通知等等。如果直接系统挨个去执行那么整个就高度耦合如果哪一天不需要哪个功能或者新加功能那么需要整体更改还有如果某个环节挂掉了或报错会影响整个支付流程引入MQ后只需要支付完后对MQ发送一条消息用户A已支付接下来主线任务完成只需要子系统自己去订阅MQ不需要管有几个子系统最后如果增改子系统也不需要对订单代码修改。异步:接着上面的例子没有MQ前是挨个执行的假设一个子系统功能需要t ms, 那么整个子系统流程要tn ms那么长的等待时间就会导致用户体验感不好用了MQ后其实只要主流程把状态一改发给MQ就可以立即给前端响应而子系统的功能并发需要在这么短的时间完成。削峰: 如果突然有几万个并发请求他们打到数据库数据库承受不住数据库会直接宕机。这时候用MQ就扮演排队区的作用请求打过来我们先统一扔到MQ里根据数据库处理情况慢慢从MQ里往外拉任务。问题 1消息丢失最常见成因3 个环节生产者丢失发送消息后MQ 未收到但生产者误以为发送成功MQ 服务端丢失消息写入 MQ 后未持久化就宕机消费者丢失消费者拿到消息后未处理完就宕机MQ 误以为消费成功。解决生产者1. 同步发送 确认如 Kafka 的 acksall、RabbitMQ 的 confirm 模式2. 本地消息表兜底发送前写库成功后删库MQ服务端1. 开启持久化Kafka 刷盘、RabbitMQ 持久化到磁盘2. 集群部署副本机制消费者1. 关闭自动确认处理完消息后手动确认2. 消费失败时重试重试失败入死信队列问题 2消息重复消费必然存在成因网络延迟消费者手动确认消息时MQ 未收到确认重新推送重试机制生产者重试发送导致 MQ 中出现重复消息。解决方案消费端幂等处理核心方案 1基于唯一 ID如订单 ID消费前查数据库是否已处理方案 2基于 Redis / 数据库做幂等标记SETNX 锁 过期时间方案 3数据库操作本身幂等如 UPDATE user SET numnum1 WHERE id1MQ 层面设置消息去重队列如 RabbitMQ 的去重插件。问题 3消息顺序消费部分场景需要成因MQ 集群部署时同批次消息可能被分发到不同消费者导致顺序错乱如订单创建→支付→发货被消费成支付→创建→发货。解决方案分区 / 队列绑定Kafka相同业务 ID如订单 ID的消息发送到同一个分区一个分区只被一个消费者消费RabbitMQ相同业务 ID 的消息发送到同一个队列一个队列只被一个消费者消费业务层面消费后按顺序号重排如给消息加 seq消费后排序再处理。问题 4消息积压高并发 / 消费慢成因生产速度远大于消费速度如秒杀产生 10 万消息消费者仅能处理 1 万 / 分钟消费者故障消费服务宕机消息堆积在 MQ 中。解决方案临时扩容增加消费者实例数提升消费速度分流处理将积压消息转发到临时队列用专门的消费集群处理限流降级生产端限流避免积压加剧预警机制监控 MQ 消息堆积量超过阈值如 1 万及时报警。问题 5分布式事务问题跨服务一致性成因生产者发送消息和本地业务操作如扣库存无法保证原子性库存扣成功消息没发出去或消息发出去库存没扣成。解决方案本地消息表方案主流步骤 1生产者本地事务同时写业务数据 消息表步骤 2定时任务扫描消息表重发未确认的消息步骤 3消费者处理完消息后回调生产者标记消息已确认Seata AT 模式基于 TC事务协调器实现分布式事务兼容 MQ 场景RocketMQ 事务消息原生支持 “半消息 回查”保证生产端原子性。问题 6MQ 集群宕机可用性问题成因单节点 MQ 宕机生产 / 消费全部中断网络分区MQ 集群脑裂。解决方案集群部署Kafka副本 分区、RabbitMQ镜像队列 集群、RocketMQ主从架构多活部署异地多活 MQ 集群某一区域宕机切换到另一区域降级方案MQ 宕机时生产端临时写本地日志恢复后补发到 MQ。8、设计一个热门话题榜单支持实时更新与c端高并发怎么设计这么系统先来分析一下这个系统需要什么功能实时更新高并发读防刷/防作弊兜底系统异常时热门话题榜单核心是分层设计写侧用消息队列异步接收行为数据Redis ZSet 实时计算热度读侧用本地缓存 RedisCDN 多级缓存扛高并发同时通过防刷、降级保证榜单真实和系统可用。整体流程用户行为上报 → 接入层Nginx/LB → 接入服务 → 消息队列 → 热度计算服务 → 榜单存储 → 多级缓存 → C端API服务 → 客户端接入层Nginx/LB 做负载均衡限流令牌桶防止突发流量打垮后端接入服务无状态化部署仅做参数校验、用户鉴权不做业务逻辑异步化将行为数据写入消息队列Kafka/RocketMQ立即返回成功C 端感知不到延迟兜底消息队列开启持久化 副本避免行为数据丢失。热度计算需异步计算 增量更新避免全量排序消耗资源增量计算用Redis Sorted SetZSet存储各维度的热度榜单ZSet 的score为计算后的热度值member为话题 ID定时全量校准定时对热点进行计算每5分钟计算结果先写入临时ZSet没问题再上刀线上ZSet防刷安全措施榜单存储多级缓存 分层存储9.刷榜问题怎么解决实时过滤行为上报时限制单用户 / IP 的行为频率如 1 分钟点赞≤10 次异常行为直接丢弃对新账号、异常设备的行为做降权处理如权重 ×0.1离线审计定时分析热度曲线标记 “短时间内热度暴涨” 的话题人工复核作弊话题直接剔除榜单并清除违规热度封禁刷量账号。

相关新闻