本地缓存的数据一致性和可观测性的实践

一.本地缓存的前沿背景和使用场景

1.1.业务背景

在互联网-电商这个业务模块,特别C端模块,整体用户基数大,特别是在做营销活动的场景,甚至做秒杀活动的时候,访问量很高,而且C端是典型的大部分数据是多读少写场景,所以需要扛高并发,并且需要支持高性能,其中一个主要实现之一:添加策略缓存,在整个访问全链路上,各个地方添加缓存,对我们后端同学来说,常见的数据可能放入分布式缓存,甚至放入应用的本地缓存中,以便支持高吞吐量,低延迟的业务需求。

1.2.本地缓存的常见使用场景

数据量不是很大
非线形一直增长
修改频率较低
实时性要求较低
甚至是部分相对静态的数据
查询QPS要求极高
通过纯内存操作,避免网络I/O开销请求
例如:秒杀热点商品缓存、地域信息缓存、行政区信息缓存、常规的枚举数据缓存等

二.本地缓存的本身常见要点和方案实现

2.1.主要考虑的要点

支持key/value 形式的数据结构
支持线程安全
支持缓存过期时间
支持缓存淘汰策略
支持缓存大小控制
支持性能处理方面
支持统计和可观察性

2.2.方案实现之一ConcurrentHashMap

支持key/value 形式的数据结构(支持)
支持线程安全(支持)
缓存过期时间(本身不支持,需要额外单独实现)
缓存淘汰策略(本身不支持,需要额外单独实现)
缓存大小控制(本身不支持,需要额外单独实现)

2.3.方案实现之一Guava-Cache

支持key/value 形式的数据结构(支持)
支持线程安全(支持)
缓存过期时间 (支持,常见使用惰性删除,读请求中混杂着写操作)
缓存淘汰策略(支持,常见使用LRU算法)
缓存大小控制(支持)

2.4.方案实现之一Caffeine

支持key/value 形式的数据结构(支持)
支持线程安全(支持)
缓存过期时间(支持,使用异步淘汰数据的策略,缩短get请求的执行时长,间接提升了响应性能)
缓存淘汰策略(支持,使用一种高效的近似LFU算法,W-TinyLFU算法 特点:高命中率、低内存占用【利用堆外缓存降低内存缓存大小,减少GC频率】)
缓存大小控制(支持)
近似统计频率(采用 Count–Min Sketch算法【类似布隆过滤器】降低频率信息带来的内存消耗)支持热点缓存(支持,维护一个PK机制保证新进入的热点数据能够缓存)
支持异步(支持,eg:支持自动异步回源)
性能优化方面(很多地方采用了异步)

2.5.推荐的使用姿势和包含的相关功能

通过统一的SDK封装支持(目标:尽量和业务解耦;方便接入;统一的可维护性)
接入方式:AOP(可以融合多种缓存能力支持:分布式缓存 OR 本地缓存 OR 组合使用)
本地缓存:推荐选型Caffeine
KEY:使用SpEL动态表达式
可支持动态调整参数【例如:通过Apollo上配置动态支持】
支持缓存降级:支持降级
支持统计各业务key类型的命中率:支持
支持统计各业务key类型的QPS:支持

三.本地缓存的数据一致性

3.1.数据一致性的业务背景

正常的使用缓存的场景,多数是多读少写,我们可以支持TTL的设置,
在缓存失效的时候我们做异步回源,不过有部分业务需求,需要更高的强实效性和数据一致性
例如B端有相关的变更,部分业务场景,我们需要及时的支持数据一致性的闭环。
例如:强实效性的业务数据,eg:定时发布页面功能,修改敏感信息等

本地缓存数据一致性主要交互流程

3.2.实现之一-利用发布订阅模式

3.2.1 Redis的发布订阅模式

发布/订阅是一种消息模式,消息的发送者不会将消息直接发送给特定的接收者,而是通过消息通道广播出去,让订阅该消息主题的订阅者消费到,到达解耦
下面是主要相关伪代码参考示例
生产者:发布消息-主要部分代码示例参考

  /**
    * 生产者:发布消息-代码示例
    * @param channel:通道:类似topic(CommonCacheConstant.CHANGE_CHANNEL)
    * @param message:信息本体
    */
   redisTemplate.convertAndSend(String channel, Object message);

消费者:监听消息和处理消息-主要部分代码示例参考

  /**
    * 消息者:订阅消息-代码示例,通过Redis消息监听容器(加载了RedisConnectionFactory和MessageListenerAdapter)
    * @param RedisConnectionFactory:redis链接工厂
    * @param ChangeListener(MessageListenerAdapter):消息监听器
    */
    RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory,ChangeListener changeListener) 
    {
   
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(connectionFactory);
        container.addMessageListener(changeListener, new PatternTopic(CommonCacheConstant.CHANGE_CHANNEL));
        return container;
    }
    
    /**
    *  消息监听器-代码示例
    */
    public class ChangeListener extends MessageListenerAdapter implements MessageListener 
    {
   
        /**
         * 处理目标消息
         */
        @Override
        public void onMessage(Message message, @Nullable byte[] pattern)
        {
   
            String partKey = message.toString();
           //log.info("更新清除本地缓存:"+ partKey);
            Set<String> deleteKeySet = new HashSet<>();
            //TODO 封装deleteKeySet
            //例如:删除目标的本地缓存
            caffeineClient.invalidateAll(deleteKeySet);
        }
    }

3.2.2 MQ的广播模式

redis的发布的消息,接入使用相对比较简单,不过也有些缺点:例如:不会持久化,消息有被丢失的风险等
若对数据一致性要求比较高的业务场景,建议可以使用MQ的类似相关功能
很多MQ都有类似功能,我们以RocketMQ为示例-使用其中的广播模式
下面是主要伪代码参考示例
生产者:发送消息-主要部分代码示例参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

微盟技术中心

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值