自定义Redis缓存实现
自定义缓存实现类
自定义MyBatis二级缓存
自定缓存类实现Cache接口
导入redis操作相关的工具(jar,JedisUtil,jedis.properties)
必须具备如下功能:
根据namepace划分缓存空间(id)
MyBatis二级缓存本质是一个Map结构
key :和执行的sql先关
value:查询结果相关
存放数据的功能: select语句(key)----查询结果(value)
获得数据的功能: 根据key
清空缓存的功能: clear
使用自定义的缓存
Session共享
为何要实现session共享?
nginx负载均衡,希望兼顾权重的按照硬件性能分配访问压力的优势,又想保证多个tomcat使用同一个session应该怎么解决?
- ip_hash
- session复制
- Redis
解决方案
使用redis管理负载均衡中多个tomcat的session.(Redis共享session)
如何实现Redis管理Session
配置步骤
- tomcat使用redis管理session的jar
将jar拷贝tomcat中lib
- 配置tomcat的session管理方式为RedisSessionManager
1. tomcat 配置文件context.xml <!--注册session管理工具--> <Manager className="session管理工具全类名" host="redis的ip地址" port="端口" maxInactiveInterval="session存活时间 秒 1800" 秒 /> <!--将session管理工具使用在tomcat操作过程中--> <Valve className="RedisSessionHandlerValve在tomcat中使用session管理工具"/>
重启两个tomcat
缓存问题
- 缓存穿透
- 缓存雪崩
User 根据主键查询 key 1 2 3 4 5 6 7 8 9 1W -id 缓存中都没有 2000个查询 每秒 可以认为是安全的
缓存穿透
缓存击穿 大量不存在的key攻击
只需要极少的空间就可以判断一个元素是不是在一个集合之内,这正好是我们所需要的场景啊:判断key是否存在
解决方案
- 空值缓存 (非恶意攻击)
key value -1 null
布隆过滤器
可以判断key是否在数据库中存在
缺点:可能会判断出错 概率不高 但是会
缓存雪崩
微博
key = 鹿晗微博1 value = 相关信息 评论 追评 点赞 等 key = 鹿晗微博2 value = 相关信息 评论 追评 点赞 等 key = 鹿晗微博3 value = 相关信息 评论 追评 点赞 等 key = 鹿晗微博4 value = 相关信息 评论 追评 点赞 等 key = 鹿晗微博5 value = 相关信息 评论 追评 点赞 等 key = 吃瓜群众1 key = 吃瓜群众2 key = 吃瓜群众3 key = 吃瓜群众4 key = 吃瓜群众5 上千万key 这些key一定会设置失效 失效时间设置的不合理 同一时间大量key过期了(500W) 如果发生在平时 无所谓 不巧的是 上热搜了 突然间 大量的用户来访问 相关的信息
大量key同一时间失效 将导致不存在的数据每次请求都要到存储层去查询,失去了缓存保护后端存储的意义
大量流量 且数据失效 导致不存在的数据每次请求都要到存储层去查询 一模一样的SQL 数据库崩溃
- 合理的设置过期时间 单体架构
- 分布式锁
- 多级缓存
分布式的锁,谁获得了这把锁,谁就可以访问数据库
大型项目中
如果说用户查不到数据 降级服务
- 先等着 两三秒
- 能直接 返回固定数据
- 等等
7、Redis 分布式锁
什么是分布式锁
一种逻辑处理
Redis 分布式锁
分布式锁本质上要实现的目标就是在 Redis 里面占一个“茅坑”,当别的进程也要来占时,发现已经有人蹲在那里了,就只好放弃或者稍后再试。
占坑一般是使用 setnx(set if not exists) 指令,只允许被一个客户端占坑。先来先占, 用完了,再调用 del 指令释放茅坑。
// 这里的冒号:就是一个普通的字符,没特别含义,它可以是任意其它字符,不要误解 > setnx lock:codehole true OK ... do something critical ... > del lock:codehole (integer) 1
但是有个问题,如果逻辑执行到中间出现异常了,可能会导致 del 指令没有被调用,这样就会陷入死锁,锁永远得不到释放。
于是我们在拿到锁之后,再给锁加上一个过期时间,比如 5s,这样即使中间出现异常也可以保证 5 秒之后锁会自动释放。
> setnx lock:codehole true OK > expire lock:codehole 5 ... do something critical ... > del lock:codehole (integer) 1
但是以上逻辑还有问题。如果在 setnx 和 expire 之间服务器进程突然挂掉了,可能是因为机器掉电或者是被人为杀掉的,就会导致 expire 得不到执行,也会造成死锁。
这种问题的根源就在于 setnx 和 expire 是两条指令而不是原子指令。如果这两条指令可以一起执行就不会出现问题。也许你会想到用 Redis 事务来解决。但是这里不行,因为 expire 是依赖于 setnx 的执行结果的,如果 setnx 没抢到锁,expire 是不应该执行的。事务里没有 if-else 分支逻辑,事务的特点是一口气执行,要么全部执行要么一个都不执行。
为了解决这个疑难,Redis 开源社区涌现了一堆分布式锁的 library,专门用来解决这个问题。实现方法极为复杂,小白用户一般要费很大的精力才可以搞懂。如果你需要使用分布式锁,意味着你不能仅仅使用 Jedis 或者 redis-py 就行了,还得引入分布式锁的 library。
为了治理这个乱象,Redis 2.8 版本中作者加入了 set 指令的扩展参数,使得 setnx 和 expire 指令可以一起执行,彻底解决了分布式锁的乱象。从此以后所有的第三方分布式锁 library 可以休息了。
> set lock:codehole true ex 5 nx OK ... do something critical ... > del lock:codehole
上面这个指令就是 setnx 和 expire 组合在一起的原子指令,它就是分布式锁的奥义所在。