首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

Redis 性能调优——缓存设计优化

  • 25-03-02 12:43
  • 3144
  • 11575
blog.csdn.net

Redis 是一个开源的高性能的 Key-Value 服务器。本篇主要介绍一下缓存的设计与优化。

1. 缓存的受益与成本

-说明
缓存的受益1、加速读写,通过缓存加速读写速度,例如 CPU L1/L2/L3 Cache、Linux page Cache 加速硬盘读写、浏览器缓存、Ehcache 缓存数据库结果;
2、降低后端负载,后端服务器通过前端缓存降低负载,业务端使用 Redis 降低后端 MySQL 负载等。
缓存的成本1、数据不一致,缓存和数据层有时间窗口不一致,和更新策略有关;
2、代码维护成本增加,多了一层缓存逻辑;
3、运维成本增加。

缓存的使用场景:

  • 降低后端负载,对高消耗的 SQL,例如 join 结果集/分组统计结果缓存;
  • 加速请求响应,利用 Redis/Memcache 优化 IO 响应时间;
  • 大量写合并为批量写,例如计数器先 Redis 累加再批量写 DB。

2.单线程架构

Redis 在一个同一时间点只会执行一条命令。

大多情况下,单线程是非常慢的。Redis 单线程架构为什么这么快?

  1. 主要原因:纯内存;
  2. 非阻塞 IO,Redis 使用 Event Loop 这样的模型作为 IO 多路复用的实现,并且 Redis 自身实现了一个事件处理,将 Event Loop 连接、读写、关闭转换为自身的一个事件,不再往 IO 上浪费过多时间;
  3. 避免线程切换和竞态消耗;

单线程架构要注意什么?

  1. 一次只运行一条命令;
  2. 拒绝长(慢)命令,例如 keys、flushall、flushdb、slow lua scrip、mutil/exec、operate big value(collection);

2.缓存更新策略

策略说明一致性维护成本
LRU/LFU/FIFO 算法剔除例如 maxmemory-policy最差低
超时剔除例如 expire较差低
主动更新开发控制生命周期强高

两条建议:

低一致性:推荐最大内存和淘汰策略;
高一致性:推荐超时剔除和主动更新结合,超时剔除是给主动更新做了一个兜底,还需要最大内存和淘汰策略二次兜底。

3.缓存粒度控制

从 MySQL 获取用户信息:select * from user where id = {id}

设置用户信息缓存:set user:{id} ‘select * from user where id = {id}’

缓存粒度:

  • 全部属性:set user:{id} ‘select * from user where id = {id}’
  • 部分重要属性:set user:{id} ‘select importantColumn1, …importantColumnK from user where id = {id}’

缓存粒度控制的三个角度:

通用性:全部属性更好;
占用空间:部分重要属性更好;
代码维护:表面上全部属性更好,增删字段不需要维护代码。

4.缓存穿透优化

缓存穿透问题,大量请求不命中?

发生缓存穿透的常见原因:

  • 业务代码自身问题;
  • 恶意攻击、爬虫等等。

如何发现问题?

  • 业务的响应时间;
  • 业务本身问题;
  • 相关监控指标:总调用数、缓存层命中数、存储层命中数;

缓存穿透问题解决方案:

方案一:缓存空对象。示例代码:

