如何设计高可靠的订单同步方案

跨业务域数据联查和搜索的需求层面优化

为了实现对跨业务域数据的联查和搜索,例如退款单的退款状态和发货状态,订单的订单状态和退款状态,需要在需求层面进行上溯。这样可以跳出现有方案框架的局限,拓展搜索解决方案的范围,并且更清晰地认识现有方案设计和实现的前提和约束。

具体来说,这种跨业务域的联查和搜索需要在设计阶段就考虑到不同业务域之间的关联和依赖。我们需要确定业务域之间的交互方式和接口,以确保数据的有效传递和使用。同时,还需要在实现阶段建立统一的数据模型和标准化的数据格式,以便于在不同业务域之间进行数据的传递和共享。此外,还需要对数据的访问权限和安全性进行严格的控制和管理,以避免敏感数据泄露和不当使用。

总之,通过上溯需求层面,我们可以更好地理解和满足用户的需求,同时也可以拓展设计和实现方案的范围,提高系统的可靠性和扩展性。

数据同步方案设计的考虑因素及挑战

如何将多源数据存储 (S1, S2, ..., Sn) 的数据同步到具备联查能力的目标数据存储 T 是一个常见的需求。在满足数据一致性的前提下,需要保证 T 中的不变数据与 Si 中对应的不变数据在指定计算关系下保持一致,并且每次同步后,T 中的可变数据与 Si 中对应的可变数据在最新状态下保持一致。

在实际场景中,源数据存储 Si 通常是数据库 DB,而 T 则有多种选择,如 Elasticsearch(ES)、MySQL、MongoDB等。选择不同的存储对解决方案的影响非常大,需要根据具体情况进行权衡和选择。

需要注意的是,需求和目标是存在差异的。目标是在当前环境约束下能够实现的第二层需求。如果跨业务域数据都存储在一张源数据存储中,即源数据存储和目标数据存储的关系为1:1,那么目标就是单数据存储的同步,解决方案也相对简单。但在实际情况中,不同业务域的数据通常存储在不同的数据表中,因此需要进行多表同步。在制定目标时,需要考虑当前环境约束的影响,如果约束过于强大,则需要适当考虑是否能够改造环境约束,以提供更灵活的解决方案。环境约束改变后,同样的需求目标也会发生变化。

因此,对于多源数据存储同步的需求,需要在设计阶段充分考虑不同存储之间的数据模型和结构差异,制定统一的数据格式和标准化的数据交换协议,以便于在不同存储之间进行数据的传递和共享。同时,需要对数据同步的频率、同步机制和同步策略进行详细的规划和设计,以确保数据的一致性和完整性。最终,通过合理的设计和实现,可以实现多源数据存储的同步,并满足具备联查能力的目标数据存储的需求

如何解决可变状态同步的难点

保证数据状态变更的每次同步具备强一致性是必要的,尤其是针对可变状态的同步。与不可变状态不同,可变状态需要考虑时间戳以及状态的逆转。在现实场景中,状态的逆转往往是不可避免的,同时状态数值的递增关系也不一定满足。因此,通用的方案不能以状态数字的递增关系为前提,而是以状态变更的原始时间戳为准,以确保方案的通用性。

实际情况下,订单状态确实存在状态逆转的情况。例如,订单状态从“已支付”变更为“已取消”或从“已发货”变更为“退货中”。因此,在状态同步方案中,不能仅以状态数字的递增关系为前提,而需要以状态变更的原始时间戳为准。对于订单状态的同步,可以通过记录订单状态变更的时间戳来实现状态同步的强一致性,以避免同步出现错误。

同步ES数据的版本控制

在多表情形下,同步ES数据需要解决全局版本控制问题。因为ES存储的数据是一个大的JSON串,没有字段级别的版本控制,只有针对整个JSON串的一个版本控制。为解决这个问题,可以参考以下文章:
《Elasticsearch数据存储详解》(https://elasticsearch.cn/article/6178)

情形一

假设有一个索引 E,它包含字段 (refund_id, order_no, rp_status, version)。同时,有一个 DB 表 r,它包含字段 (refund_id, order_no, rp_status, version)。同步方案是按照 version 字段,同步到 ES。对于 T1 M1 和 T2 M2,如果 T1.version < T2.version,将 T2 时刻的记录同步写入。在此情形下,使用非顺序队列即可。

结论:理想的情形下,一个 ES 表完全仅对应一个 DB,DB 含有 version 字段,可以作为 ES 表的 version 版本控制,使用非顺序队列。为了达到最佳的搜索索引性能,应尽可能符合这种情形。

情形二

现在假设有一个表 t,它包含字段 (order_no, shop_name),需要将 shop_name 同步到 E 中。shop_name 是不变的。这时,只要在同步处理消息的时候,关联表 t,将 shop_name 读取,写入到 E 中即可。虽然增加了一次 DB 访问,但仍然可以使用非顺序队列。

结论:将 DB1 和 DB2 同步到 E 中,以 DB1 为主,获取 DB2 的不变字段,仍然可以使用非顺序队列。DB1 的 version 字段作为版本控制。需要注意的是,对于分库分表来说,这里的 version 必须是全局递增的 version,而不是某个分表的 version。因为某个分表的 version 不能满足递增特性。

情形三

假设现在有一个表 t,包含 (order_no, order_status) 两个字段,需要将 order_status 字段同步到索引 E 中。在这种情况下,单独使用 version 字段是不够的,因为有以下情况:

假设退款单 R001 的状态从 rp_status = 1 → 3,订单状态 order_status 从 T1', M1' 到 T2', M2' 变为了 1 → 5。

首先,T2 时刻的退款单消息 M2 先于 M1 到达,实时获取了 T1' 的订单状态为 1,得到了 T2R = (R001, E001, rp_status=3, order_status=1, version=3)。
然后,T1 时刻的退款单消息 M1 到达,实时获取了 T2' 的订单状态为 5,得到了 T1R = (R001, E001, rp_status=1, order_status=5, version=1)。
由于 T2R.version > T1R.version,因此 T2R 被写入。然而,此时,order_status 的同步是错误的。
因此,当 DB1(主)和 DB2(辅)均需要同步到索引 E 时,如果 DB1 和 DB2 都有字段发生了变化,那么单独使用 DB1 的 version 字段来控制版本号是不可行的。这会导致 DB2 的字段同步变更存在错误。为避免这种情况,在设计方案时应该:

尽量避免将两个表的可变状态同时同步到一个索引中。(但是有时这是必要的)
尝试将 DB2 的可变字段冗余到 DB1 中,这样可以在业务层面上解决问题。但这样做会增加 DB1 的设计和业务更新的复杂度,而且这种冗余方法在方案设计之前可能不会被想到。(不过在实际情况中,这种方法可能不可行)

比较顺序队列方案和非顺序队列方案的优缺点

顺序队列方案和非顺序队列方案各有优劣。

顺序队列方案相对来说更简单,只需要一个任务,所有同步逻辑都在这个任务里,且流程更符合自然思维。但是,顺序队列方案容易阻塞,吞吐量有瓶颈,适合中小型业务量。

非顺序队列方案相对来说吞吐量更优,不会因为某个消息消费阻塞。不过,非顺序队列方案需要多个任务、额外全局存储,且同步逻辑较为分散,不容易直接理解。因此,非顺序队列方案适合大型业务量。

posted @ 2023-03-21 16:58  小霖2012  阅读(354)  评论(0编辑  收藏  举报