黑马点评项目感受

集群共享session问题

一般来说session存储的数据只对当前服务器可见(tomcat可见,虽然服务器可以与服务器之间共享数据,但有一个延迟的时间),当请求被其他服务器接受,将无法判断是否登录(session不共享)。

解决方案 用redis储存共享数据

注意:redis存储对象,最好用hash结构。原因 hash结构方便修改,不像json那样要整个更改。

使用StringRedisTemplate时注意 如果 某个键对应的值为 map 那么map中的所有数据都必须是String类型

该操作把UserDto的数据 转换为键值对 用map存储起来,并且 让所有键的值都为 String

熟记 BeanUtil 是一个 对象可以与对象转换,以及把 map 转换成 bean 或者 bean转换成 map

生成 随机数 6个数

项目中如果有多个拦截器,要控制拦截器的先后顺序,可以按添加拦截器的先后顺序,或者调用order方法

如图

黑马点评项目感受_第1张图片

order()方法中值越小,就越先执行

如果拦截器不指定拦截路径,就默认拦截全部路径

缓存

数据交换缓冲区,提高读写性能降低数据库的负载。

查询商户缓存

如果用String存储对象,那么就会是json格式。从json格式中取出一个java对象

指定json 和要转换的 类的字节码,也可以把类转换成JSON

查询商户类型缓存

黑马点评项目感受_第2张图片

因为 用stringRedisTemplate存储数据,并且还用 list数据结构,那么就会把从数据库中查询的list中的数据转换成 String(json)

做业务时,一定要注意返回给前端的数据类型

缓存的更新

如果 对数据库修改,并且会经常去查询,那么对数据库修改的操作和更新缓存发操作必须是原子性

