Redis
Redis 介绍
Redis 全称 Remote Dictionary Server,即远程字典服务器。是一个基于内存的键值型NoSQL数据库,支持多种数据结构,如字符串、哈希、列表、集合、有序集合等。Redis 是一个高性能的 key-value 存储系统,常用于缓存、消息队列、排行榜等场景。
Redis 特点
- 键值存储:Redis 是一个键值存储系统,支持多种数据结构,如字符串、哈希、列表、集合、有序集合等。
- 单线程:Redis 是单线程的,通过非阻塞 I/O 和事件驱动机制来实现高并发,每个命令具备原子性。
- 低延迟:Redis 通过将数据存储在内存中来实现低延迟。
- 持久化:Redis 支持 RDB 和 AOF 两种持久化方式,可以将内存中的数据持久化到磁盘中。
- 支持集群:Redis 支持主从复制、哨兵和集群等功能。
- 支持多语言:Redis 支持多种语言的客户端,如 Java、Python、Node.js 等。
Redis 安装
官网下载地址:https://redis.io/download
Redis 命令
1 | redis-server redis.conf |
Redis 缓存
缓存作用模型
缓存更新策略
主动更新策略
- 由缓存的调用者,在更新数据库的同时,更新缓存。
- 将缓存和数据库整合为一个服务,由服务来维护统一性。
- 调用者只操作缓存,由其他线程异步的将缓存数据持久化到数据库。
一般采用第一种方案,即在更新数据库的同时,更新缓存。
方案二会导致缓存和数据库的耦合度过高,不利于系统的扩展和维护。
方案三会可能导致缓存数据和数据库数据不一致,不利于数据的一致性。
缓存更新策略的最佳实践方案
- 低一致性需求:使用Redis自带的内存淘汰机制
- 高一致性需求:主动更新,并以超时剔除作为兜底策略
- 读操作
- 缓存命中则直接返回
- 缓存未命中则查询数据库,并写入缓存,设定超时时间
- 写操作
- 先写数据库,再删除缓存
- 要确保数据库与缓存操作的原子性
ps. 缓存为快速操作,可能会在数据库写入之前读取到脏数据,造成线程安全问题;之所以不选择更新缓存,因为更新操作相比删除操作,会带来更多无用操作。
缓存穿透
缓存穿透是指客户端请求的数据在缓存和数据库中都不存在,这样缓存永远不会命中,导致请求直接访问数据库,增加数据库的压力。
解决方案
- 缓存空对象
- 实现简单, 维护成本低
- 额外的内存开销(可以设置较短的过期时间)
- 可能造成短期的不一致(当有新的数据插入时,造成数据不一致)
- 布隆过滤器
- 一个很长的二进制向量和一系列随机映射函数
- 内存占用少,没有多余key
- 有一定的误判率(不存在是真的不存在,存在是可能存在)
- 增强id复杂度
- 对id进行加密,增加复杂度
- 一定程度上可以防止穿透
- 限流
- 限制请求频率,防止攻击
一般在开发中,会使用第一种方案,即缓存空对象。
流程图变化:
缓存雪崩
缓存雪崩是指缓存中的大量数据同时过期或者Redis服务宕机,导致请求直接访问数据库,增加数据库的压力。
解决方案
- 缓存数据过期时间随机
- 给不同的key的TTL设置随机值,避免大量key同时过期
多级缓存
Redis集群
- 通过主从复制和哨兵机制,保证Redis的高可用性
- 限流
缓存击穿
缓存击穿问题也叫热点Key问题,是指一个被高并发访问且缓存重建业务较复杂的Key突然失效,导致大量请求瞬间访问数据库,增加数据库的压力。
解决方案
- 互斥锁
- 在缓存失效的时候,使用互斥锁,只允许一个线程访问数据库,其他线程等待
- 适用于缓存失效后,数据重建时间较短的情况
- 逻辑过期
- 设置一个逻辑上不会过期的缓存,当逻辑过期时,重新设置缓存
- 某个线程设置缓存时,其他线程直接得到缓存数据,不考虑缓存是否过期
使用互斥锁来解决:
代码实现:
1 |
|
使用逻辑过期来解决:
1 | public Shop queryWithLogicalExpire(Long id) { |
封装缓存工具类
代码如下:
主要通过泛型和函数式编程来实现缓存工具的封装。
1 |
|