public String getPassThrough(String key) {
    String cacheValue = cache.get(key);
    if (StringUtils.isBlank(cacheValue)) {
        String storageValue = storage.get(key);
        cache.set(key, storageValue);
        // 如果存储数据为空, 需要设置过期时间
        if (StringUtils.isBlank(storageValue)) {
            cache.expire(key, 300); // 300秒
        }
        return storageValue;
    } else {
        return cacheValue;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

方案二:布隆过滤器拦截。通过很小的内存来实现对数据的过滤。

5.缓存雪崩优化

缓存雪崩:由于 cache 服务承载大量请求,当 cache 服务异常/脱机后,流量直接压向后端组件(例如 DB),造成级联故障。

缓存雪崩优化方案:

  • 保证缓存高可用性,例如 Redis Cluster、Redis Sentinel、VIP;
  • 依赖隔离组件为后端限流;
  • 提前演练,例如压力测试。

6.无底洞问题优化

无底洞问题:增加机器性能没能提升,反而下降。问题关键点就是批量操作的链化,例如 mget 操作,时间复杂度为 O(node),随着机器的增加,mget 批量操作的时间会越长,更多的机器不代表更多的性能。

但是随着数据增长,水平扩展是必须的。

优化 IO 的几种方法:

  • 命令本身优化,例如慢查询 keys、hgetall bigkey;
  • 减少网络通信次数;
  • 降低接入成本,例如客户端使用长连接/连接池、NIO 等 。

7.热点key优化

发现热点key:

方法一:客户端,可以使用 Guava 的 AtomicLongMap,记录 key 的调用次数:

public static final AtomicLongMap<String> ATOMIC_LONG_MAP = AtomicLongMap.create();
String get(String key) {
	counterKey(key);
	...
}
String set(String key, String value) {
	counterKey(key);
	...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

方法二:代理端

客户端和 Redis 中间加一个代理进行收集统计。

方法三:服务端

使用 monitor 解析,输出统计。

方法四:机器收集

抓取分析 Redis 所在机器的 TPC 数据。

四种方式对比:

方案优点缺点
客户端1、实现简单;1、内存泄露隐患,如果 key 量太大不建议使用;
2、维护成本高;
3、只能统计单个客户端;
代理端1、代理是客户端和服务端的桥梁,实现最方便最系统;1、增加代理端的开发部署成本;
服务端1、实现简单;1、monitor 本身的使用成本和危害,只能短时间使用;
2、只能统计单个 Redis 节点;
机器收集1、对于客户端和服务端无侵入和影响;1、需要专业的运维团队开发,并且增加了机器的部署成本;

优化方案:

  • 避免 bigkey;
  • 热键不要用 hash_tag,因为 hash_tag 会落到一个节点上;
  • 如果真有热点 key 而且业务对一致性要求不高时,可以用本地缓存 + MQ 解决。

8.热点key重建优化

问题:热点 key + 较长的重建时间。

获取缓存 -> 查询数据源 -> 重建缓存 -> 输出,这个步骤在高并发的情况下,由于查询数据源需要时间,所以会有很多请求会进入到 查询数据源 -> 重建缓存 这个过程。对数据源会造成很大压力,响应时间也会变慢。

三个优化目标:

  • 减少重建缓存的次数;
  • 数据尽可能一致;
  • 减少潜在风险。

两个优化方案:

  • 互斥锁(mutex key),查询数据源 -> 重建缓存 这个过程加互斥锁;
  • 永不过期,缓存层面不设置过期时间(没有用 expire),功能层面为每个 value 添加逻辑过期时间,但发现超过逻辑过期时间后,会使用单独的线程去构建缓存。

两个优化方案的对比:

策略优点缺点
互斥锁思路简单,保证一致性代码复杂度增加,存在死锁的风险
永不过期基本杜绝热点 key 重建问题不保证一致性,逻辑过期时间增加维护成本和内存成本

9.总结

  • 缓存收益:加速读写、降低后端存储负载;
  • 缓存成本:缓存和存储数据不一致性、代码维护成本、运维成本;
  • 推荐结合剔除、超时、主动更新三种方案共同完成;
  • 穿透问题:使用缓存空对象和布隆过滤器来解决,注意它们各自的使用场景和局限性;
  • 无底洞问题:分布式缓存中,有更多的机器不保证有更高的性能。有四种批量操作方式:串行命令、串行 IO、并行 IO、hash_tag;
  • 雪崩问题:缓存层高可用、客户端降级、提前演练是解决雪崩问题的重要方法;
  • 热点 key 重建问题:互斥锁、永不过期能够在一定程度上解决热点 key 问题,开发人员在使用时要了解它们各自的使用成本。
注:本文转载自blog.csdn.net的一叶知秋V的文章"https://blog.csdn.net/smartbetter/article/details/97953883"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top