Redis(五)

Redis事务-秒杀案例

讲解了之前的知识,我们来做一个秒杀实例

本文只提供后台jedis操作代码

基础代码:

设置Redis库存: set SecKill:0101:kc 10

public static boolean doSecKill(String uid,String prodid) throws IOException{

        //拼接key
        String kcKey="SecKill:"+prodid+":kc";
        String userKey="SecKill:"+prodid+":user";
        Jedis jedis=new Jedis("127.0.0.1",6379);
        //获取库存
        String kc = jedis.get(kcKey);
        //秒杀还没开始,表示库存为null
        if (kc==null)
        {
            System.out.println("秒杀还未开始");
            jedis.close();
            return false;
        }
        //已经秒杀成功,表示为存储uid的set中已经有该用户uid
        if (jedis.sismember(userKey,uid)) {
            System.out.println("已经秒杀成功,不可重复秒杀");
            jedis.close();
            return false;
        }
        //判断库存,若大于0,则减库存加人,若小于等于0,秒杀失败
        if (Integer.parseInt(kc)<=0)
        {
            System.out.println("秒杀已结束");
            jedis.close();
            return false;
        }
        //库存大于0,减库存,加人
        jedis.decr(kcKey);
        jedis.sadd(userKey,uid);
        System.out.println("秒杀成功");
        jedis.close();
        return true;
    }

可以使用ab工具模拟并发秒杀场景

ab工具在CentOS6可默认安装,CentOS7手动安装

联网 yum install httpd-tools

无网络:

(1) 进入 cd/run/media/root/CentOS 7/x86_64/Packages(路径跟CentOS6不同,镜像文件放安装包的文件夹)

(2) 顺序安装

安装命名 rpm-ivh

apr-1.4.8-3.el7.x86_64.rpm

apr-util-1.5.2-6-el7.x86_64.rpm

httpd-tools-2.4.6-67.el7.centos.x86_64.rpm

ab工具使用简介

ab -n 请求数 -c并发数 -p 指定请求数据文件 -T “application/x-www-form-urlencoded” 测试的请求

例如:

使用ab工具检验并发环境时,会出现以下问题:

Redis(五)

并发下的超卖问题

 解决办法:使用redis的watch进行监视,加入Redis的事务

public static boolean doSecKill(String uid,String prodid) throws IOException{

        //拼接key
        String kcKey="SecKill:"+prodid+":kc";
        String userKey="SecKill:"+prodid+":user";
        Jedis jedis=new Jedis("127.0.0.1",6379);
        //监视库存,以解决超卖问题
        jedis.watch(kcKey);
        //获取库存
        String kc = jedis.get(kcKey);
        //秒杀还没开始,表示库存为null
        if (kc==null)
        {
            System.out.println("秒杀还未开始");
            jedis.close();
            return false;
        }
        //已经秒杀成功,表示为存储uid的set中已经有该用户uid
        if (jedis.sismember(userKey,uid)) {
            System.out.println("已经秒杀成功,不可重复秒杀");
            jedis.close();
            return false;
        }
        //判断库存,若大于0,则减库存加人,若小于等于0,秒杀失败
        if (Integer.parseInt(kc)<=0)
        {
            System.out.println("秒杀已结束");
            jedis.close();
            return false;
        }
        //库存大于0,减库存,加人
        Transaction multi = jedis.multi();
        jedis.decr(kcKey);
        jedis.sadd(userKey,uid);
        List<Object> list = multi.exec();
        if (list==null||list.size()==0)
        {
            System.out.println("秒杀失败");
            jedis.close();
            return false;
        }
        System.out.println("秒杀成功");
        jedis.close();
        return true;
    }

执行代码后,我们发现,会产生库存遗留问题,因为在同一并发下,只有一个用户才能秒杀成功

因为watch会监视key,只要key里面的value发生变化,事务里的其他操作便都不执行了

可以使用Lua脚本解决

Lua简介

Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。(不了解者可自行google学习,这里不再叙述)

Redis(五)

Redis(五)

有时,并发环境会出现连接超时问题,这时候,我们就需要引入jedis连接池来处理

public Jedis getJedis()
    {
        JedisPool jedisPool;
        JedisPoolConfig poolConfig=new JedisPoolConfig();
        poolConfig.setMaxTotal(200);// 可用连接实例的最大数目,如果赋值为-1,表示不限制.默认值为8
        poolConfig.setMaxIdle(32); //jedis最大保存idel(空闲的)状态的对象,默认值也是8
        poolConfig.setMaxWaitMillis(100*1000); //jedis池没有对象返回时,最大等待时间
        poolConfig.setBlockWhenExhausted(true);//连接耗尽时是否阻塞, false报异常,true阻塞直到超时, 默认true
        poolConfig.setTestOnBorrow(true);// 在borrow一个jedis实例时,是否提前进行validate操作,如果为true,则得到的jedis实例均是可用的
        jedisPool=new JedisPool(poolConfig,"127.0.0.1",6379,10000);
        Jedis jedis = jedisPool.getResource();
        return jedis;

    }

相关推荐