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; }