# Redis 应用场景
缓存:热点数据 (经常查询,但不修改或删除数据) 首选 Redis 缓存,性能优秀
分布式锁:多个 tomcat 通过 Redis 获取锁后才能访问 MySQL 数据库
实现方式:Jedis,Lettuce,RedisTemplete,Redisson (更加方便,无需额外写代码)
具体实现
- 获取锁:setnx key value
- 设置锁过期时间:expire key 30
- 执行业务代码
- 释放锁: del key
Redisson 实现分布式锁
- 获取锁:redisson.getLock ("lock");lock.lock ();
- 执行业务代码
- 释放锁:lock.unlock ();
Redisson 实现的分布式锁是可重入的吗?
可重入:即可重复获取,它指的是线程 T 获取到锁 A 之后,线程 T 再次获取锁 A 还是可以获取到的,java 中的 synchronized,ReentrantLock 都是可重入锁
Token 存储
短信验证码存储 (后端生成的验证码存入 Redis, 然后与用户发送的验证码进行比较)
计数器
全局唯一 ID
排行榜 (使用 ZSet 结构)
限流
购物车
点赞关注
# 实现分布式锁需要注意哪些问题
不是原子操作 (即必须将获取锁和设置锁的过期时间变成一个整体进行操作)
没有释放锁 (没有 del 锁,导致后面的线程无法拿到锁,当然如果设置了过期时间,还可以等过期时间结束)
释放了锁,但业务还未执行完毕 (即执行业务代码耗时超过了锁的过期时间)
释放了别人的锁 (因锁的过期时间过短,导致他人拿到自己的锁,而通过释放锁的操作将他人拿到的锁释放,解决方法为在释放锁时加入判断是否是自己的锁)
大量请求竞争锁失败
解决方法
- 重试 (重试三到五次,若未拿到锁,返回获取锁失败)
- 让业务流程尽可能短
- 限流
多节点 Redis 主从复制的问题
锁的性能问题 (采用分段锁的方式,减少获取锁的排队时间)
锁的可重入性
# 采用 Redis 缓存,遇到缓存穿透,缓存击穿,缓存雪崩怎么办?
# 缓存穿透
缓存穿透是由于请求一个不存在的数据而导致的
方案一:缓存空结果,对数据库查询不存在的数据依然缓存到缓存中,比如缓存一条空值 unknow, 有效减少查询数据库的效率。优点是实现简单。缺点是缓存了无效数据,占用 Redis 缓存,可能存在缓存与数据库不一致的情况
方案二:布隆过滤器。在访问 Redis 之前,先通过布隆过滤器进行筛选。优点是不会缓存无效数据,缺点是实现比较复杂,存在一定的误判.
布隆过滤器
布隆过滤器用于检索一个元素是否在一个集合中。它采用一个很长的二进制数组,通过一些列 hash 函数来确定该数据是否存在.
具体实现如下
在向布隆过滤器中添加元素时,会使用多个哈希函数对元素进行 hash, 然后使用数组长度取余,算出一个索引位置,再把数组这几个位置都设置为 1, 这样就完成了元素的添加操作.
向布隆过滤器查询元素是存在时,和添加元素一样,. 算出数组位置。然后看数组对应位置是否都为 1, 只要有一个位置为 0, 代表存在。如果这几个位置都为 1, 代表可能存在.
目前的具体实现由 Guava,Hutool,Redisson
# 缓存击穿
高并发情况下,对于热点数据,当数据失效的一瞬间,或者刚开始时缓存中还没有对热点数据进行缓存,所有请求被发送到数据库去查询,导致数据库被压垮.
- 方案一:全局锁:在访问数据库之前都请求全局锁,获得的锁的线程才有资格去访问数据库,其他线程必须等待。由于现在的业务都是分布式的,本地锁没法控制其他服务的线程也等待,所以要用全局锁,比如分布式锁.
- 方案二:对于热点数据,设置永不过期.
- 实现方案一:不设置过期时间,即 "物理" 不过期。优点是简单。缺点是缓存的热点数据是静态的,得不到更新
- 实现方案二:逻辑不过期,通过一个异步线程,当检测到超过了过期时间,即更新缓存数据,这样只有前几条是旧数据,后面拿到的即为更新的数据.
# 缓存雪崩
在某一时刻,大量的 key 或者整个缓存的数据全部过期了或者缓存发生了故障,然后瞬间所有的请求都落到数据库,数据库被压垮
如何解决?
- Redis 高可用 (搭建 Redis Sentinel 或者 Redis Cluster 集群), 避免 Redis 不可用
- 给不同的 key 设置不同的过期时间
- 本地缓存 (二级缓存) + 限流 & 降级,避免数据库被压垮
缓存雪崩与缓存击穿的区别
缓存击穿某些热点数据 (或者说是同一条数据) 过期了,而走数据库 (即主体为被频繁访问的少量数据)
缓存雪崩强调的是大量数据 (或者说是所有数据) 都过期了,而走数据库 (即主题为大量数据)
因此,缓存雪崩核心就是,缓存无法使用,就全部走数据库 \
# Redis 内存使用完了怎么办
通过内存配置文件配置最大使用内存
当达到最大使用内存时使用内存淘汰策略
默认淘汰策略为 noeviction
# Redis 的 String 类型的值最大能放多大的数据
string:512MB
List:2^32-1 (4,294,967,295) 个元素
Set:2^32-1 (4,294,967,295) 个元素
Hash: 每个 hash 值最大能放 2^32-1 (4,294,967,295) 个 filed-value 对,Hash 类型仅受部署 Redis 的服务器上的总内存的限制
ZSet 与 Set 一样
# 如何保证数据库与 Redis 的数据一致性
即数据库发生增删改的时候,数据库的数据要和 Redis 缓存的数据保持一致性
# Redis 集群最大能部署多少个主节点
16384