投行系统的毫秒级榜单响应:如何用Redis ZSET破解同分排序难题?

不想看推导过程的小伙伴,直接看结尾一句话也就够了

背景和痛点

投行交易系统交易排行榜,存在以下痛点:

  1. 需要实时排行榜
  2. 当交易量排名相同时怎么解决?要避免同分跳变

设计思路

Redis可以实时显示排行榜
ZSET数据结构: 可以实现存储不重复的数据,包含score和value

如何解决同分僵局?

(✅推荐)方案1:整数部分保持不变(实时交易量),小数部分:采用时间戳

交易量相同的时候,按照最后更新时间进行判断
1)加入更新时间戳。作为小数部分
2)1-小数部分的更新时间戳
score = volume(交易量) + (1 - update_time(更新时间戳)/ 10的12次方)

交易id交易量时间戳score
10011101740979215805110.8259020784195
10021101740979215806110.8259020784194
10031201740979215812120.8259020784188
10041101740979215807110.8259020784193

这种方案的好处有两个:

  1. 直接在redis中可以进行排序,保证排行的实时性能
  2. 不需要应用层的参与

(❌不推荐)方案2:应用层进行二次排序

没什么好说的,需要从Redis拿到数据之后,再根据业务字段进行排序,性能不好,不要选!

上代码

注意: Redis ZSET格式的添加返回的数据,不要放在一个dto中,分为两个DTO

DTO

@Data
public class TradeVolumeRequest {
    // 在Redis中 score = volume(交易量) + (1 - update_time(更新时间戳)/ 10的12次方)
    private String stockId;
    private Integer volume;
    // 更新时间
    private Timestamp updateTime;
}
@Data
public class TradeRanking {
    private String stockId;
    private Double rankingzVolume;
}

controller

@RestController
@Slf4j
public class TradeRankingController {
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    private static final String TRADE_RANKING_KEY = "trade:volume:ranking";

    @PostMapping("/addTradeVolume")
    public String addTradeVolume(@RequestBody List<TradeVolumeRequest> tradeVolumeRequests) {
        tradeVolumeRequests.forEach(tradeVolumeRequest -> {
            double rankingScore = tradeVolumeRequest.getVolume() + (1 - System.currentTimeMillis() / 10e12);
            stringRedisTemplate.opsForZSet()
                    .add(TRADE_RANKING_KEY, tradeVolumeRequest.getStockId(), rankingScore);
        });
        return "存入数据成功, key:" + TRADE_RANKING_KEY;
    }

    @GetMapping("/getTopN")
    public List<TradeRanking> getTopN(@RequestParam(value = "topN", defaultValue = "3") int topN) {
        List<TradeRanking> tradeRankingList = new ArrayList<>();

        Set<ZSetOperations.TypedTuple<String>> typedTuples = stringRedisTemplate.opsForZSet()
                .reverseRangeWithScores(TRADE_RANKING_KEY, 0, topN - 1);
        for (ZSetOperations.TypedTuple<String> typedTuple : typedTuples) {
            TradeRanking tradeRanking = new TradeRanking();
            tradeRanking.setStockId(typedTuple.getValue());
            tradeRanking.setRankingzVolume(typedTuple.getScore());
            tradeRankingList.add(tradeRanking);
        }
        return tradeRankingList;
    }
}

测试结果

数据存入Redis中,score按照上述的公式

在这里插入图片描述

获取topN

在这里插入图片描述

一句话

通过Redis ZSET加更新时间戳,解决百万交易排行榜实时显示 同分难题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值