团队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: 更新活动版本号
上面的流程可以概括为:- 活动计算:由Crontab触发,每30分钟执行一次。
- 待处理活动列表:后台系统维护一个待处理的活动列表。
- 活动分片:将活动分解成多个分片,每个分片可以独立处理。
- 解析活动分片:对每个分片进行解析,准备进一步的处理。
- 获取分片成员:确定每个分片需要处理的数据或任务。
挑战二:数据计算、展示与更新
问题描述:
进行中的活动每半小时更新一次排行榜,但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赛中的数据处理挑战,确保排行榜的实时更新和数据的一致性。