栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Redis秒杀案例

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Redis秒杀案例

使用redis的事务完成秒杀案例,流程图如下:

 问题一:连接超时

连接超时问题可以使用redis连接池来进行解决

public static JedisPool getJedisPoolInstance() {
		if (null == jedisPool) {
			synchronized (JedisPoolUtil.class) {
				if (null == jedisPool) {
					JedisPoolConfig poolConfig = new JedisPoolConfig();
					// 设置连接池中一个pool可以设置多少个jedis实例
					poolConfig.setMaxTotal(200);
					// 设置一个pool中最多有多少个状态为idle(空闲)
					poolConfig.setMaxIdle(32);
					// 表示当borrow一个jedis实例时,最大的等待毫秒数,如果超过等待时间,则直接抛JedisConnectionException;
					poolConfig.setMaxWaitMillis(100*1000);
					poolConfig.setBlockWhenExhausted(true);
					// 获得一个jedis实例的时候是否检查连接可用性(ping());如果为true,则得到的jedis实例均是可用的;
					poolConfig.setTestonBorrow(true);  // ping  PONG

					jedisPool = new JedisPool(poolConfig, "192.168.119.130", 6379, 60000 );
				}
			}
		}
		return jedisPool;
	}

问题二:超卖问题

超卖问题可以使用redis的事务watch(乐观锁)解决超卖问题

// 创建事务(使用watch乐观锁的时候会有库存遗留的问题)
Transaction multi = jedis.multi();
// 组队操作
// 将商品数量减一
multi.decr(productKey);
// 将秒杀用户key入库
multi.sadd(userKey, uid);
// 执行
List results = multi.exec();
if (results.size() == 0) {
	System.out.println("秒杀失败");
	jedis.close();
	return false;
} 

问题三:库存遗留问题

乐观锁导致很多请求都失败,先点的没秒到,后点的可能秒到了,使用LUA脚本解决库存遗留问题。

LUA脚本在Redis中的优势

将复杂的或者多步的redis操作,写为一个脚本,一次提交给redis执行,减少反复连接redis的次数。提升性能。LUA脚本是类似redis事务,有一定的原子性,不会被其他命令插队,可以完成一些redis事务性的操作。

static String secKillscript =
			"local userid=KEYS[1];rn" +
					"local prodid=KEYS[2];rn" +
					"local qtkey='sk:'..prodid..":qt";rn" +
					"local usersKey='sk:'..prodid..":usr";rn" +
					"local userExists=redis.call("sismember",usersKey,userid);rn" +
					"if tonumber(userExists)==1 then rn" +
					"   return 2;rn" +
					"endrn" +
					"local num= redis.call("get" ,qtkey);rn" +
					"if tonumber(num)<=0 then rn" +
					"   return 0;rn" +
					"else rn" +
					"   redis.call("decr",qtkey);rn" +
					"   redis.call("sadd",usersKey,userid);rn" +
					"endrn" +
					"return 1" ;

最终代码实现如下:

// 定义两段Lua脚本(使用Lua脚本可以解决乐观锁带来的库存遗留问题)
	static String secKillscript =
			"local userid=KEYS[1];rn" +
					"local prodid=KEYS[2];rn" +
					"local qtkey='sk:'..prodid..":qt";rn" +
					"local usersKey='sk:'..prodid..":usr";rn" +
					"local userExists=redis.call("sismember",usersKey,userid);rn" +
					"if tonumber(userExists)==1 then rn" +
					"   return 2;rn" +
					"endrn" +
					"local num= redis.call("get" ,qtkey);rn" +
					"if tonumber(num)<=0 then rn" +
					"   return 0;rn" +
					"else rn" +
					"   redis.call("decr",qtkey);rn" +
					"   redis.call("sadd",usersKey,userid);rn" +
					"endrn" +
					"return 1" ;

	static String secKillscript2 =
			"local userExists=redis.call("sismember","{sk}:0101:usr",userid);rn" +
			" return 1";

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

		JedisPool jedispool =  JedisPoolUtil.getJedisPoolInstance();
		Jedis jedis=jedispool.getResource();
		jedis.select(2);

		// 通过jedis的scriptLoad方法加载Lua脚本
		String sha1=  jedis.scriptLoad(secKillscript);
		//通过jedis的evalsha方法调用Lua脚本
		Object result= jedis.evalsha(sha1, 2, uid,prodid);

		String reString=String.valueOf(result);
		if ("0".equals( reString )  ) {
			System.err.println("已抢空!!");
		}else if("1".equals( reString )  )  {
			System.out.println("抢购成功!!!!");
		}else if("2".equals( reString )  )  {
			System.err.println("该用户已抢过!!");
		}else{
			System.err.println("抢购异常!!");
		}
		jedis.close();
		return true;
	}

 

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号