一般来说session存储的数据只对当前服务器可见(tomcat可见,虽然服务器可以与服务器之间共享数据,但有一个延迟的时间),当请求被其他服务器接受,将无法判断是否登录(session不共享)。
注意:redis存储对象,最好用hash结构。原因 hash结构方便修改,不像json那样要整个更改。
使用StringRedisTemplate时注意 如果 某个键对应的值为 map 那么map中的所有数据都必须是String类型
该操作把UserDto的数据 转换为键值对 用map存储起来,并且 让所有键的值都为 String
熟记 BeanUtil 是一个 对象可以与对象转换,以及把 map 转换成 bean 或者 bean转换成 map
生成 随机数 6个数
项目中如果有多个拦截器,要控制拦截器的先后顺序,可以按添加拦截器的先后顺序,或者调用order方法
如图
order()方法中值越小,就越先执行
如果拦截器不指定拦截路径,就默认拦截全部路径
数据交换缓冲区,提高读写性能,降低数据库的负载。
如果用String存储对象,那么就会是json格式。从json格式中取出一个java对象
指定json 和要转换的 类的字节码,也可以把类转换成JSON
因为 用stringRedisTemplate存储数据,并且还用 list数据结构,那么就会把从数据库中查询的list中的数据转换成 String(json)
做业务时,一定要注意返回给前端的数据类型
如果 对数据库修改,并且会经常去查询,那么对数据库修改的操作和更新缓存发操作必须是原子性
对缓存操作时,最好是先操作数据库,再删除缓存(并且这两个操作必须是原子性)
redis 和数据库都没有 数据, 所以每次请求 都会打在数据库上
解决方案 1 缓存空对象(把存活时间设置短) 2 布隆过滤器(判断mysql数据库中是否有数据)
3 可以把查询的参数设置复杂点 4 可以做用户权限校验
布隆过滤器如图
布隆过滤器可能存在误判,如果判断出数据不存在那么数据一定不存在。
在同一时刻,redis中 大量的key同时失效 或者 redis直接宕机,就会导致数据库压力飙升,从而可能导致数据库宕机。
解决方案
1 设置不同的过期时间 ttl 2 搭建redis集群
一个高并发 的key失效 ,就会导致大量业务去访问数据库,就会导致数据库压力飙升,注意和缓存雪崩有区别,这里注重一个Key。
解决方案 1 互斥锁 2 逻辑过期(永不过期)
两者的区别
在防止缓存穿透时,设置空值缓存时 如下图
获取值做判断时 注意下图 注释
解释如图
这里存储的为空白 ,并不是""
localdateTime表示当前日期时间
如果是逻辑过期时间处理,前提是缓存中现有数据(该案例就是这么讲的)
这里 RedisData封装 了 类Shop,那么注意上图的反序列化操作后得到的redisData中的Shop类是JSONObject类型
所以只能用这种方式数据转换。
在用逻辑过期时间处理缓存击穿时,在重建缓存阶段会开启新的线程重建,那么就要保证重建的这个线程结束后才能释放锁
如图(注意注释)
注意上图,这里dbFallback的apply方法的参数是 Function
保证一个信息的安全性
该注解含义 表明表中没有该字段,不会映射。
默认当前时间,ON UPDATE 表示自动更新,后续指 更新为当前时间。
直接自定义sql,注意更新操作也会保证数据库加锁,同一时刻下就只有一个线程会修改该数据。
上诉操作只能在单台tomcat服务器下,同时也对应一台jvm,下才可满足,如果在多台服务器下,那么也是对应多台jvm下,那么就不能满足
锁对象实际用的字符串常量池的对象引用
实质用的锁对象是 字符串常量池中的userId对象引用,因为调用了intern() 所以单台服务器下每一次调用用的锁对象是同一个, 而上图的 str3 != str1,所以synchronized只能保证单个服务器下的互斥,不能保证集群下的互斥。
关闭nginx
注意在锁过期后,误删的原子性
即便加上没有过期时才能删除的条件依然不能保证原子性
原项目的解决误删的方案
通过uuid加上线程id做为分布式锁的值,因为线程的id 会在单台jvm中呈递增趋势
那么多台jvm,就有可能会使线程id 重复,那么也会造成误删,所以加上uuid可以保证
服务器会根据自己生成的uuid加上线程号删除自己对应的那把锁
自己画的图如下
原方案释放锁
对于 setnx分布式锁 缺点 1 不可重入 2不会重试 3主从同步时 , 主节点挂掉,从节点转换成主节点时,在这一阶段会出现锁被多个线程获取(概率极低)
通过hash结构,对应hash的值就是重入锁的重入次数,同样在服务器集群下,filed只需要线程id 来设置,只有value次数减为0时才会释放锁。不会向setnx那样要区分不同的服务器释放锁还需要用uuid标识不同的服务器
利用发布订阅来解决重试获取锁
利用看门狗机制,在每隔一段时间就重置过期时间
在实现秒杀抢劵时,会先查询劵的库存和 订单表是否一人一单,那么在高并发下,对于这种的查询操作完全可以放在redis中去查,减少数据库的压力
把秒杀劵的库存做缓存 如图
而对于订单表判断是否一人一单,就用set数据 结构做缓存 如图
当缓存做好时,用lua脚本保证所有操作的原子性 lua脚本如图
使用阻塞队列存储要保存的订单对象
创建stream消息队列