Shang Silicon Valley Redis learning notes -- Redis second kill case

1, Resolve transaction operations for counters and personnel records

2, Redis transaction - second kill concurrency simulation

Use tool ab to simulate the test

1. Networking: Yum install httpd tools

2. No network

(1) Enter cd /run/media/root/CentOS 7 x86_64/Packages (the path is different from centos6)
(2) Sequential installation

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 

3. Test and results

① Pass ab test

vim postfile simulates form submission parameters, ending with & symbol; Store the current directory.
Content: prodid = 0101&
command

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

3, Oversold problem

4, Use optimistic lock to eliminate users and solve the oversold problem.




5, Continue to add concurrent tests

1. Connection Limited

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


Add the - r parameter, - 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. It's been seconds, but there's still inventory

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

It's been seconds, but there's still inventory. The reason is that optimistic locks cause many requests to fail. The first one didn't arrive, and the second one may arrive.

3. Connection timeout, solved by connection pool

4. Connection pool

Save the consumption caused by each connection to the redis service, and reuse the connected instances.
Manage the behavior of connections through parameters

Connection pool parameters

MaxTotal: Control one pool How many can be allocated jedis Instance, by pool.getResource()To obtain; If the value is-1,Means no restriction; If pool Already assigned MaxTotal individual jedis Instance, then pool The status of is exhausted. 
maxIdle: Control one pool How many states are at most idle(free)of jedis example;
MaxWaitMillis: Mean when borrow One jedis The maximum number of milliseconds to wait for an instance. If the waiting time is exceeded, it will be thrown directly JedisConnectionException;
testOnBorrow: Get one jedis Whether to check the connection availability when the instance is running( ping());If yes true,Then you get jedis All instances are available;

6, Solve inventory problems

1. LUA script

Lua is a small script language. Lua script can be easily called by C/C + + code or call C/C + + functions in turn. Lua does not provide a powerful library. A complete Lua interpreter is only 200k, so Lua is not suitable for developing independent applications, but as an embedded script language.
Many applications and games use LUA as their own embedded scripting language to achieve configurability and scalability.
This includes many game plug-ins or plug-ins such as Warcraft map, world of Warcraft, Bode's gate, angry birds and so on.
https://www.w3cschool.cn/lua/

2. Advantages of LUA script in Redis

Write complex or multi-step redis operations into a script and submit them to redis for execution at one time to reduce the number of repeated connections to redis. Improve performance.
LUA scripts are similar to redis transactions. They have certain atomicity and will not be cut in line by other commands. They can complete some redis transactional operations.
Note that the lua script function of redis can only be used in redis version 2.6 or above.
Use lua script to eliminate users and solve the oversold problem.
After version 2.6 of redis, the contention problem is solved through lua script. In fact, redis uses its single thread feature to solve the multi task concurrency problem by means of task queue.

7, Redis_ Business_ Second kill case_ code

1. Project structure

2. First Edition: simple Edition

Click 10 times, normal second kill
It's normal for students to have a try together. This is because the effect of concurrency cannot be achieved.
Using tool ab to simulate concurrent testing, oversold will occur. A negative number appears when viewing inventory.

3. Second Edition: add transaction optimistic lock (solve oversold), but there are legacy inventory and connection timeout

4. Version 3: connection pool solves timeout problem

5. Fourth Edition: Solving Inventory dependency, LUA script

code

package com.atguigu;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

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);  // ping  PONG
				 
					jedisPool = new JedisPool(poolConfig, "192.168.44.168", 6379, 60000 );
				}
			}
		}
		return jedisPool;
	}

	public static void release(JedisPool jedisPool, Jedis jedis) {
		if (null != jedis) {
			jedisPool.returnResource(jedis);
		}
	}

}

Via lua script

package com.atguigu;

import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.LoggerFactory;

import ch.qos.logback.core.joran.conditional.ElseAction;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.ShardedJedisPool;
import redis.clients.jedis.Transaction;