对缓存操作时,最好是先操作数据库,再删除缓存(并且这两个操作必须是原子性

缓存穿透

redis 和数据库都没有 数据, 所以每次请求 都会打在数据库上

解决方案 1 缓存空对象(把存活时间设置短) 2 布隆过滤器(判断mysql数据库中是否有数据)

3 可以把查询的参数设置复杂点 4 可以做用户权限校验

布隆过滤器如图

黑马点评项目感受_第3张图片

布隆过滤器可能存在误判,如果判断出数据不存在那么数据一定不存在。

缓存雪崩

在同一时刻,redis中 大量的key同时失效 或者 redis直接宕机就会导致数据库压力飙升,从而可能导致数据库宕机

解决方案

1 设置不同的过期时间 ttl 2 搭建redis集群

缓存击穿(热点key访问)

一个高并发 的key失效 ,就会导致大量业务去访问数据库,就会导致数据库压力飙升,注意和缓存雪崩有区别,这里注重一个Key

解决方案 1 互斥锁 2 逻辑过期(永不过期)

黑马点评项目感受_第4张图片

两者的区别

黑马点评项目感受_第5张图片

项目注意

在防止缓存穿透时,设置空值缓存时 如下图

获取值做判断时 注意下图 注释

黑马点评项目感受_第6张图片

解释如图

黑马点评项目感受_第7张图片

这里存储的为空白 ,并不是""

如果使用逻辑过期处理互斥锁,在不修改源代码的基础上可以把 想要逻辑过期的类和时间绑定在一起

黑马点评项目感受_第8张图片

localdateTime表示当前日期时间

如果是逻辑过期时间处理,前提是缓存中现有数据(该案例就是这么讲的)

项目注意

黑马点评项目感受_第9张图片

这里 RedisData封装 了 类Shop,那么注意上图的反序列化操作后得到的redisData中的Shop类是JSONObject类型

黑马点评项目感受_第10张图片

所以只能用这种方式数据转换。

注意

在用逻辑过期时间处理缓存击穿时,在重建缓存阶段会开启新的线程重建,那么就要保证重建的这个线程结束后才能释放锁

如图(注意注释

黑马点评项目感受_第11张图片

定义泛型(返回值类型在参数中设置)

黑马点评项目感受_第12张图片

注意上图,这里dbFallback的apply方法的参数是 Function的 ID类型,R是函数的返回类型。

优惠秒杀劵

全局唯一ID

保证一个信息的安全性

该注解含义 表明表中没有该字段,不会映射

默认当前时间,ON UPDATE 表示自动更新,后续指 更新为当前时间。

MP手动设置sql写法 如图

乐观锁解决超卖

直接自定义sql,注意更新操作也会保证数据库加锁,同一时刻下就只有一个线程会修改该数据

注意 ctrl+alt+m 进行代码封装

实现一人一单(注意事项)

黑马点评项目感受_第13张图片

黑马点评项目感受_第14张图片

黑马点评项目感受_第15张图片
黑马点评项目感受_第16张图片

上诉操作只能在单台tomcat服务器下,同时也对应一台jvm,下才可满足,如果在多台服务器下,那么也是对应多台jvm下,那么就不能满足

黑马点评项目感受_第17张图片

锁对象实际用的字符串常量池的对象引用

黑马点评项目感受_第18张图片

实质用的锁对象是 字符串常量池中的userId对象引用,因为调用了intern() 所以单台服务器下每一次调用用的锁对象是同一个, 而上图的 str3 != str1,所以synchronized只能保证单个服务器下的互斥,不能保证集群下的互斥。

项目注意

关闭nginx

黑马点评项目感受_第19张图片

分布式锁

注意在锁过期后,误删的原子性

黑马点评项目感受_第20张图片

即便加上没有过期时才能删除的条件依然不能保证原子性

原项目的解决误删的方案

通过uuid加上线程id做为分布式锁的值,因为线程的id 会在单台jvm中呈递增趋势

那么多台jvm,就有可能会使线程id 重复,那么也会造成误删,所以加上uuid可以保证

服务器会根据自己生成的uuid加上线程号删除自己对应的那把锁

黑马点评项目感受_第21张图片

自己画的图如下

黑马点评项目感受_第22张图片

原方案释放锁

黑马点评项目感受_第23张图片

用lua脚本保证释放锁的原子性

黑马点评项目感受_第24张图片

lua脚本

黑马点评项目感受_第25张图片

redisson分布式锁

对于 setnx分布式锁 缺点 1 不可重入 2不会重试 3主从同步时 , 主节点挂掉,从节点转换成主节点时,在这一阶段会出现锁被多个线程获取(概率极低)

redisson的可重入原理

黑马点评项目感受_第26张图片

通过hash结构,对应hash的值就是重入锁的重入次数,同样在服务器集群下,filed只需要线程id 来设置,只有value次数减为0时才会释放锁。不会向setnx那样要区分不同的服务器释放锁还需要用uuid标识不同的服务器

利用发布订阅来解决重试获取锁

利用看门狗机制,在每隔一段时间就重置过期时间

项目注意

在实现秒杀抢劵时,会先查询劵的库存和 订单表是否一人一单,那么在高并发下,对于这种的查询操作完全可以放在redis中去查,减少数据库的压力

把秒杀劵的库存做缓存 如图

黑马点评项目感受_第27张图片

而对于订单表判断是否一人一单,就用set数据 结构做缓存 如图

当缓存做好时,用lua脚本保证所有操作的原子性 lua脚本如图

黑马点评项目感受_第28张图片

使用阻塞队列存储要保存的订单对象

黑马点评项目感受_第29张图片

黑马点评项目感受_第30张图片
黑马点评项目感受_第31张图片

redis消息队列实现异步秒杀

基于redis的list结构实现

黑马点评项目感受_第32张图片

发布订阅

黑马点评项目感受_第33张图片

stream流数据格式(消息队列)

创建stream消息队列

黑马点评项目感受_第34张图片

读取消息队列

黑马点评项目感受_第35张图片

你可能感兴趣的:(服务器,java,数据库)