通用设计思想

scale out 横向扩展

如:数据库一主多从,分库分表,存储分片;

缓存

性能差异(大概)为:磁盘寻址 10ms -> 内存寻址 100ns

  现有的经典场景如:TLB、HTTP(浏览器缓存、代理缓存、CDN 缓存)、DNS、路由(路由信息,目标ip、下一跳等)、磁盘、cpu等;

  业务常用两种缓存:动态(如 redis为代表,内存较大)、本地(内存较小),通常先访问后者再访问前者;

  缓存的倾向一般是:热点数据,但同时需要注意这里缓存一致性OOM 等问题;

可见本处的讨论:Consistency

异步

设计目标

高性能

原则:以问题为导向,找到 二八原则 中的病因,通过测试的性能数据作为支持,持续优化性能;

指标:降低的平响、提升的吞吐量,平均值、最大值、分位值(TP99 为200ms内,99.99 分位在1s内);

方法

  • 提高系统的处理核心数,核心在于从根本上提高并行处理的能力;
  • cpu 密集型尝试使用优化算法;
  • IO 密集型 使用监控(三件套 log+trace+metrics)分析整个 环节 的性能瓶颈,数据库、网络等;

池化

  核心目的是对于频繁创建+销毁的非超大对象,为降低创建和回收的耗时,通过池维护对象,使对象可被复用 减少创建+销毁;

数据库连接池逻辑:

  • 复用空闲连接;
  • 如果当前连接数 小于 最小连接数,则创建新连接,大于 最小连接数但小于最大连接数时,如果此时没有可复用的连接则创建新连接;
  • 如果当前连接数 大于 最大连接数,则等待直到超时;

db 连接池潜在的问题

  • db 域名对于 IP 发生变化,但池中连接没有更新,当原 IP 的 db 关闭后,旧连接的查询都会失败;
  • 通常池会解决这个问题(即自动重连):wait_timeou 代表连接空闲超过一定时间后,db 会主动关闭这条连接,注意使用方是无感知的(比如说用终端连接 db 时,挂久了不动,再尝试发生命令就会报错类似:MySQL server has gone away);

线程(goroutine)池,使用方面:

  • web 服务线程池,并发处理 http 请求(如 gin 会将每一个请求都创建一个 goroutine 去执行,就是普通的并发池);
  • 计算密集型任务池,worker 的数量尽可能小于等于核心数(因为要一直算);
  • IO 密集型任务池,worker 的数量尽可能大于等于核心数(因为要等);

线程(goroutine)池潜在问题

  • 任务堆积 -> 具体场景需要具体分析,如因为线程的调度策略导致过多线程的切换、干活线程太少(cpu 占用低,都在等)等情况;
  • 注意在内存中尽可能不要使用无界队列(即 任务不会被丢失),潜在有 full GC 的问题(java 有,go 基本不会因为 GC 导致服务不可用?个人理解),但最终存在 OOM 这个最终 Boss;

分布式存储

主从读写分离

eg:ES 存储的索引分片会被复制到多个节点、HDFS 的文件也会被复制到多个数据块上;

详细类似的讨论可见:DDIA ch.5 DDIA ch.6

NoSQL

如:

  • redis、levelDB 这类的存储,前者是内存+IO多路复用,后者是内存+追加写/合并,相比于普通的数据库,读写性能强一大截;
  • Hbase、Cassandra 这类的列式存储,适用于数据分析的场景;
  • MongoDB 这类的文档型数据库,特点是 SchemaFree,因为可在一个集合中存储多个不同结构的数据,字段可以任意扩展;

LSM 不展开了,可见:DDIA ch.3

高可用

原则: 服务的稳定提供;

指标

  • MTBF(Mean Time Between Failure)平均故障间隔,越长说明系统越稳定;
  • MTTR(Mean Time To Repair)表示故障的平均恢复时间,越短说明对用户影响越小;
  • 可用性 = MTBF / (MTBF + MTTR)

    3 个 9 就必须自动化了;

方法

  • failover:服务节点故障时,自动切换流量到其他 peer 节点;
  • 容灾:主备不对等节点间,主节点主要负责提供服务,备节点负责故障转移(raft 最终一致性下,主提供读写、从提供读+故障转移),主要包括 对数据的备份(follower)+故障检测(heartRPC)+自动转移(选举);
  • 超时控制:通过 TP99 等指标,确定静态或者动态的超时时间,防止长尾延迟,保证可用性;
  • 断路器、限流/降级:前者直接干掉请求,直到故障恢复(过程是渐进的,不展开聊),后者分别是减少请求量和只走业务核心逻辑(某些情况下就是干掉耗时长的业务流程,举例就是返回默认值);

op 方面:

  • 灰度(用户维度)、ab(用户维度,侧重比较效果)、分级(机器维度);

高扩展

原则:单机资源有限,分布式环境下数据一致性、缓存一致性、依赖第三方的能力、负载均衡、交换机/网络带宽等问题又接踵而至,但分布式是不可少的;

方法

  • 存储层面的扩展:分库分表,核心注意数据复制、数据迁移过程中出现的问题;
  • 业务层面的扩展:即微服务的形式提供服务,单独的服务(包括集群)单独部署,使用单独的资源(注意 过微的情况下,考虑是否为了性能将部分相关性强的微服务合并);当服务特别庞大时,也可以考虑将服务分为核心服务和辅助服务,将其拆分到依赖不同的资源上;