public class SecKill_redisByScript {
	
	private static final  org.slf4j.Logger logger =LoggerFactory.getLogger(SecKill_redisByScript.class) ;

	public static void main(String[] args) {
		JedisPool jedispool =  JedisPoolUtil.getJedisPoolInstance();
 
		Jedis jedis=jedispool.getResource();
		System.out.println(jedis.ping());
		
		Set<HostAndPort> set=new HashSet<HostAndPort>();

	//	doSecKill("201","sk:0101");
	}
	
	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" ;
			 
	static String secKillScript2 = 
			"local userExists=redis.call(\"sismember\",\"{sk}:0101:usr\",userid);\r\n" +
			" return 1";

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

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

		 //String sha1=  .secKillScript;
		String sha1=  jedis.scriptLoad(secKillScript);
		Object result= jedis.evalsha(sha1, 2, uid,prodid);

		  String reString=String.valueOf(result);
		if ("0".equals( reString )  ) {
			System.err.println("Empty!!");
		}else if("1".equals( reString )  )  {
			System.out.println("Rush purchase succeeded!!!!");
		}else if("2".equals( reString )  )  {
			System.err.println("The user has robbed!!");
		}else{
			System.err.println("Panic buying exception!!");
		}
		jedis.close();
		return true;
	}
}
package com.atguigu;

import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.LoggerFactory;

import ch.qos.logback.core.rolling.helper.IntegerTokenConverter;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.ShardedJedisPool;
import redis.clients.jedis.Transaction;

/**
 *
 */
public class SecKill_redis {

	public static void main(String[] args) {
		Jedis jedis =new Jedis("192.168.44.168",6379);
		System.out.println(jedis.ping());
		jedis.close();
	}

	//Second kill process
	public static boolean doSecKill(String uid,String prodid) throws IOException {
		//1. Judgment of uid and prodid non null
		if(uid == null || prodid == null) {
			return false;
		}

		//2. Connect to redis
		//Jedis jedis = new Jedis("192.168.44.168",6379);
		//Get jedis object through connection pool
		JedisPool jedisPoolInstance = JedisPoolUtil.getJedisPoolInstance();
		Jedis jedis = jedisPoolInstance.getResource();

		//3 splicing key
		// 3.1 inventory key
		String kcKey = "sk:"+prodid+":qt";
		// 3.2 second kill successful user key
		String userKey = "sk:"+prodid+":user";

		//Monitor inventory
		jedis.watch(kcKey);

		//4. Get the inventory. If the inventory is null, the second kill has not started yet
		String kc = jedis.get(kcKey);
		if(kc == null) {
			System.out.println("The second kill hasn't started yet, please wait");
			jedis.close();
			return false;
		}

		// 5 judge whether the user repeats the second kill operation
		if(jedis.sismember(userKey, uid)) {
			System.out.println("The second kill has been successful. You can't repeat the second kill");
			jedis.close();
			return false;
		}

		//6. Judge if the commodity quantity and inventory quantity are less than 1, the second kill is over
		if(Integer.parseInt(kc)<=0) {
			System.out.println("The second kill is over");
			jedis.close();
			return false;
		}

		//7 second kill process
		//Use transaction
		Transaction multi = jedis.multi();

		//Team operation
		multi.decr(kcKey);
		multi.sadd(userKey,uid);

		//implement
		List<Object> results = multi.exec();

		if(results == null || results.size()==0) {
			System.out.println("The second kill failed....");
			jedis.close();
			return false;
		}

		//7.1 inventory-1
		//jedis.decr(kcKey);
		//7.2 add successful users to the list
		//jedis.sadd(userKey,uid);

		System.out.println("The second kill succeeded..");
		jedis.close();
		return true;
	}
}

Keywords: Redis

Added by etsauer on Tue, 18 Jan 2022 16:36:14 +0200