商城电商day07 商品详情页面优化

本文主要探讨商品详情页面的优化,通过整合Redis实现缓存,以减少数据库查询。并讨论了分布式锁的必要性,分析了本地锁的局限性,提出分布式锁的解决方案,如使用Redisson,并展示了如何结合AOP实现缓存操作。此外,还涉及首页商品分类的实现,考虑了页面静态化和数据接口的封装。
摘要由CSDN通过智能技术生成

day07 商品详情页面优化

一般分为两个层面,一是提高数据库sql本身的性能,二是尽量避免直接查询数据库。
重点要讲的是另外一个层面:尽量避免直接查询数据库。
解决办法就是:缓存

1.2 整合redis到工程

开始开发先说明redis key的命名规范,由于Redis不像数据库表那样有结构,其所有的数据全靠key进行索引,所以redis数据的可读性,全依靠key。
企业中最常用的方式就是:object🆔field
比如:sku:1314:info
user:1092:info
:表示根据windows的 /一个意思

二、分布式锁

2.1 本地锁的局限性

接下来启动8206 8216 8226 三个运行实例。
运行多个service-product实例:
server.port=8216
server.port=8226
**集群情况下又出问题了!!!
以上测试,可以发现:
本地锁只能锁住同一工程内的资源,在分布式系统里面都存在局限性。
此时需要分布式锁。。
**

2.2 分布式锁实现的解决方案

  1. 多个客户端同时获取锁(setnx)
  2. 获取成功,执行业务逻辑{从db获取数据,放入缓存},执行完成释放锁(del)
  3. 其他客户端等待重试

为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件:

  • 互斥性。在任意时刻,只有一个客户端能持有锁。
  • 不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。
  • 解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。
  • 加锁和解锁必须具有原子性

2.4 使用redisson 解决分布式锁

1 获得skuKey
2 先查询缓存 通过skuKey从redis获取SkuInfo
3 缓存没有 就要去查数据库
4,先获取锁 若果数据库也没有
5 就把空的数据放入 缓存
6,如果数据库有数据 就把数据放入redis缓存
7,try catch 有异常打印 finally 释放锁
8,如果没获取到锁 睡一会 在尝试
9,缓存有就加入缓存
10 为了防止缓存宕机:从数据库中获取数据 return getSkuInfoDB(skuId);

if (skuInfonull){//先查询缓存 通过skuKey从redis获取SkuInfo
if (res){//先获取锁
if (skuInfo
null){ //若果数据库也没有
}
}else{
// 其他线程等待 如果没获取到锁 睡一会 在尝试
Thread.sleep(1000);
return getSkuInfo(skuId);
}
}else{
return skuInfo;
}
return getSkuInfoDB(skuId);

// 使用redis’ 做分布式锁

package com.atguigu.gmall.product.service.impl;

import com.alibaba.nacos.client.utils.StringUtils;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.model.product.SkuImage;
import com.atguigu.gmall.model.product.SkuInfo;
import com.atguigu.gmall.product.mapper.SkuImageMapper;
import com.atguigu.gmall.product.mapper.SkuInfoMapper;
import com.atguigu.gmall.product.service.TestService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import javax.swing.*;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
public class TestServiceImpl implements TestService {
   

    //都是源码的内容 用redis时使用的
    @Autowired
    private StringRedisTemplate stringRedisTemplate;


    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private SkuInfoMapper skuInfoMapper;

    @Autowired
    private SkuImageMapper skuImageMapper;




/*
    // 使用redis' 做分布式锁
    private SkuInfo getSkuInfoRedis(Long skuId) {
        SkuInfo skuInfo = null;
        try {
            // 缓存存储数据:key-value
            // 定义key sku:skuId:info
            String skuKey = RedisConst.SKUKEY_PREFIX+skuId+RedisConst.SKUKEY_SUFFIX;
            // 获取里面的数据? redis 有五种数据类型 那么我们存储商品详情 使用哪种数据类型?
            // 获取缓存数据
            skuInfo = (SkuInfo) redisTemplate.opsForValue().get(skuKey);
            // 如果从缓存中获取的数据是空
            if (skuInfo==null){
                // 直接获取数据库中的数据,可能会造成缓存击穿。所以在这个位置,应该添加锁。
                // 第一种:redis ,第二种:redisson
                // 定义锁的key sku:skuId:lock  set k1 v1 px 10000 nx
                String lockKey = RedisConst.SKUKEY_PREFIX+skuId+RedisConst.SKULOCK_SUFFIX;
                // 定义锁的值
                String uuid = UUID.randomUUID().toString().replace("-","");
                // 上锁
                Boolean isExist = redisTemplate.opsForValue().setIfAbsent(lockKey, uuid, RedisConst.SKULOCK_EXPIRE_PX2, TimeUnit.SECONDS);
                if (isExist){
                    // 执行成功的话,则上锁。
                    System.out.println("获取到分布式锁!");
                    // 真正获取数据库中的数据 {数据库中到底有没有这个数据 = 防止缓存穿透}
                    skuInfo = getSkuInfoDB(skuId);
                    // 从数据库中获取的数据就是空
                    if (skuInfo==null){
                        // 为了避免缓存穿透 应该给空的对象放入缓存
                        SkuInfo skuInfo1 = new SkuInfo(); //对象的地址
                        redisTemplate.opsForValue().set(skuKey,skuInfo1,RedisConst.SKUKEY_TEMPORARY_TIMEOUT,TimeUnit.SECONDS);
                        return skuInfo1;
                    }
                    // 查询数据库的时候,有值
                    redisTemplate.opsForValue().set(skuKey,skuInfo,RedisConst.SKUKEY_TIMEOUT,TimeUnit.SECONDS);
                    // 解锁:使用lua 脚本解锁
                    String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
                    // 设置lua脚本返回的数据类型
                    DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
                    // 设置lua脚本返回类型为Long
                    redisScript.setResultType(Long.class);
                    redisScript.setScriptText(script);
                    // 删除key 所对应的 value
                    redisTemplate.execute(redisScript, Arrays.asList(lockKey),uuid);

                    return skuInfo;
                }else {
                    // 其他线程等待
                    Thread.sleep(1000);
                    return getSkuInfoRedis(skuId);
                }
            }else {

                return skuInfo;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 为了防止缓存宕机:从数据库中获取数据
        return getSkuInfoDB(skuId);
    }
*/

    @Override
    public void testLock() {
   

    }

    @Override
    public
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

明月常新

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值