• Nresource服务之接口缓存化


    1、 背景

        Nresource服务日均4.5亿流量,考虑到未来流量急增场景,我们打算对大流量接口进行缓存化处理;根据服务管理平台数据统计显示getUsableResoureCount接口调用量很大,接近40%,故对此接口进行缓存化处理。

    2、 方案调研

        getUsableResoureCount接口用途:获取用户的可用资源数,契约为:Map<String, Integer> getUsableResoureCount(long userId, int resourceType, ResourceCountTypeEnum typeEnum, List<Integer> cities, List<Integer> caties) throws Exception;支持传入资源数类型(全国/本地)多城市和多类目按照包含的策略进行资源过滤;

        由于资源库存表设计字段:城市和类目写入格式为:1,2,3,即以逗号组合的字符串,所以如果缓存用户对应城市和类目的所有余量,实现难度大且意义不大。

        通过对接口返回值的观察,我们发现getUsableResoureCount接口返回值很多都是查询用户所有资源余量,不区分城市和类目,且查询结果都为0,所以我们切换思路:是不是可以把这些无余量的数据缓存起来?

        通过2021-07-27 10:00:00数据统计我们发现,单节点一分钟getUsableResoureCount接口调用量为395,其中查询所有资源余量的微287,返回值为0的为183,接近46%的请求是无数据的,所以缓存用户无余量的数据是有意义的,能挡住接近50%的无用请求。

        缓存用户无余量的数据,我们需要在所有涉及余量增减的操作,同时维护redis中余量变化,如何维护db与缓存中数据的一致性,也是面临的问题,所以我们想:可不可以不缓存余量,只缓存一个余量标识,这样既能挡住无用请求,又不需要维护db与缓存的数据一致性呢?参考布隆过滤器,我们决定对此接口进行缓存余量标识,只记录有还是没有,即1和0。

    3、 方案实施

    getUsableResoureCount接口先查询缓存,如果返回值为0,则放入缓存,所有增加余量的操作均需要更新redis标识为1。基本目标就是:保证更新标识为有的操作一定成功。考虑到服务并发,写操作(更新redis标识为1)可能会被读操作(更新redis标识为0)覆盖,所以更新redis标识为0的时候,先增加时间校验即晚一分钟更新,因为redis标识更新为0或null,不会影响最终查询,而是会查询db,晚一分钟又可以避免读写覆盖的问题。最终采用lua脚本实现读操作,而写操作采用同步set异步重试保证最终成功;

    ps :有没有考虑过载读接口中为什么要晚一分钟中更新缓存?

    答:主从延迟,以及并发场景下,将缓存更新错,命名用户有资源,结果给更新成了0从而影响用户使用。

    lua脚本如下:

    4、最终效果

    接口响应耗时:

    2021.9.2 VS 2021.9.9

    2021.9.8 VS 2021.9.9

    性能提升(ms):1.4 -> 0.9 提升超过30%

    整体服务响应耗时:

    2021.9.8 VS 2021.9.9

    性能提升(ms):1.8 -> 1.6 提升超过10%

     

    缓存命中率统计:

    服务节点:10.***.60.***

    命中率接近80%

    5、未来考量

    1)缓存的扩展性,未来新的写接口需要更新redis标识为1;

    2)缓存的接口适配性,考虑更多查询接口应用缓存; 

      ps: 整体思路,由于业务原因,没有采用传统缓存用redis存有效数据的思路,我们是反其道行之,用redis来挡住无效流量

  • 相关阅读:
    js---05 自定义属性
    js---04 属性 this
    js---03属性操作
    js02---字符串
    js01----json,数组
    android framework 02
    android framework 01
    Android Service完全解析,关于服务你所需知道的一切(下)
    Android Framework 记录之二
    Android Framework 记录之一
  • 原文地址:https://www.cnblogs.com/yulinfu/p/15335120.html
Copyright © 2020-2023  润新知