1.数据库与缓存一致性方案
2.热key探测体系处置惩罚热key题目
3.缓存大value监控和切分处置惩罚方案
4.Redis内存不足强制回收监控诉警方案
5.Redis集群缓存雪崩自动探测 + 限流降级方案
6.缓存击穿的办理方法
线上Redis比较严肃的题目排序是:数据库和缓存一致性、热key、大value、缓存雪崩限流降级、内存不足强制回收
1.数据库与缓存一致性方案
(1)数据库与缓存同步双写强一致性方案
(2)数据库与缓存异步同步最终一致性方案
现有的业务场景下,都会涉及到数据库以及缓存双写的题目。⽆论是先删缓存再更新数据,或先更新数据再删缓存,都⽆法保证⼀致性。自己它们就不是⼀个数据源,⽆法通过代码上的谁先谁后去保证序次。
(1)数据库与缓存同步双写强一致性方案
这是适合中小企业的方案:读数据时自动进行读延期,实现数据冷热分离。在保证数据库和缓存一致性时使用分布式锁,第一个获得分布式锁的线程双写数据库和缓存成功后才释放分布式锁。然后在高并发下,通过锁超时时间,实现"串行等待分布式锁 + 串行读缓存"转"串行读缓存"。
(2)数据库与缓存异步同步最终一致性方案
如果不想对数据库和缓存进行双写,可以监听数据库binlog日志,通过异步来进行数据复制同步,从而保证数据的最终一致性。
这个方案需要先写成功DB,之后才能读到缓存value。这个方案需要确保binlog不能丢失,并且需要使用Canal监听binlog。
一.具体的数据一致性方案设计
⾸先对于所有的DB操作都不去添加具体的删除缓存操作,⽽是待数据确认已提交到数据库后,通过Canal去监听binlog的变化。
Canal会将binlog封装成消息发送到MQ,然后体系消费MQ的消息时,需要过滤出增编削类型的binlog消息。接着根据binlog消息 + 一致性相干的表和字段组装需要进行缓存删除的key,最后组装出key就可以对缓存进行删除了。
相干文章:
https://book.qq.com/book-search/%E5%90%8D%E4%BC%98%E9%A6%86%E7%BD%91%E3%80%9023Y4.com%E3%80%91?a21a
https://book.qq.com/book-search/%E6%B5%B7%E8%A7%92%E7%A4%BE%E5%8C%BA%E3%80%9023Y4.com%E3%80%91?b21b
https://book.qq.com/book-search/%E8%89%B3%E6%AF%8D%E7%BD%91%E8%BF%9B%E3%80%9023Y4.com%E3%80%91?c21c
https://book.qq.com/book-search/%E6%9E%9C%E5%86%BB%E4%BC%A0%E5%AA%92%E3%80%9023Y4.com%E3%80%91?d21d
https://book.qq.com/book-search/%E6%9E%9C%E5%86%BB%E4%BC%A0%E5%AA%92%E8%BF%9B23Y4.com%E7%9C%8B?e21e
https://book.qq.com/book-search/%E6%80%A7%E5%B7%B4%E5%85%8B%E8%BF%9B%E3%80%9023Y4.com%E3%80%91?f21f
https://book.qq.com/book-search/%E7%88%B1%E5%A8%81%E5%A5%B6%E7%BD%91%E3%80%9023Y4.com%E3%80%91?g21g
https://book.qq.com/book-search/%E7%A6%81%E6%BC%AB%E5%A4%A9%E5%A0%82%E3%80%9023Y4.com%E3%80%91?c21a
https://book.qq.com/book-search/%E6%92%B8%E6%92%B8%E7%A4%BE%E7%BD%91%E3%80%9023Y4.com%E3%80%91?a21b
https://book.qq.com/book-search/%E6%8A%96%E9%98%B4%E4%B8%8B%E8%BD%BD%E3%80%8A23Y4.com%E3%80%8B?b21c
https://book.qq.com/book-search/%E6%8A%96%E9%98%B4%E7%BD%91%E7%AB%99%E3%80%9023Y4.com%E3%80%91?c21d
二.具体的数据一致性方案流程图
三.处置惩罚MQ消息保证最终数据一致性
- //处理MQ消息保证最终数据一致性
- @Slf4j
- @Component
- public class CookbookConsistencyListener implements MessageListenerConcurrently {
- @Autowired
- private RedisCache redisCache;
- //处理MySQL的binlog变化,处理需要清理的缓存key
- @Override
- public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
- try {
- for (MessageExt messageExt : list) {
- String msg = new String(messageExt.getBody());
- //解析binlog数据模型,并过滤掉查询
- BinlogDataDTO binlogData = buildBinlogData(msg);
- //获取binlog的模型,获取本次变化的表名称,在本地配置常量类里面匹配对应的缓存key前缀以及缓存标识字段,非配置的表不进行处理
- String cacheKey = filterConsistencyTable(binlogData);
- //删除该key的缓存
- deleteCacheKey(cacheKey);
- }
- } catch (Exception e) {
- log.error("consume error, 缓存清理失败", e);
- //本次消费失败,下次重新消费
- return ConsumeConcurrentlyStatus.RECONSUME_LATER;
- }
- return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
- }
- //解析binlog的数据模型,并过滤掉查询的binlog
- private BinlogDataDTO buildBinlogData(String msg) {
- //先解析binlog的对象,转换为模型
- BinlogDataDTO binlogData = BinlogUtils.getBinlogData(msg);
- //模型为null,则直接返回
- if (Objects.isNull(binlogData)) {
- return null;
- }
- Boolean isOperateType = BinlogType.INSERT.getValue().equals(binlogData.getOperateType())
- || BinlogType.DELETE.getValue().equals(binlogData.getOperateType())
- || BinlogType.UPDATE.getValue().equals(binlogData.getOperateType());
- //只保留增删改的binlog对象,如果数据对象为空则也不处理
- if (!isOperateType || CollectionUtils.isEmpty(binlogData.getDataMap())) {
- return null;
- }
- //返回解析好的可用模型
- return binlogData;
- }
- //过滤掉目前不需要处理的表的Binlog,并返回组装所需的缓存key
- private String filterConsistencyTable(BinlogDataDTO binlogData) {
- if (Objects.isNull(binlogData)) {
- return null;
- }
- String tableName = binlogData.getTableName();
- List<Map<String, Object>> dataList = binlogData.getDataMap();
- //获取配置的常量映射的具体配置
- ConsistencyTableEnum consistencyTableEnum = ConsistencyTableEnum.findByEnum(tableName);
- if (Objects.isNull(consistencyTableEnum)) {
- return null;
- }
- String cacheValue = "";
- if (CollectionUtils.isNotEmpty(dataList)) {
- Map<String, Object> dataMap = dataList.get(0);
- cacheValue = dataMap.get(consistencyTableEnum.getCacheField()) + "";
- }
- if (StringUtils.isBlank(cacheValue)) {
- return null;
- }
- //获取配置的缓存前缀key + 当前的标识字段,组装缓存key
- return consistencyTableEnum.getCacheKey() + cacheValue;
- }
- //对缓存进行清理
- private void deleteCacheKey(String cacheKey) {
- if (StringUtils.isBlank(cacheKey)) {
- return;
- }
- redisCache.delete(cacheKey);
- }
- }
- public enum ConsistencyTableEnum {
- //商品表缓存配置
- SKU_INFO("sku_info", RedisKeyConstants.GOODS_INFO_PREFIX, "id");
- //配置相关的表名称
- private final String tableName;
- //缓存的前缀key
- private final String cacheKey;
- //缓存的标识字段
- private final String cacheField;
- ...
- }
复制代码
2.热key探测体系处置惩罚热key题目
(1)什么是热key题目
(2)怎样办理热key题目
(3)开源热key探测体系的工作流程图和架构图
当体系大量使用Redis进行开发后,线上肯定会碰到热key和大value题目。
(1)什么是热key题目
比如微博大热门,瞬间万万级大流量都会涌入某微博来浏览某个数据。这时如果这个热门数据是存储在一个Redis节点里,那么就会出现每秒百万级的请求都到一个Redis节点去了。如下图示:
(2)怎样办理热key题目
为了办理热key题目,我们首先需要一个热key探测体系。热key探测体系会在服务体系(Redis客户端)进行接入统计,一旦热key探测体系在服务体系识别出某个key符合热key的条件,那么就会将这个热key的数据缓存到服务体系的JVM本地缓存里。
所以,热key探测体系具备的两大核心功能:
一.自动探测热key
二.自动缓存热key数据到JVM本地缓存
(3)热key探测体系的工作流程图和架构图
一.热key探测体系的工作流程简图
二.热key探测体系的具体架构图
3.缓存大value监控和切分处置惩罚方案
大value,顾名思义,就是value值特别大,几M甚至几十M。如果在一次网络读取里面,频繁读取大value,会导致网络带宽被占用掉。value太大甚至会把带宽打满,导致其他数据读取请求非常。所以对于大value,要进行特殊的切分处置惩罚。
一.首先要能够对Redis里的大value进行监控。如果发现超过1MB的大value 的值,就监控和报警。
二.然后进行自动处置惩罚,也就是把这个value的值,转换为拆分字符串来缓存。比如将一个大value拆分为10个串,把一个kv拆分为10个kv来存储:test_key => test_key_01、test_key_02...
三.接着进行读取的时间,则依次读取这拆分字符串对应的key,最后将读出来的拆分字符串进行重新拼接还原成原来的大value值。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |