目录
Redis 内存管理
缓存数据设置过期时间?
Redis 是如何判断数据是否过期的呢?
过期删除策略
内存淘汰机制
主从模式下对过期键的处理?
LRU和LFU的区别
Redis事务
定义和原理
Redis 事务的注意点?
为什么不支持回滚?
bigkey和hotkey
bigkey
如何处理 bigkey
hotkey
如何解决 hotkey?
Redis 内存管理
缓存数据设置过期时间?
因为内存是有限的,如果缓存中的所有数据都是一直保存的话,分分钟直接 Out of memory。
另外很多时候,我们的业务场景就是需要某个数据只在某一时间段内存在,比如我们的短信验证码可能只在 1 分钟内有效,用户登录的 token 可能只在 1 天内有效。
如果使用传统的数据库来处理的话,一般都是自己判断过期,这样更麻烦并且性能要差很多。
Redis 是如何判断数据是否过期的呢?
Redis 通过一个叫做过期字典(可以看作是 hash 表)来保存数据过期的时间。过期字典的键指向 Redis 数据库中的某个 key(键),过期字典的值是一个 long long 类型的整数,这个整数保存了 key 所指向的数据库键的过期时间(毫秒精度的 UNIX 时间戳)。
过期删除策略
-
惰性删除:只会在取出 key 的时候才对数据进行过期检查。这样对 CPU 最友好,但是可能会造成太多过期 key 没有被删除。
-
定期删除:每隔一段时间「随机」从数据库中取出一定数量的 key 进行检查,并删除其中的过期key。并且,Redis 底层会通过限制删除操作执行的时长和频率来减少删除操作对 CPU 时间的影响。
缺点:不太好确定删除操作执行的时长和频率。如果执行的太频繁,就会对 CPU 不友好;如果执行的太少,那又和惰性删除一样了,过期 key 占用的内存不会及时得到释放。
定期删除对内存更加友好,惰性删除对 CPU 更加友好。两者各有千秋,所以 Redis 采用的是 定期删除+惰性/懒汉式删除 。
内存淘汰机制
1、不进行数据淘汰的策略
noeviction(Redis3.0之后,默认的内存淘汰策略) :它表示当运行内存超过最大设置内存时,不淘汰任何数据,而是不再提供服务,直接返回错误。
2、进行数据淘汰的策略
在设置了过期时间的数据中进行淘汰:
-
volatile-random:随机淘汰设置了过期时间的任意键值;
-
volatile-ttl:优先淘汰更早过期的键值。
-
volatile-lru(Redis3.0 之前,默认的内存淘汰策略):淘汰所有设置了过期时间的键值中,最久未使用的键值;
-
volatile-lfu(Redis 4.0 后新增的内存淘汰策略):淘汰所有设置了过期时间的键值中,最少使用的键值;
在所有数据范围内进行淘汰:
-
allkeys-random:随机淘汰任意键值;
-
allkeys-lru:淘汰整个键值中最久未使用的键值;
-
allkeys-lfu(Redis 4.0 后新增的内存淘汰策略):淘汰整个键值中最少使用的键值。
主从模式下对过期键的处理?
当 Redis 运行在主从模式下时,从库不会进行过期扫描,从库对过期的处理是被动的。也就是即使从库中的 key 过期了,如果有客户端访问从库时,依然可以得到 key 对应的值,像未过期的键值对一样返回。
从库的过期键处理依靠主服务器控制,主库在 key 到期时,会在 AOF 文件里增加一条 del 指令,同步到所有的从库,从库通过执行这条 del 指令来删除过期的 key。
LRU和LFU的区别
Redis事务
定义和原理
Redis 中的事务是一组命令的集合,是 Redis 的最小执行单位。它可以保证一次执行多个命令,每个事务是一个单独的隔离操作,事务中的所有命令都会序列化、按顺序地执行。服务端在执行事务的过程中,不会被其他客户端发送来的命令请求打断。
它的原理是先将属于一个事务的命令发送给 Redis,然后依次执行这些命令。
Redis 事务的注意点?
需要注意的点有:
-
Redis 事务是不支持回滚的,不像 MySQL 的事务一样,要么都执行要么都不执行;
-
Redis 服务端在执行事务的过程中,不会被其他客户端发送来的命令请求打断。直到事务命令全部执行完毕才会执行其他客户端的命令。
为什么不支持回滚?
Redis 的事务不支持回滚,但是执行的命令有语法错误,Redis 会执行失败,这些问题可以从程序层面捕获并解决。但是如果出现其他问题,则依然会继续执行余下的命令。这样做的原因是因为回滚需要增加很多工作,而不支持回滚则可以保持简单、快速的特性。
bigkey和hotkey
bigkey
是指键值占用内存空间非常大的 key。例如一个字符串 a 存储了 200M 的数据。
bigkey 的主要影响有:
-
网络阻塞;获取 bigkey 时,传输的数据量比较大,会增加带宽的压力。
-
超时阻塞;因为 bigkey 占用的空间比较大,所以操作起来效率会比较低,导致出现阻塞的可能性增加
-
导致内存空间不平衡;一个 bigkey 存储数据量比较大,同一个 key 在同一个节点或服务器中存储,会造成一定影响。
如何处理 bigkey
分割 bigkey:将一个 bigkey 分割为多个小 key。这种方式需要修改业务层的代码,一般不推荐这样做。
手动清理:Redis 4.0+ 可以使用 UNLINK
命令来异步删除一个或多个指定的 key。Redis 4.0 以下可以考虑使用 SCAN
命令结合 DEL
命令来分批次删除。
采用合适的数据结构:比如使用 HyperLogLog 统计页面 UV。
开启 lazy-free(惰性删除/延迟释放) :lazy-free 特性是 Redis 4.0 开始引入的,指的是让 Redis 采用异步方式延迟释放 key 使用的内存,将该操作交给单独的子线程处理,避免阻塞主线程。
hotkey
简单来说,如果一个 key 的访问次数比较多且明显多于其他 key 的话,那这个 key 就可以看作是 hotkey。
影响:
处理 hotkey 会占用大量的 CPU 和带宽,可能会影响 Redis 实例对其他请求的正常处理。此外,如果突然访问 hotkey 的请求超出了 Redis 的处理能力,Redis 就会直接宕机。这种情况下,大量请求将落到后面的数据库上,可能会导致数据库崩溃。
因此,hotkey 很可能成为系统性能的瓶颈点,需要单独对其进行优化,以确保系统的高可用性和稳定性。
如何解决 hotkey?
读写分离:主节点处理写请求,从节点处理读请求。
使用 Redis Cluster:将热点数据分散存储在多个 Redis 节点上。
二级缓存:hotkey 采用二级缓存的方式进行处理,将 hotkey 存放一份到 JVM 本地内存中(可以用 Caffeine)。