跳转至

系统设计面试题

以对话形式回答,模拟真实面试场景

20. 编程题:了解的负载均衡算法有什么?实现一个负载均衡算法。

面试官:你了解哪些负载均衡算法?能实现一个吗?

回答:我了解的负载均衡算法有这些:

轮询算法最简单,就是按顺序分配请求;加权轮询可以根据服务器性能设置权重;随机算法是随机选择服务器;加权随机也是考虑权重的随机选择。

还有最少连接算法,选择当前连接数最少的服务器;一致性哈希算法,根据请求的某个标识哈希到固定的服务器;IP哈希算法,根据客户端IP哈希选择服务器。

面试官:那你实现一个轮询算法吧。

回答:好的,我来实现一个简单的轮询算法。

首先定义一个结构体,包含服务器列表、当前索引和互斥锁:

type RoundRobinBalancer struct {
    servers []*Server
    current int64
    mutex   sync.RWMutex
}

然后实现获取服务器的方法:

func (r *RoundRobinBalancer) GetServer(clientID string) (*Server, error) {
    r.mutex.RLock()
    defer r.mutex.RUnlock()

    if len(r.servers) == 0 {
        return nil, fmt.Errorf("no available servers")
    }

    next := atomic.AddInt64(&r.current, 1)
    index := int(next-1) % len(r.servers)

    return r.servers[index], nil
}

面试官:为什么用原子操作?

回答:因为在高并发场景下,多个 goroutine 可能同时调用这个方法,用原子操作可以保证 current 字段的线程安全,避免竞态条件。虽然有读写锁保护,但原子操作的性能更好。

面试官:还能优化吗?

回答:可以考虑几个方面:加入健康检查机制,过滤掉不健康的服务器;支持动态添加和删除服务器;或者实现加权轮询,根据服务器性能分配不同的权重。

在实际项目中,我还会考虑响应时间、当前连接数等因素,实现更智能的负载均衡策略。比如结合服务器的响应时间和当前连接数来计算一个综合分数,选择分数最高的服务器。

面试官:一致性哈希了解吗?

回答:了解的。一致性哈希主要用于分布式缓存场景,可以在节点数量变化时最小化数据迁移。

基本思路是把哈希值空间想象成一个环,服务器和数据都映射到这个环上,数据存储到顺时针方向第一个服务器上。当服务器增减时,只影响相邻的服务器,大部分数据不需要迁移。

为了解决数据分布不均的问题,还会引入虚拟节点,每个物理服务器对应多个虚拟节点,这样可以让数据分布更加均匀。

高并发系统设计

系统架构设计

面试官:如果让你设计一个高并发的电商系统,你会怎么设计?

回答:我会从几个维度来考虑:

架构分层: - 接入层:使用 Nginx 做负载均衡和反向代理 - 网关层:API 网关处理认证、限流、监控 - 服务层:按业务拆分微服务,用户服务、商品服务、订单服务等 - 数据层:读写分离,分库分表,缓存层

缓存策略: - 多级缓存:浏览器缓存、CDN、Redis、本地缓存 - 缓存预热:提前加载热点数据 - 缓存更新:使用消息队列异步更新

数据库设计: - 垂直分库:按业务模块分离 - 水平分表:按用户ID或时间分片 - 读写分离:主库写,从库读 - 分布式事务:使用 TCC 或 Saga 模式

高可用设计

容错机制: - 熔断器:防止级联故障 - 降级策略:核心功能优先保障 - 超时控制:避免长时间等待 - 重试机制:指数退避算法

监控告警: - 业务监控:订单量、支付成功率 - 系统监控:CPU、内存、网络 - 应用监控:响应时间、错误率 - 日志分析:ELK 栈进行日志收集分析

性能优化

数据库优化: - 索引优化:合理设计索引 - SQL 优化:避免 N+1 查询 - 连接池:复用数据库连接 - 分页优化:游标分页替代 offset

应用优化: - 对象池:减少 GC 压力 - 异步处理:消息队列解耦 - 批量操作:减少网络开销 - 压缩传输:gzip 压缩响应

分布式系统常见问题

分布式锁

实现方式: 1. 基于 Redis:SET NX EX 实现 2. 基于 ZooKeeper:临时顺序节点 3. 基于数据库:唯一索引约束

注意事项: - 锁超时:防止死锁 - 锁续期:防止业务执行时间过长 - 锁释放:原子性操作 - 锁重入:支持同一线程多次获取

分布式事务

解决方案

两阶段提交(2PC): - 准备阶段:所有参与者投票 - 提交阶段:根据投票结果执行 - 问题:阻塞,单点故障

TCC 模式: - Try:尝试执行,预留资源 - Confirm:确认执行,提交资源 - Cancel:取消执行,释放资源

Saga 模式: - 将长事务拆分为多个本地事务 - 每个本地事务都有对应的补偿操作 - 适用于长流程业务

服务治理

服务发现: - 客户端发现:Eureka - 服务端发现:Consul + Nginx - 服务网格:Istio

负载均衡: - 轮询:简单均匀分配 - 加权轮询:根据服务器性能 - 最少连接:选择连接数最少的 - 响应时间:选择响应最快的

限流策略: - 令牌桶:允许突发流量 - 漏桶:平滑限制流量 - 计数器:简单但不够平滑 - 滑动窗口:更精确的控制

秒杀系统设计

系统特点

  • 高并发:瞬时大量请求
  • 读多写少:大量查询,少量购买
  • 时间集中:特定时间点爆发

设计方案

前端优化: - 静态化:商品页面静态化 - CDN:就近访问 - 防刷:验证码、限制频率

后端优化: - 异步处理:消息队列削峰 - 库存预扣:Redis 原子操作 - 分层过滤:多级验证

数据库优化: - 读写分离:查询走从库 - 分库分表:分散压力 - 缓存优化:热点数据缓存

核心流程

  1. 商品预热:提前加载到缓存
  2. 流量控制:网关层限流
  3. 库存扣减:Redis 原子操作
  4. 订单创建:异步处理
  5. 支付处理:独立服务

微服务架构

服务拆分原则

按业务拆分: - 用户服务:注册、登录、个人信息 - 商品服务:商品管理、库存管理 - 订单服务:下单、支付、物流 - 营销服务:优惠券、活动管理

拆分考虑: - 业务边界清晰 - 数据独立性 - 团队组织结构 - 技术栈选择

服务间通信

同步通信: - HTTP/REST:简单易用 - gRPC:高性能二进制协议 - GraphQL:灵活的查询语言

异步通信: - 消息队列:RabbitMQ、Kafka - 事件驱动:发布订阅模式 - 任务队列:异步任务处理

数据一致性

强一致性: - 分布式事务 - 性能代价高 - 适用于核心业务

最终一致性: - 事件驱动更新 - 性能好,复杂度高 - 适用于非核心业务

面试建议

系统设计思路

  1. 需求分析:明确功能和非功能需求
  2. 容量估算:用户量、QPS、存储量
  3. 架构设计:分层、分模块设计
  4. 技术选型:根据需求选择合适技术
  5. 优化方案:性能、可用性、扩展性

常见考察点

  1. 高并发处理:缓存、分库分表、异步处理
  2. 高可用设计:冗余、故障转移、降级
  3. 数据一致性:事务、锁、最终一致性
  4. 性能优化:数据库、缓存、网络
  5. 监控运维:日志、监控、告警

回答技巧

  1. 结构化思考:按层次、模块组织答案
  2. 权衡取舍:说明设计选择的原因
  3. 实际经验:结合项目经验说明
  4. 持续优化:提及后续改进方向