美文网首页
设计 | 基于 Redis 谈一谈缓存设计思想

设计 | 基于 Redis 谈一谈缓存设计思想

作者: RadonDB | 来源:发表于2021-08-31 14:27 被阅读0次

王奇 顾问软件工程师

目前从事 PaaS 中间件服务(Redis/MongoDB/ELK 等)开发工作,对 NoSQL 数据库有深入的研究以及丰富的二次开发经验,热衷对 NoSQL 数据库领域内的最新技术动态的学习,能够把握行业技术发展趋势。

| 前言

前段时间跟同事一起聊到 Redis 的那些坑,不谋而合感触很深。有的时候当业务规模未达到一定瓶颈,很可能发现不了问题,例如下面的这段代码。

public static LotteryPeriod getCurrentAwardNumberPeriod(String gameId) {
    List<LotteryPeriod> periodList = getPeriodsByGameId(gameId);
    if (periodList == null) {
        return null;
    }
    Timestamp now = DateUtil.getCurrentTimestamp();
    LotteryPeriod lotteryPeriod = null;
    for (LotteryPeriod period : periodList) {
        if (period.getEndTime().before(now) && period.getAwardTime().before(now)) {
          if (LotteryAwardCache.getLotteryAward(gameId, period.getPeriod(), false) != null) {
             lotteryPeriod = period;
             break;
          }
        }
    }
    return lotteryPeriod;
}

这是一个关于期次类的一个业务,这么写乍眼看没什么问题,但由于业务类型增加,时间一久代码量超过了 3000+,接近 20 个getXXPeriod(String),Java 运行后加载了几万个 Period 实例。

| 改进思路

  • Timestamp --> Long
  • startTime, endTime....betDeadline 迭代比较 --> Timeline
  • Cache

| Period 缓存设计思想

file

Period 实体类包含以上私有属性和构造方法,系统所有有关期次的时间计算均围绕 startTime、endTime、saleStartTime、saleEndTime、awardTime、openTime 这些属性。

如果将所有期次对象按照期次顺序缓存起来,后续的计算也不方便。同时效率也是需要考虑的,如果每次用户读取某些特殊需求的期次,例如获取当前销售期次,每次都需要循环处理并判断销售开始时间、结束时间与当前时间的关系。最后,Timestamp 的比较在 Redis 缓存里面不能直接计算,这样导致很多计算需要将数据传输到本地后再进行,计算的算法效率也不一定有 Redis 高。

基于上述几点考虑做以下设计:将每一个时间维度按照 Redis 缓存的 SortedSet 结构保存,Timestamp 转化为 Long 作为 SortedSet 的 score 存储,member 则为 periodId;为了提高用户访问效率,将每个特定需求的期次信息直接使用 Redis 缓存 String 结构保存,即将对象转化为字节数组保存;为保证这些数据时效性,设置合理的过期时间并且定时刷新这些数据。

2.png

一级缓存

总计有五种时间轴(TimelineEnum 枚举类)—— START_TIME、END_TIME、SALE_START_TIME、SALE_END_TIME、OPEN_TIME。

每种时间轴使用 SortedSet 结构保存,初始化的时候载入最新一定数量的期次,一般是未来 N 天以及过去 M 天,这里的数量主要保证追号功能和最近开奖号码功能显示正常即可。时间轴对应的时间属性转化为 Long 后作为 SortedSet 的 score,periodId 则为 member,这样每条时间轴均按照对应时间属性从小到大排序,获取满足小于或者大于某个时间点的期次列表使用 Redis 命令即可方便获取。

file

系统提供periodRedisService.refreshTimeline(gameId)按照指定彩种刷新所有的 Timeline,提供periodRedisService.rebuildTimeline(gameId)按照指定彩种重建所有的 Timeline,此方法会先删除所有 Timeline 的 Key 再刷新。

二级缓存

总计有以下系统需要访问对象(PeriodEnum 枚举类)—— CURRENT_PERIOD、CURRENT_PERIODS、TODAY_PERIODS、CURRENT_SALE_PERIOD、NEXT_SALE_PERIOD、LAST_10AWARD_PERIOD、FUTURE_3DAY_PERIOD、LAST_OPEN_PERIOD、RECENT_3PERIODS、LAST_100AWARD_PERIOD。

每种对象使用 String 结构保存,定时刷新这些已经过期的缓存。系统提供 periodRedisService.refreshPeriodInfo(gameId) 按照指定彩种刷新所有的这些缓存信息,提供 periodRedisService.refreshExpirePeriodInfo(gameId) 按照指定彩种刷新所有的 Key。

| 改善后

@Override
public List<GamePeriod> getCurrentPeriods(Long gameId) {
    String key = RedisConstant.getPeriodDetailKey(gameId, RedisConstant.CURRENT_PERIOD);
    List<GamePeriod> list = redisService.hessian2Get(key);
    if (GameCache.getGame(gameId).getGameType() != Game.GAME_TYPE_TRADITIONAL) {
        list = resetRedisTimeline(gameId, RedisConstant.CURRENT_PERIOD, list);
    }
    return (list == null || list.isEmpty()) ? null : list;
}

相关文章

  • 设计 | 基于 Redis 谈一谈缓存设计思想

    王奇 顾问软件工程师目前从事 PaaS 中间件服务(Redis/MongoDB/ELK 等)开发工作,对 NoSQ...

  • 【MyBatis】学习纪要八:缓存(二)

    缓存工具 MyBatis缓存 Ehcache Redis 缓存设计 缓存接口 我们来讨论一下,说到MyBatis,...

  • Redis第三天

    基于Redis设计的投票网站实战

  • redis缓存设计

    缓存的利于弊及应用场景 这里我们主要讨论以Redis为代表的基于内存的缓存方案。 缓存的优点 提升访问速度,减少后...

  • 缓存设计思想

    缓存设计思想 概念思想 缓存就是将频繁使用的资源或者数据放入到内存中,每次操作的时候,先到内存中获取,获取不到,再...

  • Metro 的分析及其局限(Spr.1 2015)

    @(Diary) 主要从Metro 谈一谈设计和心理学。 Metro是基于瑞士平面设计的设计原则(清晰,真实,美观...

  • MI 2021-07-09

    一面 问的设计题偏多 Redis 缓存热点数据 热点数据指的是什么? Redis缓存数据量关注过吗? 如果全部缓存...

  • Redis入门——缓存设计

    缓存优点 加速读写:缓存通常是全内存的(例如Redis、Memcache),而存储层通常读写性能不够强悍,通过缓存...

  • Redis 缓存设计原则

    基本原则 只应将热数据放到缓存中 所有缓存信息都应设置过期时间 缓存过期时间应当分散以避免集中过期 缓存key应具...

  • Redis学习--缓存设计

    缓存的收益和成本 加速读写 降低后端负载缓存层+存储层基本流程 数据不一致性 代码维护成本 运维成本 缓存更新策略...

网友评论

      本文标题:设计 | 基于 Redis 谈一谈缓存设计思想

      本文链接:https://www.haomeiwen.com/subject/lojziltx.html