答案以下是迅雷服务器开发工程师一面可能涉及的详细问题解答:
自我介绍需简洁说明教育背景、技术方向(如网络编程/分布式系统)、项目经验(如高并发服务开发)、掌握的核心技能(如TCP/IP、数据库优化、RPC框架)及个人优势(如问题排查能力、性能调优经验)。
TCP可靠传输的实现特性
- 确认应答(ACK):接收方对收到的数据包发送确认报文。
- 超时重传:发送方未收到ACK时重新发送数据。
- 序列号与滑动窗口:通过序列号去重和排序,滑动窗口控制流量。
- 校验和:检测数据传输中的错误。
- 四次挥手过程
- 第一次挥手:客户端发送FIN报文,进入FIN_WAIT1状态。
- 第二次挥手:服务端回复ACK,客户端进入FIN_WAIT2状态。
- 第三次挥手:服务端发送FIN报文,进入LAST_ACK状态。
- 第四次挥手:客户端回复ACK,服务端关闭连接;客户端等待2MSL后进入CLOSED状态。
- 大量TIME_WAIT状态连接的影响
- 资源占用:占用文件描述符和内存资源。
- 端口耗尽:高并发场景下本地端口可能被占满,导致新连接失败。
- 影响复用:TIME_WAIT状态默认持续2MSL(约1分钟),可能延迟端口复用。
- 排查与解决TIME_WAIT过多问题
- 排查方法:
使用netstat -ant | grep TIME_WAIT | wc -l统计数量。
通过ss -s查看各状态连接分布。
- 原因分析:
短连接频繁创建/关闭(如HTTP短连接)。
服务端主动关闭连接(如客户端未复用连接)。
- 解决方案:
启用net.ipv4.tcp_tw_reuse复用TIME_WAIT端口。
调整net.ipv4.tcp_max_tw_buckets限制最大数量。
优化应用层,使用长连接或连接池。
- HTTP/1.1的性能问题
- 队头阻塞:同一TCP连接上请求需串行处理,前序请求延迟会导致后续阻塞。
- 无二进制分帧:数据以文本格式传输,解析效率低。
- 头部冗余:每次请求需携带完整头部,增加传输开销。
- 单向性:仅支持客户端发起请求,无法服务器推送。
- HTTP/2与HTTP/3的改进
- 队头阻塞的解决方案演进
- HTTP/1.1:通过域名分片(Domain Sharding)创建多个TCP连接并行加载资源。
- HTTP/2:多路复用允许单个连接内并发请求,但TCP层仍存在队头阻塞。
- HTTP/3:QUIC协议在UDP上实现多路复用,丢包仅影响对应流,其他流不受影响。
- HTTP/3的Connection ID解决网络切换问题
- 问题:TCP依赖四元组(源IP、源端口、目的IP、目的端口)标识连接,网络切换(如WiFi到4G)会导致四元组变化,连接中断。
- 解决方案:QUIC引入Connection ID,客户端/服务端通过唯一ID标识连接,即使IP/端口变化,仍可恢复连接。
- InnoDB的隔离级别
- 读未提交(Read Uncommitted):可能读到未提交数据(脏读)。
- 读已提交(Read Committed):避免脏读,但可能不可重复读。
- 可重复读(Repeatable Read,默认):避免脏读和不可重复读,但可能幻读。
- 串行化(Serializable):完全串行执行,避免所有并发问题。
- 默认隔离级别为可重复读的原因
- 平衡性能与一致性:避免不可重复读(如事务内多次查询结果不一致),同时比串行化性能更高。
- 幻读场景较少:多数业务可通过间隙锁或应用层控制规避幻读。
- 可重复读与间隙锁的幻读问题
- 可重复读:无法完全解决幻读(如其他事务插入新记录)。
- 间隙锁:通过锁定索引间隙防止幻读,但以下场景仍可能失效:
非索引列查询:间隙锁仅对索引生效。
外键约束:外键关联表可能插入新记录。
多表事务:跨表操作时其他事务可能插入数据。
- MVCC的实现方法
- 版本链:每行记录包含隐藏字段(DB_TRX_ID、DB_ROLL_PTR、DB_ROW_ID),DB_ROLL_PTR指向Undo Log中的旧版本。
- ReadView:事务开始时生成一致性视图,包含当前活跃事务ID列表,用于判断记录可见性。
- 清理机制:通过Purge线程删除不再需要的旧版本数据。
- Undo Log的作用
- 事务回滚:记录数据修改前的状态,支持事务回滚。
- MVCC实现:提供旧版本数据,支持非锁定读(如可重复读隔离级别)。
- MySQL三大日志及作用
- Redo Log(重做日志):物理日志,记录页的物理修改,用于崩溃恢复。
- Undo Log(回滚日志):逻辑日志,记录SQL执行前的数据状态,用于事务回滚和MVCC。
- Binlog(二进制日志):逻辑日志,记录所有修改数据的SQL语句,用于主从复制和数据恢复。
- 回表问题及优化
- 联合索引的使用注意事项
- 最左前缀原则:查询需从索引最左列开始,否则无法使用索引。
- 索引选择性:选择性高的列(如唯一值多)应放在左侧。
- 避免冗余索引:如已有(a,b)索引,再建(a)索引可能冗余。
- 范围查询影响:范围查询右侧列无法使用索引(如a=1 AND b>2 AND c=3中仅a、b用索引)。
- UUID作为主键的可行性
- 性能问题的类型
- 查询性能:索引设计、SQL优化、缓存命中率等。
- 写入性能:事务隔离级别、锁竞争、日志写入速度等。
- 缓存与数据库一致性方案
- 先删缓存再改数据库的风险
- 并发问题:删除缓存后,其他线程可能从数据库读到旧数据并重新缓存,导致不一致。
- 推荐方案:先更新数据库,再删除缓存(利用数据库事务保证原子性)。
- 其他一致性方案
- 订阅Binlog:通过Canal等工具监听数据库变更,异步更新缓存。
- 分布式事务:如Seata框架,但性能开销较大。
- 最终一致性:允许短暂不一致,通过定时任务修复。
- gRPC在聊天服务器中的应用场景
- 服务间通信:如用户服务、消息服务、存储服务间的RPC调用。
- 高性能传输:基于HTTP/2的多路复用和Protobuf二进制编码,适合高并发场景。
- 跨语言支持:方便不同语言编写的服务协同工作。
- 服务节点动态变化的应对方案
- 服务发现:通过Zookeeper、Etcd或Nacos动态注册/发现服务节点。
- 负载均衡:客户端负载均衡(如Ribbon)或服务端负载均衡(如Nginx)。
- 健康检查:定期检测节点状态,自动剔除不可用节点。
- RPC框架设计考虑方面
- 通信协议:选择HTTP/2、gRPC或自定义协议(如基于TCP的二进制协议)。
- 序列化方式:Protobuf、JSON或Thrift,平衡性能与可读性。
- 服务注册与发现:支持动态扩容和故障转移。
- 负载均衡策略:轮询、权重、最少连接数等。
- 容错机制:超时重试、熔断降级、限流。
- 监控与日志:链路追踪、性能指标收集。