ICode9

精准搜索请尝试: 精确搜索
首页 > 数据库> 文章详细

总结篇:redis 典型缓存架构设计问题及性能优化

2022-08-06 22:01:19  阅读:128  来源: 互联网

标签:架构设计 缓存 数据库 redis bigkey key 数据


redis 典型缓存架构设计问题及性能优化总结:

缓存穿透

查询一个根本不存在的数据,缓存层和存储层都不会命中。通常出于容错的考虑,如果从存储层查不到数据,则不写入缓存层。

原因:

自身业务代码或数据有问题
恶意攻击等造成大量空命中
解决方案1:缓存空对象

解决方案2:布隆过滤器

当布隆过滤哭喊 说某个值存在时,这个值可能不存在。当说它不存在时,那就肯定不存在。

对于不存在的数据布隆过滤器一般都能过滤掉,不再让请求再往后端发送。

布隆过滤器就是一个大型的位数组和几个不一样的无偏hash 函数,所谓无偏就是能够把元素的hash 值算得比较均匀。

这种方法适用于数据命中不高、数据相对稳定、实时性低的应用场景,通常是数据集较大,代码维护较为复杂,但是缓存空间占用较少。

缓存击穿

大量缓存同时失效导致请求同时穿透缓存直达数据库,可能会造成数据库瞬间压力过大挂掉。最好将这一批数据的缓存过期时间设置为一个时间段内的不同时间。

int expireTime - new Random().nextInt(300) + 300;

缓存血崩

如果缓存架构设计得不好,大量请求访问bigkey,导致缓存能支撑的并发急剧下降,大量请求都会打到存储层,造成存储层也会级联宕机的情况。 解决问题:

1 保证缓存层服务高可用性,比如使用redis Sentinel 或 redis Cluster
2 依赖隔离组件为后端限流熔断并降级。比如使用 Sentinel 或 Hystrix 限流降级组件。
我们可以针对不同的数据采取不同的处理方式。当业务应用访问的是非核心数据,如商品属性,用户信息等,暂时停止从缓存中查询这些数据,而是直接返回预定义的默认降级信息、空值或是错误提示信息;当业务应用访问的是核心数据,如商品库存,仍然允许查询缓存,如果缓存缺失,可以继续通过数据库读取。

3 做好数据容灾。提前演练,并做一些预案。
热点数据缓存优化

使用“缓存+过期时间”的策略既可以加速数据读写,又保证数据的定期更新,这种模式基本能够满足绝大部分需求。

但是这种策略存在的问题,对应用却是致命的。

当前key 是一个热点key, 如热门活动,并发量非常大
重建缓存不能在短时间完成,可能是一个复杂计算,例如繁杂的SQL,多次IO, 多个依赖等,在缓存操作新选的瞬间,有大量纯种来重建缓存,造成后端负载加大,甚至可能会让应用崩溃。
解决这个问题,就是要避免大量纯种同时重建缓存。

可以利用互斥锁,此方法只允许一个纯种重建缓存,其他线程等待重建缓存的线程执行完,重新从缓存获取数据。

String get(String key) {

// 从redis 中获取数据
String value = redis.get(key);

// 如果value 为空,则重构缓存
if(null == value){
    // 只允许一个线程重建缓存,使用nx, 并设置过期时间ex
    String mutexKey = "mutext:key:" + key;
    
    if(redis.set(mutexKey,"1","ex 180",nx)){
        // 从数据库中取数据
        value = db.get(key);
        // 设置过期时间
        redis.setex(key,timeout,value);
        // 删除key_mutex
        redis.delete(mutexKey);
    } else {
        Thread.sleep(50);
        
        get(key);
    }
    
}
return value;

}

缓存数据库读写不一致

1 双写不一致
2 读写不一致
解决方案:

1 对于并发几率很小的数据,如个人维度的订单数据,用户数据等,这种几乎不用考虑这个问题,很少会发生缓存不一致,可以给缓存数据加上过期时间,每隔一段时间触发读的主动更新即可。
2 就算并发很高,如果业务上能容忍短时间的缓存数据不一致,如商品名称,商品分类菜单等,缓存加上过期时间依然可以解决大部分业务对于缓存的要求。
3 如果不能容忍缓存数据不一致,可以通过读写锁保证并发读写或写写的时间按顺序排好队,读读的时间相当于无锁。
4 也可以用阿里开源的canal 通过监听数据库的binlog 日志及时的去修改缓存,但是引入了新的中间件,增加了系统的复杂度。
小结:一般针对是读多写少的情况加入缓存提高性能,如果写多读多的情况又不能容忍缓存数据不一致,那就没必要加缓存了,可以直接操作数据库。如果数据库抗不住压力,还可以把缓存作为数据读写的主存储,异步将数据同步到数据库,数据库只是作为数据的备份。

加入缓存的数据库应该是对实时性、一致性要求不是很高的数据,切记不要为了用缓存,同时又要保证绝对的一致性做大量的过度设计和控制,增加系统的复杂性。

开发规范

key 名设计

1 建议:可读性和可管理性
以业务名或数据库名库前缀,防止key 冲突,用冒号分隔。如 业务名:表名:id

2 建议:简洁性
保证语义的前提下,控制key 长度,当key 较多时,内存占用也不容忽视。

3 强制:不要包含特殊字符
反例:空格,换行,单双引号以及其他转义字符

value 设计

1 强制:拒绝bigkey ,防止网卡流量,慢查询
在redis 中,一个字符串最大512M, 一个二级数据结构可以存储大约40亿(2^32 -1)个元素,但是实际中如果有下面两种情况,我们认为它是bigkey。

1 字符串类型:它的big 体现在单个value 值很大,一般认为超过10KB 就是bigkey。
2 非字符串类型:hash, list,set, zset,它们的big 体现在元素个数太多。
一般来说,String 类型控制在10kb 以内, hash, list, set, zset 元素个数不要超过5000。非字符串的bigkey,不要使用del 删除,使用hscan,sscan,zscan 方式浙进式删除,同时要注意防止bigkey 过期时间自动删除问题。(如一个200w 的zset 设置一个小时过期,会触发 del 操作,造成阻塞)

bigkey 性能优化

1 bigkey 的产生

一般来说,bigkey 的产生都是由于程序设计不当,或者对于数据规模预料不清楚造成的。

1 社交类:大V 粉丝列表,如果设计不当,必是bigkey
2 统计类:如按天存储某项功能或者网站的用户集合,除非没人用,否则必是bigkey
3 缓存类:将数据从数据库中load 出来序列化放在redis 中,但是要注意1-是不是有必要把所有字段都缓存;2-有没有相关关联的数据,为图方便而产生关联数据, 产生bigkey.
2 如何优化

1 拆

big list : list1, list2, ... , listN

big hash :可以将数据分段存储,比如一个大的key, 假设存了100w 的用户数据,可以拆分成200个key, 每个key 下面5000个用户数据。

2 推荐:选择适合的数据类型

举例:

// 正例:
hmset user:1 name tom age 20 favor swimming

// 反例:
set user 1 : name tom
set user 1 : age 20
set user 1 : favor swimming

3 推荐:控制key 的生命周期

建议使用expire 设置过期时间,同时过期时间要随机,防止集中过期。

标签:架构设计,缓存,数据库,redis,bigkey,key,数据
来源: https://www.cnblogs.com/yizhiamumu/p/16557996.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有