为什么会用到缓存?
- 不同设备的速度不同,磁盘,内存,网络
在什么地方用缓存?
- 用户不会修改的:状态的数字与名字的对应,前端缓存
- 地址码->国家,省市区的名字,nginx缓存,key:api+参数,value:返回值
- 应用服务器的本地缓存
- 外部分布式缓存
怎样实现缓存?
MyBatis缓存
提供一级缓存和二级缓存
key: sql语句+参数->hash值
value: 结果
一级缓存
一个事务里面的缓存
Repeatable Read没问题,RC会有问题
多个前端发相同的请求,可以考虑二级缓存
二级缓存
查过的数据放在二级缓存中,第二次先去二级缓存找
只有一台服务器的话没什么问题
服务器有多台会有问题,高并发尽量别用
缓存发霉
解决:记录缓存对应的表,如果之后有对这个表进行update,delete,insert就把缓存删掉
问题:用mybatis只能用RR隔离级别,因为一级缓存默认开启
Redis缓存
出发点:
- “高性能”数据库,内存
- 只支持key,value查询
大多数场合用主键查
可以持久化
崩溃一般是因为高并发
必须要提供主从功能,保证数据同步,内存中保存同步相比内存与磁盘同步更容易
I/O多路复用
单线程处理完成I/O的请求
理论上读11万次/s,写8万次/s
基本上每个请求几毫秒,MySQL在高负载下基本上要几百毫秒
又因为完成请求是单线程,所以是安全的,具有原子性
Redis数据结构
字符串
二进制(可以存数字,字符串,对象)
eg
SET key value [ex seconds] [px milliseconds] [nx|xx]
redisTemplate.opForValue().set(key, value, timeout, TimeUnit.SECONDS) // Java
hash
HSET key field value
redisTemplate.opsForHash().put(key, field, value)
HSET key field
redisTemplate.opsForHash().get(key, field)
List
列表
set
集合
交集并集差集
BitMap
位图
设某一位的值(0/1)
拿到某一位的值
看从第几位开始到第几位结束共几个1
内置Lua解释器
Lua脚本
Redis的应用场景
尽量用单个的命令而不是脚本
缓存频繁读的数据
加时效
久而久之留下的都是热点数据
缓存发霉问题
修改,删除数据
如果修改,也是去删掉缓存
如果先改缓存,那数据库更新前就不一致
所以要删掉缓存
删的弊端:如果在Dao层删,然后返回Service又做了一些事,那么在当前时间到事务提交之前的这一段可能会读到别人更新的缓存,所以一般在事务的最后再删,让影响最小。
用户角色权限的计算(Set)
每个角色的权限拿出来存到Redis,之后每次需要直接去Redis计算并集拿结果。
库存高并发的扣减
MySQL无法解决这个问题,因为写的时候要加锁
用Redis4结合带事务的消息队列,把库存量依次写回
布隆过滤器
判断一个元素是否在集合里
默认的做法比较hashcode,所以每个集合中的元素都要存一个hashcode,元素数量越多越占空间
布隆过滤器希望做到的是固定大小的存储空间,用一个定长的数,比如512k的bit
用多个哈希函数计算key的hashcode取模将对应位置为1,后面判断的时候依然用这些哈希函数对待判断的key分别计算取模,看对应的所有位是否全都为1,如果有一位不是1证明待判断的key不在集合中。
问题:可能所有位都置为1,那布隆过滤器就没用了
场景:开放不需要登陆的api,比如给用户展示商品信息(所有人都能调用api)黑客频繁去查不存在的id,绕过Redis访问数据库
方案:
- 查一次数据库后在Redis中设一个空值,避免同一个不存在id的频繁访问(但是费内存
- 布隆过滤器,记没有的商品的id(内存开销固定,但有误报率
Redis内存管理
清理到期的数据
惰性删除
到期了也不主动删,再次访问的时候才删,对Redis压力最小,但如果一直不访问就占内存
因为是内存数据库,所以很可能撑不到一天数据就占满了,不能像数据库一样晚上低峰期的时候去清理,而是要定期清理。
定期删除
有一定性能损耗,不能太高频,不能全部扫描,太耗时,尽量让每次的负担是固定的,比如随机选取一个固定的值,看这个数量的数据里面哪些是过期的。
有一种可能就是有些过期数据永远访问不到,或者多数数据不设置过期时间,内存还是可能满
只移除有有效期的数据
volatile-lru
volatile-random
volatile-ttl
移除所有
allkeys-lru:默认
allkeys-random
noeviction