02.Redis基础
# 01.Redis简介
# 1、什么是Redis?
Redis本质上是一个Key-Value类型的内存数据库,很像memcached
整个数据库统统加载在内存当中进行操作,定期通过异步操作把数据库数据flush到硬盘上进行保存
因为是纯内存操作,Redis的性能非常出色,每秒可以处理超过 10万次读写操作,是已知性能最快的Key-Value DB
# 2、Redis特点
Redis比memcached优点
- 数据类型:Redis支持五中数据类型, memcached所有的值均是简单的字符串
- value大小限制:Redis单个value的最大限制是512M,memcached只能保存1MB的数据
- 持久化:Redis可以持久化,memcached不支持持久化
Redis为什么快?
- 纯内存操作
- 单线程避免上下文切换
- 非阻塞IO多路复用机制
- 高效的数据结构,这些结构经过优化,能够快速完成各种操作
Redis缺点
- 内存限制,不能用作海量数据存储
- 注:如果需要存储海量数据,可以使用MongoDB
# 02.Redis内存碎片
# 1、内存碎片化的成因
- Redis 中的内存碎片化指的是由于频繁的内存分配和释放操作
- 导致内存被分割成无法连续使用的小块,从而影响 Redis 的内存利用率和性能碎片化的内存块虽小且无法被利用
对象的频繁创建和销毁: 频繁的插入和删除操作会导致小块内存频繁被分配和释放
变长数据结构的使用:
- Redis 中一些数据结构如 list、set、hash、zset 是变长结构,随着数据的增删,内存分配会动态扩展或收缩
- 如果这些数据结构频繁扩展和收缩,可能导致无法连续使用的内存块残留,形成碎片
大对象的删除或修改:
- Redis 使用内存管理库(如 jemalloc)对内存块进行分配
- 假如删除或修改了大对象,释放的内存块可能会被分割成多个小块,这些小块无法被有效利用
# 2、内存碎片化影响
- 内存浪费:
- 虽然 Redis 的进程看似占用大量内存,但因为碎片化,Redis 实际可以利用的内存可能较少
- 随着碎片化的加剧,系统会出现内存不足的假象,而 Redis 进程占用的内存实际没有减少
- 性能下降:
- 碎片化的内存难以高效利用,可能导致 Redis 的性能下降
- 例如,在进行大数据插入时,Redis 可能频繁进行内存分配和整理,增加 CPU 的负载
- 可能导致 OOM(Out Of Memory):
- 当系统报告有足够内存可用,但实际因为碎片化无法分配足够的连续内存时
- 可能导致 Redis 进程无法分配所需的内存,从而导致 OOM
# 3、检测 内存碎片化
- Redis 提供了
INFO memory
命令来查看内存使用情况 - 其中
mem_fragmentation_ratio
参数可以反映 Redis 的内存碎片化情况
127.0.0.1:6379> INFO memory
# Memory
used_memory:1024000 # Redis 实际使用的内存
used_memory_human:1000K
used_memory_rss:2048000 # Redis 进程从操作系统申请的物理内存
used_memory_rss_human:2M
mem_fragmentation_ratio:2.00 # 内存碎片率
2
3
4
5
6
7
- mem_fragmentation_ratio:
- 内存碎片率,表示
used_memory_rss / used_memory
- 如果该值大于 1,则表明有内存碎片,理想状态下,这个值应接近 1
- 当
mem_fragmentation_ratio > 1.5
时,说明 Redis 出现较为严重的内存碎片化
- 内存碎片率,表示
# 4、Redis 内存碎片化的优化方法
使用 Jemalloc:
Redis 默认使用
jemalloc
作为内存分配器,它比系统默认的malloc
更高效,尤其在管理小对象和避免内存碎片方面有显著优势jemalloc
的内存池管理机制可以减少碎片化的产生,推荐在生产环境中使用检查 Redis 是否在使用
jemalloc
:Redis-server --version
定期进行内存整理:
- 如果发现 Redis 内存碎片化比较严重,可以尝试通过重启 Redis 进程来整理内存
- 不过,重启 Redis 会中断服务,因此不适合在高并发的生产环境下使用
- 另一种方式是将数据持久化后,采用以下步骤:
- 执行
BGSAVE
将数据保存到磁盘 - 使用
SHUTDOWN SAVE
命令关闭 Redis 实例 - 再次启动 Redis 实例,Redis 会从持久化的文件中加载数据,此时会重新分配内存,有效减少内存碎片
- 执行
合理配置 Redis 的 maxmemory 策略:
Redis 提供了内存淘汰策略,通过合理配置
maxmemory
参数和淘汰策略volatile-lru
:只对设置了过期时间的 key 进行 LRU 淘汰allkeys-lru
:对所有 key 进行 LRU 淘汰volatile-random
:随机淘汰设置了过期时间的 keyvolatile-ttl
:根据 key 的剩余 TTL 值淘汰
减少对象的频繁创建和销毁:
- 避免在 Redis 中频繁创建和销毁对象,可以通过优化业务逻辑来减少这种操作
- 比如减少不必要的
SET
、DEL
操作,或者批量处理数据而不是逐条处理
使用适当的数据结构:
- 根据数据的实际用途,选择适合的数据结构
- 比如,尽量避免使用过长的 list 或 hash,因为这些变长数据结构在增长和收缩时容易导致内存碎片
- 可以尝试将较大的对象分割成小块进行存储,避免内存块的不连续分配
升级 Redis 版本:
- Redis 的不同版本在内存管理方面的表现有所不同,新版本往往包含更好的内存管理机制和优化
- 如果你使用的 Redis 版本较老,可以考虑升级到较新版本以获得更好的内存管理效果
# 03.Redis 阻塞
1)O(n)复杂度命令
Redis 中的大部分命令都是 O(1)时间复杂度,但也有少部分 O(n) 时间复杂度的命令
KEYS *
:会返回所有符合规则的 keyHGETALL
:会返回一个 Hash 中所有的键值对LRANGE
:会返回 List 中指定范围内的元素SMEMBERS
:返回 Set 中的所有元素SINTER
/SUNION
/SDIFF
:计算多个 Set 的交集/并集/差集
2)SAVE 创建 RDB 快照
save
: 同步保存操作,会阻塞 Redis 主线程;bgsave
: fork 出一个子进程,子进程执行,不会阻塞 Redis 主线程,默认选项默认情况下,Redis 默认配置会使用
bgsave
命令如果手动使用
save
命令生成 RDB 快照文件的话,就会阻塞主线程
3)AOF 日志记录阻塞Redis
- AOF 持久化机制是在执行完命令之后再记录日志
4)大 Key
如果一个 key 对应的 value 所占用的内存比较大,那这个 key 就可以看作是 bigkey
具体多大才算大呢?有一个不是特别精确的参考标准
string 类型的 value 超过 1MB
复合类型(列表、哈希、集合、有序集合等)的 value 包含的元素超过 5000 个
大 key 造成的阻塞问题如下
客户端超时阻塞
引发网络阻塞:如果一个 key 的大小是 1 MB,每秒访问量为 1000,那么每秒会产生 1000MB 的流量
阻塞工作线程:如果使用 del 删除大 key 时,会阻塞工作线程
# 04.Redis大key
- 在 Redis 中,大 key(即数据量很大的键)会对系统性能和稳定性造成一定影响
- 大 key 可能是一个包含大量元素的复杂数据结构(如 list、hash、set、zset),或者是一个非常长的字符串
- 如果不加控制,大 key 会导致一些操作(如删除、迁移、备份)耗时过长,甚至阻塞 Redis 线程,影响系统整体性能
# 1、大 key 问题的影响
- 阻塞 Redis 线程:
- Redis 是单线程处理请求的,因此如果一个大 key 的操作(如删除、迁移等)需要很长时间,会阻塞其他客户端的请求,导致延迟变大
- 网络传输耗时:
- 如果大 key 占用大量的网络带宽,传输大 key 时会导致 Redis 客户端和服务器之间的传输延迟
- 内存使用问题:
- 大 key 占用的内存较多,容易导致 Redis 的内存消耗过高,并可能引发 Redis 的内存淘汰机制,导致不必要的 key 被删除
- 持久化和恢复:
- Redis 持久化机制如 RDB 和 AOF 可能需要大量时间来保存或恢复大 key,影响 Redis 的持久化效率
# 2、大 key 的分片
对大 key 进行分片存储是避免创建大 key 的有效方法
可以通过一定的规则将数据拆分为多个小 key 存储
时间分片:
- 根据时间戳将数据分散到不同的 key 上
- 例如,将每天的用户日志记录存储在
user_logs:20231008
、user_logs:20231009
这样的 key 上
哈希分片:
对 key 的一部分进行哈希运算,存储到多个 Redis key 上
例如,将用户数据根据用户 ID 进行分片
user:1234 -> shard1 user:5678 -> shard2
1
2通过哈希分片可以将大数据量分散存储到多个 key 中,避免形成单个大 key
# 3、持久化大 key 的优化
Redis 的持久化操作会扫描和保存所有的 key,如果存在大 key,持久化过程会变慢可以采取以下优化措施:
- 持久化策略调整:
- 对于需要持久化的大 key,可以适当延长持久化周期
- 或者只对重要的小 key 进行持久化,将不重要的大 key 设置为不持久化(通过调整
RDB
和AOF
策略) - 使用
SAVE
或者BGSAVE
配合EXCLUDE
将某些不需要持久化的 key 排除在外
- AOF 文件压缩:
- AOF 方式可以在重写时将大 key 分批重写,避免一次性重写全部内容
- Redis 6.0 及以后版本的 AOF 重写机制已经优化过这类问题
# 4、监控大 key
- 定期运行 Redis 的
bigkeys
脚本,获取实例中的大 key - 设置合理的告警阈值,当某个 key 的大小超过阈值时,触发告警,及时处理