提升系统性能
明确性能的度量指标十分重要
主要有两种思路:
可用性是一个抽象的概念,你需要知道要如何来度量它,与之相关的概念是:MTBF 和 MTTR。
- MTBF(Mean Time Between Failure)是平均故障间隔的意思,代表两次故障的间隔时 间,也就是系统正常运转的平均时间。这个时间越长,系统稳定性越高。
- MTTR(Mean Time To Repair)表示故障的平均恢复时间,也可以理解为平均故障时 间。这个值越小,故障对于用户的影响越小。
可用性与 MTBF 和 MTTR 的值息息相关,我们可以用下面的公式表示它们之间的关系: Availability = MTBF / (MTBF + MTTR)
这个公式计算出的结果是一个比例,而这个比例代表着系统的可用性。一般来说,我们会使用几个九来描述系统的可用性。
开发角度
1. failover(故障转移)
针对不对等节点的 failover 机制会复杂很多。比方说我们有一个主节点,有多台备用节 点,这些备用节点可以是热备(同样在线提供服务的备用节点),也可以是冷备(只作为备 份使用),那么我们就需要在代码中控制如何检测主备机器是否故障,以及如何做主备切 换。
使用最广泛的故障检测机制是“心跳”。你可以在客户端上定期地向主节点发送心跳包,也
可以从备份节点上定期发送心跳包。当一段时间内未收到心跳包,就可以认为主节点已经发
生故障,可以触发选主的操作。
选主的结果需要在多个备份节点上达成一致,所以会使用某一种分布式一致性算法,比方说 Paxos,Raft。
2. 超时控制
除了故障转移以外,对于系统间调用超时的控制也是高可用系统设计的一个重要考虑方面。
复杂的高并发系统通常会有很多的系统模块组成,同时也会依赖很多的组件和服务,比如说
缓存组件,队列服务等等。它们之间的调用最怕的就是延迟而非失败,因为失败通常是瞬时
的,可以通过重试的方式解决。而一旦调用某一个模块或者服务发生比较大的延迟,调用方
就会阻塞在这次调用上,它已经占用的资源得不到释放。当存在大量这种阻塞请求时,调用
方就会因为用尽资源而挂掉。
在系统开发的初期,超时控制通常不被重视,或者是没有方式来确定正确的超时时间。
超时时间短了,会造成大量的超时错误,对用户体验产生影响;超时时间长了,又起不到作 用。我建议你通过收集系统之间的调用日志,统计比如说 99% 的响应时间是怎样的,然后 依据这个时间来指定超时时间。如果没有调用的日志,那么你只能按照经验值来指定超时时 间。不过,无论你使用哪种方式,超时时间都不是一成不变的,需要在后面的系统维护过程 中不断地修改。
3. 降级和限流
超时控制实际上就是不让请求一直保持,而是在经过一定时间之后让请求失败,释放资源给
接下来的请求使用。这对于用户来说是有损的,但是却是必要的,因为它牺牲了少量的请求
却保证了整体系统的可用性。而我们还有另外两种有损的方案能保证系统的高可用,它们就
是降级和限流。
降级是为了保证核心服务的稳定而牺牲非核心服务的做法。比方说我们发一条微博会先经过 反垃圾服务检测,检测内容是否是广告,通过后才会完成诸如写数据库等逻辑。
反垃圾的检测是一个相对比较重的操作,因为涉及到非常多的策略匹配,在日常流量下虽然
会比较耗时却还能正常响应。但是当并发较高的情况下,它就有可能成为瓶颈,而且它也不
是发布微博的主体流程,所以我们可以暂时关闭反垃圾服务检测,这样就可以保证主体的流
程更加稳定。
限流完全是另外一种思路,它通过对并发的请求进行限速来保护系统。
比如对于 Web 应用,我限制单机只能处理每秒 1000 次的请求,超过的部分直接返回错误 给客户端。虽然这种做法损害了用户的使用体验,但是它是在极端并发下的无奈之举,是短 暂的行为,因此是可以接受的。实际上,无论是降级还是限流,在细节上还有很多可供探讨的地方,我会在后面的课程中,
随着系统的不断演进深入地剖析,在基础篇里就不多说了。
运维角度
1. 灰度发布
灰度发布指的是系统的变更不是一次性地推到线上的,而是按照一定比例逐步推进的。一般 情况下,灰度发布是以机器维度进行的。比方说,我们先在 10% 的机器上进行变更,同时 观察 Dashboard 上的系统性能指标以及错误日志。如果运行了一段时间之后系统指标比较 平稳并且没有出现大量的错误日志,那么再推动全量变更。
灰度发布给了开发和运维同学绝佳的机会,让他们能在线上流量上观察变更带来的影响,是
保证系统高可用的重要关卡。
2. 故障演练
故障演练指的是对系统进行一些破坏性的手段,观察在出现局部故障时,整体的系统表现是
怎样的,从而发现系统中存在的,潜在的可用性问题。
如何可扩展
拆分是提升系统扩展性最重要的一个思路,它会把庞杂的系统拆分成独立的,有单一职责的 模块。相对于大系统来说,考虑一个一个小模块的扩展性当然会简单一些。将复杂的问题简单化,这就是我们的思路。
那么社区可能有 5 个模块。所以存储拆分首先考虑的维度是业务维度。
用户:负责维护社区用户信息,注册,登陆等;
关系:用户之间关注、好友、拉黑等关系的维护;
内容:社区发的内容,就像朋友圈或者微博的内容;
评论、赞:用户可能会有的两种常规互动操作;
搜索:用户的搜索,内容的搜索。
针对数据库做第二次拆分。