Redis:12-Redis事务秒杀案例

文章目录

    • 一、解决计数器和人员记录的事务操作
    • 二、Redis事务秒杀并发模拟
      • 1、安装httpd-tools
      • 2、测试及结果
    • 三、超卖问题
    • 四、乐观锁解决超卖问题
    • 五、继续增加并发测试
      • 1、连接有限制
      • 2、已经秒光,但还有库存
      • 3、连接超时,通过连接池解决
      • 4、连接池
    • 六、解决库存遗留问题
      • 1、LUA脚本
      • 2、LUA脚本在Redis中的优势
    • 七、代码
      • 1、项目结构
      • 2、第一版:简单版
      • 3、第二版:加事务-乐观锁(解决超卖)
      • 4、第三版:连接池解决超时问题
      • 5、第四版:解决库存依赖问题,LUA脚本
      • 6、最终效果

一、解决计数器和人员记录的事务操作

Redis:12-Redis事务秒杀案例_第1张图片

二、Redis事务秒杀并发模拟

使用工具ab模拟测试
CentOS6 默认安装
CentOS7需要手动安装

1、安装httpd-tools

有网络的话直接安装:

yum install httpd-tools

Redis:12-Redis事务秒杀案例_第2张图片

没网络:进入cd /run/media/root/CentOS 7 x86_64/Packages(路径跟centos6不同),顺序安装

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  

2、测试及结果

(1)通过ab测试
创建文件postfile,填写参数

vim postfile 模拟表单提交参数,以&符号结尾;存放当前目录。

postfile文件的内容:

prodid=0101&

添加请求:

ab -n 2000 -c 200 -k -p ~/postfile -T application/x-www-form-urlencoded http://192.168.2.115:8080/Seckill/doseckill

~/postfile是存储参数的文件
http://192.168.2.115:8081/Seckill/doseckill是本地Java Web项目
-n 2000表示请求数量为2000
-c 200表示并发数量为200
-k:启用HTTP KeepAlive功能,即在一个HTTP会话中执行多个请求。默认时,不启用KeepAlive功能。

(2)超卖
并发情况下会出现超卖问题,即库存为负值。

三、超卖问题

Redis:12-Redis事务秒杀案例_第3张图片

四、乐观锁解决超卖问题

利用乐观锁淘汰用户,解决超卖问题。

Redis:12-Redis事务秒杀案例_第4张图片

五、继续增加并发测试

1、连接有限制

ab -n 2000 -c 200 -k -p postfile -T 'application/x-www-form-urlencoded' http://192.168.140.1:8080/seckill/doseckill

增加-r参数,-r Don’t exit on socket receive errors.(抛出异常继续执行测试任务)

ab -n 2000 -c 100 -r -p postfile -T 'application/x-www-form-urlencoded' http://192.168.140.1:8080/seckill/doseckill

2、已经秒光,但还有库存

ab -n 2000 -c 100 -p postfile -T 'application/x-www-form-urlencoded' http://192.168.137.1:8080/seckill/doseckill

已经秒光,可是还有库存。原因,就是乐观锁导致很多请求都失败。先点的没秒到,后点的可能秒到了。
Redis:12-Redis事务秒杀案例_第5张图片

3、连接超时,通过连接池解决

在这里插入图片描述

4、连接池

Redis:12-Redis事务秒杀案例_第6张图片

节省每次连接redis服务带来的消耗,把连接好的实例反复利用。通过参数管理连接的行为。

链接池参数:

  • MaxTotal:控制一个pool可分配多少个jedis实例,通过pool.getResource()来获取;如果赋值为-1,则表示不限制;如果pool已经分配了MaxTotal个jedis实例,则此时pool的状态为exhausted。
  • maxIdle:控制一个pool最多有多少个状态为idle(空闲)的jedis实例;
  • MaxWaitMillis:表示当borrow一个jedis实例时,最大的等待毫秒数,如果超过等待时间,则直接抛JedisConnectionException;
  • testOnBorrow:获得一个jedis实例的时候是否检查连接可用性(ping());如果为true,则得到的jedis实例均是可用的;

六、解决库存遗留问题

乐观锁会造成库存遗留问题,当第一个请求修改了版本号之后,之后的请求发现版本号不一样会中断请求,所以即使还有库存,也不会在执行请求了,就造成了库存遗留问题,可使用LUA脚本解决这个问题。

1、LUA脚本

Lua 是一个小巧的脚本语言,Lua脚本可以很容易的被C/C++ 代码调用,也可以反过来调用C/C++的函数,Lua不适合作为开发独立应用程序的语言,而是作为嵌入式脚本语言。

2、LUA脚本在Redis中的优势

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

七、代码

1、项目结构

Redis:12-Redis事务秒杀案例_第8张图片

2、第一版:简单版

使用工具ab模拟并发测试,会出现超卖情况。查看库存会出现负数。手动请求,非并发。
Redis:12-Redis事务秒杀案例_第9张图片

3、第二版:加事务-乐观锁(解决超卖)

解决超卖问题,但出现遗留库存和连接超时。

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);  // ping  PONG

				//解决超时问题
				jedisPool = new JedisPool(poolConfig, "192.168.44.168", 6379, 60000 );
			}
		}
	}
	return jedisPool;
}

4、第三版:连接池解决超时问题

//解决超时问题
jedisPool = new JedisPool(poolConfig, "192.168.44.168", 6379, 60000 );

5、第四版:解决库存依赖问题,LUA脚本

Luau脚本,嵌入Java语言中

//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" ;

6、最终效果

Redis中添加库存:
库存为500

set sk:0101:qt 500

模拟并发:
http://192.168.146.1:8080/SecKill根据项目实际访问路径填写

ab -n 2000 -c 200 -k -p postfile -T 'application/x-www-form-urlencoded' http://192.168.222.1:8080/SecKill

ab并发测试访问超时了,应该是服务器上访问不了本地Java项目:
在这里插入图片描述

贴一个手动模拟的非并发:

Redis:12-Redis事务秒杀案例_第10张图片

Redis中商品库存为0了:

在这里插入图片描述

你可能感兴趣的:(Redis,redis,ab测试)