团队PK赛:分布式任务分片与数据一致性解决方案

团队PK赛是一种激励司机通过组织或自由报名形成小团队,并针对特定业务指标进行竞赛的活动。在满足基本入围条件后,城市会根据排名发放奖励。本文记录当时遇到的问题和解决方案。

挑战一:数据拉取与并行计算

问题描述

排行榜需要从特征团队拉取数据,如果单机串行处理,面对大量活动时可能无法及时完成数据处理;单机并行处理又可能导致资源耗尽。因此,需要实现多机并行计算。

解决思路

方案1:一致性哈希

通过Apollo或GetHosts获取当前机器列表,使用取模方式分配SQL查询,以拉取并处理数据。这需要考虑节点不可用的情况,可能需要引入探活组件。

方案2:分布式主从模型

使用多个Docker机,以Redis作为锁,选择一个master Docker机执行定时任务,通过Nginx负载均衡分配任务。

方案3:MQ任务分发

在方案2的基础上,需要一个主进程来推送任务,然后分发到MQ。使用拉模式,并使用ACK保障任务的已执行。

解决方案方案3

使用定时任务从特征团队拉取数据,并通过MQ进行任务分发,实现并行计算。利用MQ拉取ACK机制,确保消息的有效一次性处理,并控制消费速度。

sequenceDiagram participant Crontab participant 后台 participant MQ participant REDIS participant 特征团队 Crontab ->> 后台: 活动计算 (30min/次) 后台 ->> 后台: 待处理,活动列表 后台 ->> MQ: 活动分片 (活动ID+团队ID+版本号) MQ ->> MQ: 消息推送 MQ ->> 后台: MQ消息(sdk拉) 后台 ->> 后台: 解析活动分片 后台 ->> REDIS: 获取分片版本数据 REDIS ->> REDIS: 版本是否存在? alt 版本存在 REDIS ->> MQ: ACK end alt 版本不存在 后台 ->> 后台: 获取分片成员 后台 ->> 特征团队: 获取所有成员版本数据 特征团队 ->> 特征团队: 计算分片指标 特征团队 -->> REDIS: 返回数据 end REDIS ->> REDIS: 版本是否存在? alt 版本存在 REDIS ->> MQ: ACK end alt 版本不存在 REDIS ->> REDIS: 写入分片版本数据 REDIS ->> REDIS: 计算分片计数 incr REDIS -->> 后台: 返回数据 end 后台 ->> MQ: ACK 后台 ->> 后台: 活动计算完成? alt 计算未完成 后台 ->> Crontab: 结束 end alt 计算完成 后台 ->> 后台: 活动计算排名 end 后台 ->> REDIS: 更新活动版本号
上面的流程可以概括为:

  1. 活动计算:由Crontab触发,每30分钟执行一次。
  2. 待处理活动列表:后台系统维护一个待处理的活动列表。
  3. 活动分片:将活动分解成多个分片,每个分片可以独立处理。
  4. 解析活动分片:对每个分片进行解析,准备进一步的处理。
  5. 获取分片成员:确定每个分片需要处理的数据或任务。

挑战二:数据计算、展示与更新

问题描述

进行中的活动每半小时更新一次排行榜,但220W左右的司机数据,从特征团队拉取全量指标容易触发超时,不仅更新慢,且读取压力大。因此,需要一套合理的更新及读取方案。

解决思路

  • 在团队PK赛中,无论是司机还是团队的数据,特征团队都能够提供。通过传递一个特定的标签标识(tag_id),特征团队能够根据这个标识对数据进行聚合处理。理想情况下,如果特征团队能够无限期地保留这些数据,我们完全可以依赖他们进行实时数据拉取,无需本地保存数据快照。
  • 但经过与特征团队的沟通,我们了解到他们只能保留数据至多30天。这意味着,对于任何历史活动,都有数据无法追溯的风险,我们仍然需要在本地维护一份数据快快照。
  • 此外,数据一致性问题也需要重视。在某个时间段,特征团队会对相关指标进行计算和聚合,如果此时我们已经开始从他们那里读取数据,就会导致团队数据不等于司机维度的总和。为了确保数据的一致性,将数据聚合工作放在本地进行,并采用乐观锁机制来解决数据一致性的问题。

解决方案

  • 数据聚合与乐观锁:在本地进行数据聚合,使用乐观锁解决数据一致性问题。每半小时更新一次数据,采用类似CAS的机制,通过数据多版本控制读写一致性。
  • 使用Redis缓存
    • 活动版本号:以Key-Value存储
    • 活动数据:以Hash存储,Key为活动id加版本号,子Key为团队id。
    • 团队数据:同样以Hash存储,Key为团队id加版本号,子Key为司机id。
  • 更新流程:
    • 从特征团队UFS拉取司机维度数据(订单,流水,神访合格率等)。
    • 生成新版本号V2,写入Redis司机维度数据;聚合团队数据,写入团队维度数据。
    • 更新活动id指向新版本号V2。

通过上述解决方案,可以有效地应对团队PK赛中的数据处理挑战,确保排行榜的实时更新和数据的一致性。