顺序处理

215 阅读2分钟

这是我参与 8 月更文挑战的第 14 天,活动详情查看: 8月更文挑战

绝大多数的场景下,业务操作不需要保证严格的顺序处理,但在数据存储上却是最常规的要求,比如MySQL在集群模式下多节点间的数据写入顺序必然是需要一致的。在业务操作上比较典型的是数据库日志的同步,我们一般会订阅到Kafka,然后从Kafka异步消费,这之中就要保证消费时记录的顺序与数据库一致。

顺序处理的基础是要做到时钟一致,本质的技术有以下几种:

单节点处理:用一个节点处理所有消息,这种最简单,但有违微服务避免单点的原则,在可用性和可维护性上需要平衡,对一些边缘业务可采用此做法。

单节时序生成:用一个节点生成Timestamp,这样就有了一个全局可排序的数据记录,当然也同样有违避免单点的原则,但这却是很多分布式数据库的选择(比如TiDB),因为它足够简单。

TureTime方案:由多个部署有GPS同步能力的时钟及原子钟节点提供Timestamp服务,这一方案避免了单点问题,问题在于太过昂贵,一般只有大型集群才会考虑使用(如GoogleSpanner,使用这一方案保证不同服务节点的时间误差小于10ns

Lamport Timestamps:上面说的都是物理时钟,而Lamport提出的是逻辑时钟概念,通过为每一操作带上本地或接收到消息的时间戳来解决访问链路的顺序问题。Lamport Timestamps的局限只能处理有相关性记录的顺序,像上文说到数据库日志记录就无能为力了。

上述方案可以解决时钟一致性,但这远远不够。我们用MQ做服务解耦就会经常遇到顺序问题,比如将用户的关键操作流程通过Kafka传送到日志服务,日志就要保证顺序写入。但目前Kafka及主流的MQ都无法保证严格顺序,因为成本太高:要先保证生产者都同步生产消息到MQMQ的存储要避免多个写入节点,并且消费者只能有一个,进行逐条消费等,这在性能、可用性上都大打折扣。这时我们只能根据用户Id计算hash后分配到相同的写入节点(对应于KafkaPartition),这样就能做到同一用户的日志消费顺序等同于日志的发送(同步发送方式)顺序。

顺序处理的成本不低,在开发中我们应该尽量避免。