数据库
首页 > 数据库> > 测试redis分布式锁

测试redis分布式锁

作者:互联网

1、基础知识:

通过HttpServletRequest获取对方请求的IP地址
request.getRemoteAddr()直接请求到服务器,获取对方的IP地址
request.getHeader("")通过nginx负载均衡访问的服务器,获取对方的IP地址。
在这里插入图片描述

@RequestMapping("{skuId}.html")
public String item(@PathVariable String skuId, ModelMap modelMap, HttpServletRequest request){

    String IP = request.getRemoteAddr();   //直接请求到服务器,获取对方的IP地址

    //request.getHeader("");  通过nginx负载均衡访问的服务器,获取对方的IP地址

2、设置分布式锁防止redis缓存击穿
(1)第1位同学先请求页面,就有第1条线程先进来。先查询缓存但是没查到,就去申请设置锁,并且拿到了锁,有权在10秒的过期时间内访问数据库。
为了看到设置锁的效果,这里设置第1位同学的线程等待5秒再去查询数据库和把结果存入redis,再归还删除锁
(2)第2名同学紧接着也请求页面,查询缓存但没查到。就申请设置锁。因为第1名同学还在等待5秒钟,还没有归还删除锁。
所以第2名同学就拿不到锁,也线程等待3秒,本方法自旋1次(第2名同学照样拿不到锁),线程继续等待3秒,本方法自旋第2次(正常从缓存查到数据)
(3)5秒过去了,第1名同学的线程开始执行,查询数据库和把结果存入redis,再归还删除锁。
(4)这样第2名同学的线程第2次自旋本方法,查询缓存,查到了,成功访问到前端页面!!!
在这里插入图片描述
3、实现代码如下(好好理解里面的注释):
基础知识:

Thread.sleep(1000*5) //线程睡5秒
Thread.currentThread().getName() //获取线程的名称
@Override
//用户购买商品页面:查看sku商品,sku图片集合
public PmsSkuInfo getSkuById(String skuId , String IP) {

    System.out.println("ip为:"+IP+"的同学的线程:"+Thread.currentThread().getName()+"正准备进入商品详情页面");
    PmsSkuInfo pmsSkuInfo = new PmsSkuInfo();

    //连接缓存
    Jedis jedis = redisUtil.getJedis();

    //查询缓存
    String skuKey = "sku:"+skuId+":info";
    String skuJson = jedis.get(skuKey);   //根据skuId到redis缓存查询得到一个json的字符串

    if(StringUtils.isNotBlank(skuJson)) {   //如果不等于空,即是可以在缓存查到
        pmsSkuInfo = JSON.parseObject(skuJson,PmsSkuInfo.class);  //把查询到的json字符串 转为 java对象
        System.out.println("ip为:"+IP+"的同学的线程:"+Thread.currentThread().getName()+"成功从缓存获取商品详情页面QWQ");

    }else {
        //如果缓存没有,就查询mysql数据库

        System.out.println("ip为:"+IP+"的同学的线程:"+Thread.currentThread().getName()+"发现缓存没有,申请获取锁");
        //设置分布式锁,防止缓存击穿。让用户有秩序的拿锁访问数据库,而不是一窝蜂进到数据库
        String ok = jedis.set("sku:" + skuId + ":lock", "1", "nx", "px", 10*1000);
        if(StringUtils.isNotBlank(ok)&&ok.equals("OK")){  //设置锁返回ok成功,允许这位用户访问数据库,10秒后过期时间,再允许下一位访问
                                    //注意返回来的字符串OK是大写!
            System.out.println("ip为:"+IP+"的同学的线程:"+Thread.currentThread().getName()+"拿到了锁"+"sku:" + skuId + ":lock"+",有权在10秒过期时间内访问数据库");


            System.out.println("ip为:"+IP+"的同学的线程:"+Thread.currentThread().getName()+"让该线程等待5秒。。。");


            try {
                Thread.sleep(1000*5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            pmsSkuInfo = getSkuByIdFromDb(skuId);  //建立redis缓存服务器,就是为了保护这行代码:查询数据库的操作
            if(pmsSkuInfo!=null) {
                //mysql查询结果存入redis
                jedis.set("sku:"+skuId+":info", JSON.toJSONString(pmsSkuInfo));    //把java对象 转为 json字符串

            }else {  //mysql查询结果为null,防止缓存穿透!!!!

                //把这个不存在的skuId设为:空值json字符串null,并存到redis服务器中。
                jedis.setex("sku:"+skuId+":info",60*3, JSON.toJSONString(""));
                //seconds是秒。3分钟内再有请求这skuId,就直接返回一个空的对象给前端(没有任何信息)。不再执行查询数据库操作
            }

            jedis.del("sku:" + skuId + ":lock");   //访问数据库结束后,删除锁。下一个用户继续拿锁访问!
            System.out.println("ip为:"+IP+"的同学的线程:"+Thread.currentThread().getName()+"访问数据库完毕,归还并删除锁:"+"sku:" + skuId + ":lock");

        }else {  //设置锁不成功,自旋(设置该线程睡眠几秒,再重新尝试访问本方法)

            System.out.println("ip为:"+IP+"的同学的线程:"+Thread.currentThread().getName()+"没拿到锁,开始自旋!");

            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }


            return getSkuById(skuId, IP);   //这里要return返回这个方法重新访问,同一条线程!!
                                        //不加return,则创建一条新的线程,原来的线程还是访问不到,即原来的线程成了一条孤儿线程!
        }

    }
    jedis.close();   //关闭缓存连接
    return pmsSkuInfo;
}

标签:同学,skuId,缓存,Thread,IP,redis,线程,测试,分布式
来源: https://blog.csdn.net/Hiber12/article/details/111156457