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的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学习,这里不再叙述)


有时,并发环境会出现连接超时问题,这时候,我们就需要引入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;
}