互联网服务的有损价值观第一条是:弱化绝对的一致性,保证最终一致性。
服务的可用性,是基于对数据进行多处的备份,也就是数据冗余来实现的。这也是最有效的一种手段。像银行账户这样的关键数据,银行的数据中心要遍布全球各地,即使地震火灾导致某个机房发生了物理性的数据毁坏,只要有数据备份,就可以保证数据不丢失,服务持续提供有效的服务。
数据冗余带来的问题是数据的一致性。数据从写入主机,到同步到备机之间存在一定的延迟,主备机之间可能存在数据的不一致。假设你往你的账户存入100块钱,数据写入到主机成功了,此时主机挂掉,服务切换到备机,当数据同步延迟时,你查询账户余额的请求落到了备机上,会发现这100块钱不见了。为了保证写入主机后,从备机读取到的也是100块钱,那么就要锁住主机,当同步完成后,才允许向主机继续写入数据。锁住主机的这个时间点,服务是不可用的。
这么看来,可用性和一致性,貌似是矛盾的、无法同时存在的?
问题是与否,答案在于有没有一种准确的数据同步机制,确保切换备机后备机数据准确。
然而分布式世界里的现实是,在时间连续的角度上来看,不存在这样的一种准确无误的数据一致性机制,信息字节的传播需要时间,无论多么地迅速,都存在那么一个短暂的时间片刻。在那个片刻中,主机与备机的数据是不一致的。而传输的信道,也是不可靠的,信息字节流可能丢失。
完美的一致性并不存在。
既然这样,那么上面问题的答案也就是:可用性和一致性,它们是矛盾的。既然是矛盾的,那么就存在取舍。
金融行业最注重一致性,为了保证一致性,金融行业可以让用户的请求进行排队。等到数据都一致了再给用户返回,也不是不可以。互联网服务与金融、电信等服务存在巨大的差异。互联网服务的访问量、存储规模,都要远远高于金融、电信等行业服务。互联网服务其实对一致性的要求并不高,所以可以适当地降低一致性,来提高服务的可用性。
这里所说的放弃绝对的一致性,并不是指数据完全错乱,而是在整体大部分数据都同步的情况下,不断地同步剩余的未同步的部分数据,最终所有的数据都保持同步。在此期间,服务是在不间断提供服务的,用户有可能拉取到不一致的数据,但无伤大雅。例如某博客论坛有两个IDC,IDCA的用户发布了一篇帖子,发布成功后,IDCA的所有用户都可以看到这篇帖子,而IDCB的用户需要等后台同步程序将IDCA的数据同步过来后,才能够看到这个帖子。在这个例子中,我们放弃了绝对的一致性,在同步数据的这个时间差,用户甚至都感知不到这个差异的存在。
这种放弃绝对一致性的思路,并不只是适用于分布式服务。在业务逻辑的开发中,可以通过精心设计,避免过度地依赖事务,或者尽可能减小事务的范围,从而提高服务的服务能力。
互联网服务的有损价值观第二条:服务降级,提供柔性可用
单机故障几乎是不可避免的。虽然分布式部署避免了单机故障时服务不可用的问题,但是分布式环境下没有对单机故障或其他异常(模块bug、网络故障、硬件故障等)进行恰当处理,可能会使整个服务的网络带宽、机器负载等资源被打满,或是中断了关键链路的调用,从而拖垮整个服务。
柔性可用是指当服务链路上的某个节点出现故障时,无法提供给用户所有的功能,那么通过开关的方式将某些分支功能移除,保证关键路径用户的正常访问。以此避免整个服务不可用。例如线上农场,用户的关键路径是访问首页、种菜、偷菜。而参加抽奖活动、参加大转盘活动等属于非关键路径的分支逻辑。在系统出现负载问题时,可以降级到只保留关键路径,以此恢复系统的稳定性。
柔性可用的关键思路,是对用户体验进行分级,开发同学要考虑好不同分级对应的网络带宽和服务处理能力。例如QQ相册,一级体验是高清原图,二级体验是压缩缩略图等等。分级之后,通过系统的决策(例如机器cpu负载、rt等因子)来判断是否服务集群是否处于不健康的状态,从而自动进行服务降级;或是事先预备好开关。这种用户体验分级的降级策略,在类似电商双十一的场景是必备的手段,例如下单链路上,用户购买物品的相似推荐,并不是下单的关键流程,这个依赖模块是锦上添花的功能,极端情况下,这些依赖模块都可以降级掉。
互联网服务的有损价值观还不止这两点,本文只提及非绝对一致性与柔性可用。欢迎讨论。