最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 架构设计 缓存管理模式,监控和内存回收策略

    正文概述 掘金(用户1548941426154)   2021-04-26   379

    一、缓存设计

    1、缓存的作用

    在业务系统中,查询时最容易出现性能问题的模块,查询面对的数据量大,筛选条件复杂,所以在系统架构中引入缓存层,则是非常必要的,用来缓存热点数据,达到快速响应的目的。

    缓存使用的基本原则:

    • 所有缓存数据,必须设置过期时间;
    • 核心业务流程不通过缓存层;
    • 缓存层移除,不影响现有流程;
    • 系统各个端首页数据不实时查询;
    • 报表数据不实时查询加载;
    • 归档数据(定时统计的结果数据)不实时查询;

    这里是业务架构中常用的缓存策略,缓存通过牺牲强一致性来提高性能,所以并不是所有的业务都适合用缓存,实际考量都会针对具体的业务,比如用户相关维度的数据修改频率低,会使用缓存,但是用户权限数据(比如:免费次数)会考虑实时校验,缓存层使用的相对较少。

    2、缓存设计模式

    Cache-Aside模式

    业务中最常用的缓存层设计模式,基本实现逻辑和相关概念如下:

    架构设计  缓存管理模式,监控和内存回收策略

     

    • 缓存命中:直接查询缓存且命中,返回数据;
    • 缓存加载:查询缓存未命中,从数据库中查询数据,获取数据后并加载到缓存;
    • 缓存失效:数据更新写到数据库,操作成功后,让缓存失效,查询时候再重新加载;
    • 缓存穿透:查询数据库不存在的对象,也就不存在缓存层的命中;
    • 缓存击穿:热点key在失效的瞬间,高并发查询这个key,击穿缓存,直接请求数据库;
    • 缓存雪崩:缓存Key大批量到过期时间,导致数据库压力过大;
    • 命中率:缓存设计的是否合理要看命中率,命中率高说明缓存有效抗住了大部分请求,命中率可以通过Redis监控信息计算,一般来说命中率在(70-80)%都算合理。 并发问题

    执行读操作未命中缓存,然后查询数据库中取数据,数据已经查询到还没放入缓存,同时一个更新写操作让缓存失效,然后读操作再把查询到数据加载缓存,导致缓存的脏数据。

    在遵守缓存使用原则下出现该情况概率非常低,可以通过复杂的Paxos协议保证一致性,一般情况是不考量该场景的处理,如果缓存管理过于复杂,会和缓存层核心理念相悖。

    基本描述代码:

    @Service
    public class KeyValueServiceImpl extends ServiceImpl<KeyValueMapper, KeyValueEntity> implements KeyValueService {
        @Resource
        private RedisService redisService ;
        @Override    public KeyValueEntity select(Integer id) {        // 查询缓存
            String redisKey = RedisKeyUtil.getObectKey(id) ;        String value = redisService.get(redisKey) ;        if (!StringUtils.isEmpty(value) && !value.equals("null")){
                return JSON.parseObject(value,KeyValueEntity.class);
            }        // 查询库
            KeyValueEntity keyValueEntity = this.getById(id) ;
            if (keyValueEntity != null){
                // 缓存写入
                redisService.set(redisKey,JSON.toJSONString(keyValueEntity)) ;
            }
            // 返回值
            return keyValueEntity ;
        }
        @Override
        public boolean update(KeyValueEntity keyValueEntity) {
            // 更新数据
            boolean updateFlag = this.updateById(keyValueEntity) ;
            // 清除缓存
            if (updateFlag){
                redisService.delete(RedisKeyUtil.getObectKey(keyValueEntity.getId()));
            }
            return updateFlag ;
        }
    }
    

    Read-Throug模式

    当应用系统向缓存系统请求数据时,如果缓存中并没有对应的数据存在,缓存系统将向底层数据源的读取数据。如果数据在缓存中存在,则直接返回缓存中存在的数据。把更新数据库的操作由缓存层代劳了。

    Write-Through模式

    更新写数据时,如果没有命中缓存,则直接更新数据库,如果命中了缓存,则先更新缓存,然后由缓存系统自行更新数据库。

    Write-Behind模式

    应用系统对缓存中的数据进行更新时,只更新缓存,不更新数据库,缓存系统会异步批量向底层数据源更新数据。

    二、数据一致问题

    业务开发模式中,会涉及到一个问题:如何最大限度保证数据库和Redis缓存的数据一致性?

    首先说明一下:数据库和缓存强一致性同步成本太高,如果追求强一致,缓存层存在的价值就会很低,如上缓存模式一中几乎可以解决大部分业务场景问题。

    解决这个问题的方式很多:

    架构设计  缓存管理模式,监控和内存回收策略

     

    方案一说明:

    • 数据库更新写入数据成功;
    • 准备一个先进先出模式的消息队列;
    • 把更新的数据包装为一个消息放入队列;
    • 基于消息消费服务更新Redis缓存;

    分析:消息队列的稳定和可靠性,操作层面数据库和缓存层解耦。

    方案二说明:

    • 提供一个数据库Binlog订阅服务,并解析修改日志;
    • 服务获取修改数据,并向Redis服务发送消息;
    • Redis数据进行修改,类似MySQL的主从同步机制;

    分析:系统架构层面多出一个服务,且需要解析MySQL日志,操作难度较大,但流程上更为合理。

    总结描述

    分布式架构中,缓存层面的基本需求就是提高响应速度,不断优化,追求数据库和Redis缓存的数据快速一致性,从提供的各种方案中都可以看出,这也在增加缓存层面处理的复杂性,架构逻辑复杂,就容易导致程序错误,所以针对业务选择合理的处理逻辑,这点很关键。

    三、缓存监控

    1、Redis服务监控

    通过info命令查看Redis服务的参数信息,可以通过传参查看指定分类配置。通过config..set设置具体配置参数。例如:

    @Override
    public Properties info(String var) {
        if (StringUtils.isEmpty(var)){
            return redisTemplate.getRequiredConnectionFactory().getConnection().info();
        }    return redisTemplate.getRequiredConnectionFactory().getConnection().info(var);
    }
    

    传参说明:

    • memory:内存消耗相关信息
    • server:有关Redis服务器的常规信息
    • clients:客户端连接部分
    • stats:一般统计
    • cpu:CPU消耗统计信息

    应用案例:

    @RestController
    public class MonitorController {
        @Resource
        private RedisService redisService ;
        private static final String[] monitorParam = new String[]{"memory","server","clients","stats","cpu"} ;
        @GetMapping("/monitor")
        public List<MonitorEntity> monitor (){
            List<MonitorEntity> monitorEntityList = new ArrayList<>() ;
            for (String param:monitorParam){
                Properties properties = redisService.info(param) ;            MonitorEntity monitorEntity = new MonitorEntity () ;
                monitorEntity.setMonitorParam(param);            monitorEntity.setProperties(properties);            monitorEntityList.add(monitorEntity);        }        return monitorEntityList ;
        }}
    

    通过上述参数组合,把Redis相关配置参数打印出来,然后可视化输出,俨然一副高端的感觉。

    配置参数说明:

    这里只对两个参数说明一下,计算命中率的关键信息:

    • keyspace_misses:查找缓存Key失败的次数;
    • keyspace_hits:查找缓存Key命中的次数;

    公式:命中率=命中次数/(hits+misses)查找总次数。

    2、LRU算法说明

    Redis的数据是放在内存中的,所以速度快,自然也就受到内存大小的限制,如果内存使用超过配置,Redis有不同的回收处理策略。

    内存模块参数:maxmemory_policy

    • noenviction:不回收数据,查询直接返回错误,但可以执行删除;
    • allkeys-lru:从所有的数据中挑选最近最少使用的数据淘汰;
    • volatile-lru:已设置过期时间的数据中挑选最近最少使用的数据淘汰;
    • allkeys-random:从所有数据中任意选择数据淘汰;
    • volatile-random:从已设置过期时间的数据中任意选择数据淘汰;
    • volatile-ttl:从已设置过期时间的数据中挑选将要过期的数据淘汰;

    大部分情况下,业务都是希望最热点数据可以被缓存,所以相对使用allkeys-lru策略偏多。这里要根据业务模式特点衡量。


    起源地下载网 » 架构设计 缓存管理模式,监控和内存回收策略

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元