首页 >

一起聊聊Redis实现秒杀的问题

数据库|Redis一起聊聊Redis实现秒杀的问题
redis
数据库-Redis
图书类网站 源码,vscode外部js提示,ubuntu 安装 gb,tomcat 大写jsp,sqlite3 工具来,微信小程序就是一个前端框架,农村厕所里边爬虫有几种,php sql注入代码,黄石seo在线咨询,阿狸表白网站源码 修改,html网页圆形表格动态,动易导入模板lzw
Redis视频教学
微信客户管理系统源码,vscode变成中文菜单,ubuntu更改inet,tomcat不带端口访问,sqlite3移植开发板,用橡皮泥做内鬼之王蓝色雷姆爬虫,php 获取汇率,淮南seo网络推广收费,网站 手机 css,c7-2分项模板lzw
1、秒杀逻辑
体育赛事源码,vscode下载下载失败,光盘恢复 ubuntu,tomcat加入项目编译,qt-sqlite停车场项目,怀孕五个月吃了小爬虫药,redhat安装php,烟台seo推广哪家便宜,电影院网站模板,oa网站模板下载lzw
秒杀:解决计数器和人员记录的事务操作

1.uid和proid非空判断2.连接redis3.拼接key库存key秒杀成功用户key4.获取库存,如果库存为null,秒杀还没开始5.判断用户是否重复秒杀操作6.判断商品数量,库存数量小于1,秒杀结束7.秒杀过程库存-1把秒杀成功用户添加清单里面2、存在问题2.1、连接超时

原因:由于大量创建连接,十分消耗性能,并且有时获取连接不及时,出现连接超时的情况

2.2、超卖

在并发的情况下发生的,就是在输出没有库存(秒杀结束)后还有商品售出导致库存数量为负数。
一起聊聊Redis实现秒杀的问题

2.3、库存遗留

使用乐观锁解决问题2之后,出现问题3

如果库存数量相对并发更多,由于使用乐观锁,第一个用户秒杀成功后会修改库存键的版本号,其他抢到的用户会因为版本号不同导致无法继续购买,就会有库存遗留问题

3、解决3.1、连接超时

使用连接池,工具类如下:

public class JedisPoolUtil {	private static volatile JedisPool jedisPool = null;	private JedisPoolUtil() {	}	public static JedisPool getJedisPoolInstance() {		if (null == jedisPool) {			synchronized (JedisPoolUtil.class) {				if (null == jedisPool) {					JedisPoolConfig poolConfig = new JedisPoolConfig();					poolConfig.setMaxTotal(200);					poolConfig.setMaxIdle(32);					poolConfig.setMaxWaitMillis(100 * 1000);					poolConfig.setBlockWhenExhausted(true);					poolConfig.setTestOnBorrow(true);					jedisPool = new JedisPool(poolConfig, "127.0.0.1", 6379, 60000);				}			}		}		return jedisPool;	}}//使用JedisPool jedisPoolInstance = JedisPoolUtil.getJedisPoolInstance();Jedis jedis = jedisPoolInstance.getResource();

springBoot版本(pom.xml引入,application.yml配置,然后注入对象即可)

    org.springframework.boot    spring-boot-starter-data-redis    redis.clients    jedis    3.2.0
spring:  redis:    host: 127.0.0.1    port: 6379    database: 0    timeout: 1800000    lettuce:      pool:        max-active: 20        max-wait: -1        max-idle: 5        min-idle: 0
    @Autowired    private RedisTemplate redisTemplate;

3.2、超卖问题

使用Redis事务,乐观锁 + watch

//监视库存jedis.watch(kcKey);//中间代码忽略//7 秒杀过程//使用事务Transaction multi = jedis.multi();//组队操作multi.decr(kcKey);multi.sadd(userKey,uid);//执行List results = multi.exec();if(results == null || results.size()==0) {    System.out.println("秒杀失败了....");    jedis.close();    return false;}

3.3、乐观锁导致的库存遗留问题

使用Lua嵌入式脚本语言

将复杂的或者多步的 Redis 操作,写为一个脚本,一次提交给Redis运行,减少反复连接 reids的次数。提升性能。LUA脚本是类似 redis 事务,有一定的原子性,不会被其他命令插队,可以完成redis事务性的操作LUA脚本功能,在Redis 2.6以上的版本才可以使用利用 lua 脚本淘汰用户,解决超卖问题。redis 2.6 版本以后,通过 lua 脚本解决争抢问题,实际上是 redis 利用其单线程的特性,用任务队列的方式解决多任务并发问题

local userid=KEYS[1];				//1、2行定义两个变量,					local prodid=KEYS[2];local qtkey="sk:"..prodid..":qt";	//3,4行定义拼接keylocal usersKey="sk:"..prodid..":usr";local userExists=redis.call("sismember",usersKey,userid); //5-8,判断用户是否存在,不存在return 2if tonumber(userExists)==1 then    return2;endlocal num=redis.call("get",qtkey);	//9-11,判断商品是否存在if tonumber(num)<=0 then    return 0;else								//12-15,用户和商品操作    redis.call("decr",qtkey);    redis.call("sadd",usersKey,userid);endreturn1;  							//最后一行return 1;  秒杀成功

完整代码如下:

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

Redis视频教学


  • 暂无相关文章
  • Posted in 未分类
    © 牛的日记 | www.liuzhongwei.com
    网站部分内容来源于网友供稿,若有侵权请联系删除,970928#QQ.com