-
动静分离
将css、js、img、html等静态界面放在nginx,动态界面从应用服务器获取 -
JVM内存优化(尽可能的使内存大些,这样垃圾回收占用的时间便会减少)
-Xmx1024m: jvm最大可用内存为1024m
-Xms1024m: jvm启动时分配的内存为1024m,此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
-Xmn512m: 新生代内存大小为512m,整个JVM内存大小=新生代大小 + 老年代大小 + 永久代大小 -
数据库索引
pms_category中的paremt_cid(父节点分类id)设置索引,在三级分类查询过程中会有优化. -
数据库的多次查询变成一次
-
原来三级查询的方式
先从数据库中查一级分类,再将一级分类的cat_id作为二级分类的parent_cid从数据库中查询二级分类信息,再将二级分类的cat_id作为三级分类的parent_cid查询三级分类信息.
需要向数据库中查询三次 -
优化的查询方式
从数据库中获取分类表中所有的数据,进行三次过滤操作,即一级分类的parent_cid=0,二级分类的parent_cid等于一级分类的cid,三级分类的parent_cid等于二级分类的cid.
只需要向数据库中查询一次,虽然之后要有过滤操作,但是要比向数据库中查询快的多.
- 使用redis缓存
- 应用场景
- 即时性、数据一致性要求不高
- 访问量大且更新频率不高的数据
- 引用举例
- 秒杀商品中的商品信息(库存等),显示的库存可能会大于实际库存,但是只要保证不多卖就可以.
- redis缓存存在的问题及解决办法
-
1.缓存穿透
当查询一个一定不存在的数据,由于缓存不能命中,将去查询数据库,但是数据库中也没有这个记录,这将导致这个不存在的数据每次都要去存储层查询,失去了缓存的意义.而且会有利用不存在的数据进行攻击,致使数据库瞬间压力增大导致崩溃的风险. -
缓存穿透解决办法
将null结果缓存,并加入短暂过期时间 -
2.缓存雪崩
在设置缓存时key采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重雪崩 -
缓存雪崩的解决办法
原来的失效时间基础上增加一个随机值 -
3.缓存击穿
对于一些设置了过期时间的key,如果这些key在某些时间点会被超高并发访问,并且这个key在大量请求进来前刚好失效,那么所有对于这个key的查询都落到db,导致数据库瞬间压力增大导致崩溃. -
缓存击穿解决办法
通过加锁,大量并发只让一个去查,其他请求等待,查到后首先将数据放入缓存然后再释放锁(如果先释放锁再将数据放入缓存,可能会导致多次查询数据库),其他请求获取到锁,先查缓存. -
4.缓存击穿本地锁的具体实现
从redis中获取数据; if(redis中获取数据为null){ // 对当前对象加锁,因为springboot中的所有组件在容器中都是单例的 synchronized (this){ // 获取锁后再从redis中获取数据的原因是因为之前可能多个请求堵塞在锁外部(锁外部的redis获取数据为空) 从redis中获取数据; if(redis中获取的数据不为空){ 返回数据; }else{ 从数据库中获取数据; 将数据放到redis缓存中; } } }else{ return redis中获取的数据; }
在分布式环境下存在的问题: 同一个服务中只会从数据库中获取一次,但是多个服务会获取多次.
-
6.缓存数据一致性(redis和数据库)解决方案
方案一: 缓存的所有数据都有过期时间,数据过期下一次查询触发主动更新缓存
方案二: 读写数据的时候,加上分布式读写锁
-
-
使用线程池异步编排
@Override public SkuItemVo item(Long skuId) throws ExecutionException, InterruptedException { SkuItemVo skuItemVo = new SkuItemVo(); CompletableFuture<SkuInfoEntity> infoFuture = CompletableFuture.supplyAsync(()->{ // 1. sku基本信息获取 pms_sku_info SkuInfoEntity info = getById(skuId); skuItemVo.setInfo(info); return info; }, executor); CompletableFuture<Void> saleAttrFuture = infoFuture.thenAcceptAsync((res)->{ // 3. 获取sku的销售属性组合 List<SkuItemSaleAttrVo> saleAttrVos = skuSaleAttrValueService.getSalesAttrsBySpuId(res.getSpuId()); skuItemVo.setSaleAttr(saleAttrVos); }, executor); CompletableFuture<Void> descFuture = infoFuture.thenAcceptAsync((res)->{ // 4. 获取spu描述信息 SpuInfoDescEntity spuInfoDescEntity = spuInfoDescService.getById(res.getSpuId()); skuItemVo.setDesp(spuInfoDescEntity); },executor); CompletableFuture<Void> baseAttrFuture = infoFuture.thenAcceptAsync((res)->{ // 5. 获取spu的规格参数信息 List<SpuItemAttrGroupVo> attrGroupVos = attrGroupService.getAttrGroupWithAttrsBySpuId(res.getSpuId(), res.getCatalogId()); skuItemVo.setGroupAttrs(attrGroupVos); }, executor); CompletableFuture<Void> imageFuture = CompletableFuture.runAsync(()->{ // 2. sku的图片信息 List<SkuImagesEntity> skuImagesEntities = skuImagesService.list(new QueryWrapper<SkuImagesEntity>().eq("sku_id", skuId)); skuItemVo.setImages(skuImagesEntities); }, executor); // 所有异步执行完成才返回,参数不用写infoFuture,因为saleAttrFuture, descFuture, baseAttrFuture都是在infoFuture的基础上执行的 CompletableFuture.allOf(saleAttrFuture, descFuture, baseAttrFuture, imageFuture).get(); return skuItemVo; }