背景

Datamaid 是我们设计的一套统一数据治理系统。通过在系统上进行配置,不需要开发就可以把上游数据灌入到你自己的业务表或其他存储引擎。

因为我们的业务上需要使用大量的字段筛选,所以底层存储是用了 ES。

至此,我们有了一个系统,每天灌入数据到存储里。

为什么要使用双索引?

这时候问题出现了。

数据灌入并不是瞬间完成的。我们最小的实体也有千万级别的数据。使用生产者消费者模式灌入,最快也要 30 分钟。

而灌数的过程中,会出现同一个表里的不同数据,更新日期是不同的。

这个场景可能在单ID 查询服务不会有啥影响。业务上一般不会在意。

A 查出的数据是 1 号的,B 查出的数据是 2 号的。但是 A 和 B 是业务中互不影响的个体。

而且过一会儿 A 的数据也就更新成 2 号咯。

但是在我们的后台数据场景会出现一个问题:

聚合数据错误!

没错,单一数据查询影响的是个体。这个时效问题不需要解决。

但是数据聚合影响的是整体,这时候如果一组数据里,有 1 号更新的,有 2 号更新的,聚合出来的数据就无法表示真实的业务情况。这个时效问题必须解决。

解决方案

  • 灌数时停止数据查询。

灌数开始和结束,我们都能拿到信号。只要灌数的几分钟别提供查询服务,不就行了?

炫杉:杀敌 1000,自损 800。PM 怒吼:不能接受。

  • 双索引

通过两个存储A/B,交替使用,读 A 写 B,读 B 写 A。

同一个读版本存储里的数据版本一定是一致的。

双索引其实是双集群

了解了双索引方案。聪明的你可能立马想到了,ES 的索引 alias!

没错,这是实现双索引方案最简单的研发思路:

ES alias 双索引方案

创建两个 ES index,通过 ES 索引的 alias 功能,业务永远只需要查询一个 index 名字。

当新一天的数据灌入后,把这个 alias 赋给新的索引。

但实际上,我们发现这样会引发另一个问题:

当灌数脚本大量开始的时候,整个 ES 集群的压力飙升,大量的写操作导致集群的读 IO 大大下降。所以虽然是用了两个索引 A、B,但是写 A 的流量太大,导致读 B 的平响升高,服务大量超时。

这时候,我们改成了双集群方案。

双 ES 集群[组]方案

读写集群分离。不仅解决了本文的多版本数据并存问题,还极大地发挥了 ES 的数据查询能力。

至此,方案已经很好地解决问题啦。

为啥我还在标题里带了 ES 集群“组”呢?其实我们并没有使用集群组。因为我们的 ES 服务主要是提供给运营使用,他们都是在北京办公,再加上预算限制(ES 是真滴贵),我们只部署了单地域集群。如果是使用 ES 为 C 端提供服务,自然还需要使用数据同步服务,这样双集群方案其实就是双组方案了!

实时数据怎么办?

答案:双写。

其他探讨过的方案

更新完 A,当天再去更新 B。等于永远都是 B 是主存。

实际上这个方案是过度设计,交替使用双集群已经足够解决本文要解决的问题。