五个基本数据类型
String(字符串)
- 使用最广最频繁
- 常用存储以下数据
-
- 常规数据
- 计数(用户单位时间请求数-简单限流、页面单位时间访问数)
- 分布式锁(一般是用Lua脚本)
Hash(散列)
- 存储一个对象,可以直接操作字段,但是一般被String存储对象Json所代替
List(列表)
- 类比于Java,可以看作是一个可以头操作,尾操作的List<String>
Set(集合)
- 存放不重复数据
- 获取多个数据集的交集、并集、差集(共同好友、好友推荐...)
- 随机获取数据源中的一个数据(抽奖系统、随机点名)
Z-Set(有序集合)
- 排行榜
- 滑动窗口限流
三个特殊数据类型
HyperLogLog(基数统计)
- 统计页面UV
Bitmap(位图)
- Bitmap 存储的是连续的二进制数字(0 和 1)
- 统计活跃用户数(key做日期,用户ID作为offset)
Geospatial(地理位置)
- 不知道
...(布隆过滤器、位域等)
持久化
AOF
- 记录操作
RDB
- 默认支持
- 记录数据
如何判断数据是否过期?
- 通过过期字典(类似于Hash结构)保存了数据过期的时间。
- 过期字典中的键指向数据库中的Key
- 值是一个longlong类型整数,记录的是键的过期时间(毫秒精度)
过期数据的删除策略
- 惰性删除:取key的时候判断是否过期
- 定期删除:每隔一段时间抽取一批Key执行删除过期Key的操作。底层会该操作的执行时长和频率来减少对CPU的影响
Redis采用的是定期删除+惰性删除的组合方式
Redis内存淘汰机制
- volatile-lru:从已设置的过期时间的数据集中挑选最近最少使用的数据淘汰。
- volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰。
- volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰。
- allkeys-lru(least recently used):当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key(这个是最常用的)。
- allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰。
- no-eviction:禁止驱逐数据,也就是说当内存不足以容纳新写入数据时,新写入操作会报错。这个应该没人使用吧!
布隆过滤器
当Key不存在于布隆过滤器中时,在缓存中一定不存在
当Key存在于布隆过滤器中时,在缓存中不一定存在
击穿、穿透、雪崩
缓存穿透
- 大量请求的Key不合理
- 根本不存在于缓存中,也不存在于数据库中
解决方案:
- 缓存无效的Key
- 布隆过滤器
- 接口限流
缓存击穿
- 热点Key过期,导致大量请求直接打到了数据库
解决方案:
- 热Key不过期或长过期时间
- 预热,在热门活动场景内设置不过期
- 请求数据库写入缓存前,获取互斥锁,保证只有一个请求落到数据库
缓存雪崩
- 缓存在同一时间大面积失效,导致大量请求打到数据库
解决方案:
- Redis集群
- 缓存预热
- 固定过期时间+随机值
如何保证缓存和数据库数据一致性?
- 更新数据库+更新缓存(淘汰)
- 更新数据库+删除缓存
- 在方案2中,并发状态下依然有数据不一致的可能性,解决方案是【延迟双删】,通过重试或者消息队列来实现