Redis 常见数据结构以及使用场景分析
string
list
hash
存对象,用户信息(用field:value)
set
可以基于 set 轻易实现交集、并集、差集的操作。 比如:你可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。Redis 可以非常方便的实现如共同关注、共同粉丝、共同喜好等功能。
sorted set
比如在直播系统中,实时排行信息包含直播间在线用户列表,各种礼物排行榜,弹幕消息(可以理解为按消息维度的消息排行榜)等信息。
bitmap
就很多状态需要存储,但是这些状态都是 是/否 的问题。所以一个信息用一位存储就好了。那么就可以用如8位存储8个状态
Redis 单线程模型详解
关于reactor设计模式
餐厅只用一个服务员,当客户多,点单慢的时候,服务员可以终于发现了一个新的方法,那就是:当客
人点菜的时候,服务员就可以去招呼其他客人了,等客人点好了菜,直接招呼一声“服
务员”,马上就有个服务员过去服务。
redis给缓存数据设置过期时间的两个作用
1.防止内存溢出
2.完成token的过期策略(用传统数据库来判断相当麻烦)
过期数据的删除策略
1.惰性删除
优点:对CPU友好
缺点:万一我再也不get这个数据,就永远不会删除,造成内存泄漏
2.定期删除
Redis 底层会通过限制删除操作执行的时长和频率来减少删除操作对 CPU 时间的影响。
redis的做法:1+2
还是有缺陷。
因为还是可能存在定期删除和惰性删除漏掉了很多过期 key 的情况。
这样就导致大量过期 key 堆积在内存里,然后就 Out of memory 了。
Redis 内存淘汰机制了解么?
相关问题:MySQL 里有 2000w 数据,Redis 中只存 20w 的数据,如何保证 Redis 中的数据都是热点数据?
分成两种,一种是面向设置了过期时间的数据进行淘汰。一种是面向键空间的所有数据
Redis 持久化机制### (怎么保证 Redis 挂掉之后再重启数据可以进行恢复)
1.快照
策略:不能定时无脑创建快照,在一段时间内,内存变化量必须达到一个标准才值得去创建快照。
save 900 1 #在900秒(15分钟)之后,如果至少有1个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
save 300 10 #在300秒(5分钟)之后,如果至少有10个key发生变化,Redis就会自动触发BGSAVE命令创建快照。
save 60 10000 #在60秒(1分钟)之后,如果至少有10000个key发生变化,Redis就会自动触发BGSAVE命令创建快照
2.AOF持久化
AOF 持久化的实时性更好,因此已成为主流的持久化方案。默认情况下 Redis 没有开启 AOF(append only file)方式的持久化,可以通过 appendonly 参数开启 :appendonly yes
Redis Bigkey
定义: 如果一个 key 对应的 value 所占用的内存比较大,那这个 key 就可以看作是 bigkey。具体多大才算大呢?有一个不是特别精确的参考标准:string 类型的 value 超过 10 kb,复合类型的 value 包含的元素超过 5000 个(对于复合类型的 value 来说,不一定包含的元素越多,占用的内存就越多)。
Bigkey的危害
1、使用 Redis 自带的 --bigkeys 参数来查找。
2、分析 RDB 文件
通过分析 RDB 文件来找出 big key。这种方案的前提是你的 Redis 采用的是 RDB 持久化。
Redis 事务
通过键入 MULTI 命令,表示MULTI后的命令集合暂不执行,放到队列里,一直碰到EXEC命令的时候,将这之间的命令作为一个事务一起执行。
配套使用的命令: DISCARD:取消一个事务,它会清空事务队列中保存的所有命令。 WATCH: 该命令用于监听指定的键,当调用
EXEC命令执行事务时,如果一个被WATCH命令监视的键被修改的话,整个事务都不会执行,直接返回失败。
Redis 是不支持 roll back 的,因而不满足原子性的(而且不满足持久性)。
Redis 可以做消息队列吗
可以,但是效果不好。还是有很多欠缺的地方比如消息丢失和堆积问题不好解决。
缓存穿透
缓存穿透说简单点就是大量请求的 key 根本不存在于缓存中,导致请求直接到了数据库上,根本没有经过缓存这一层。举个例子:某个黑客故意制造我们缓存中不存在的 key 发起大量请求,导致大量请求落到数据库。
解决方案
- (最基本)参数校验
错误的入参肯定不能在redis里面命中,会导致这些无效请求落到数据库,也会造成数据库的压力。所以要尽量在到达业务层之前或者在业务层做好参数校验,拒绝非法请求。
- 缓存无效key
无效的入参到达服务器后,也给打入redis,如果后续这个攻击的key没有频繁变化的话,则后续的请求可以被redis命中。
所以缓存无效key不能从根本上解决问题。因为无法解决key频繁变化的情景,会导致无效key大量堆积。所以如果非要使用它,请使用一个较短的过期时间
用Java代码展示:
public Object getObjectInclNullById(Integer id) {
// 从缓存中获取数据
Object cacheValue = cache.get(id);
// 缓存为空
if (cacheValue == null) {
// 从数据库中获取
Object storageValue = storage.get(key);
// 缓存空对象
cache.set(key, storageValue);
// 如果存储数据为空,需要设置一个过期时间(300秒)
if (storageValue == null) {
// 必须设置过期时间,否则有被攻击的风险
cache.expire(key, 60 * 5);
}
return storageValue;
}
return cacheValue;
}
3.布隆过滤器
将所有可能的请求存储到布隆过滤器中。
但布隆过滤器可能会存在误判的情况。总结来说就是: 布隆过滤器说某个元素存在,小概率会误判。布隆过滤器说某个元素不在,那么这个元素一定不在。
布隆过滤器的原理后续单独补充
缓存雪崩
缓存雪崩描述的就是这样一个简单的场景:缓存在同一时间大面积的失效,后面的请求都直接落到了数据库上,造成数据库短时间内承受大量请求。 这就好比雪崩一样,摧枯拉朽之势,数据库的压力可想而知,可能直接就被这么多请求弄宕机了。
针对 Redis 服务不可用的情况:
- 采用 Redis 集群,避免单机出现问题整个缓存服务都没办法使用。
- 限流,避免同时处理大量的请求。
针对热点缓存失效的情况:
- 设置不同的失效时间比如随机设置缓存的失效时间。
- 缓存永不失效。