8. Spring boot integrates redis
Import dependency
<!--redis rely on--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <!-- <version>2.1.6.RELEASE</version>--> </dependency> <!--Spring2.x integrate redis what is needed common-pool2--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <!-- <version>2.6.2</version>--> </dependency>
Configure redis information (application.properties)
# REDIS (RedisProperties) # Redis database index (0 by default) spring.redis.database=0 # Redis server address spring.redis.host= # Redis server connection port spring.redis.port= # Redis server connection password (blank by default) spring.redis.password= # Maximum number of connections in the connection pool (negative value indicates no limit) spring.redis.jedis.pool.max-active=8 # Maximum blocking wait time of connection pool (negative value indicates no limit) spring.redis.jedis.pool.max-wait=-1 # Maximum free connections in the connection pool spring.redis.jedis.pool.max-idle=8 # Minimum free connections in connection pool spring.redis.jedis.pool.min-idle=0 # Connection timeout (MS) spring.redis.timeout=5000 # #redis: ## Redis database index (0 by default) #database: 0 ## Redis server address #host: 127.0.0.1 ## Redis server connection port #port: 6379 ## Redis server connection password (blank by default) #password: ## Maximum number of connections in the connection pool (negative value indicates no limit) #jedis: #pool: #max-active: 8 ## Maximum blocking wait time of connection pool (negative value indicates no limit) #max-idle: 8 ## Maximum free connections in the connection pool #max-wait: -1 ## Minimum free connections in connection pool #min-idle: 0 ## Connection timeout (MS) #timeout: 0
Configure redis tool class
package com.example.springbootredis.util; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration public class RedisConfig extends CachingConfigurerSupport { @Bean @SuppressWarnings("all") public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory){ RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(factory); Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = this.jackson2JsonRedisSerializer(); //String serialization StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); //The key is serialized by string template.setKeySerializer(stringRedisSerializer); //The key of hash is serialized by string template.setHashKeySerializer(stringRedisSerializer); //jackson is also used for value serialization template.setValueSerializer(jackson2JsonRedisSerializer); //The value of hash is also jackson template.setHashValueSerializer( jackson2JsonRedisSerializer); template.afterPropertiesSet(); return template; } /** * Custom jackson2JsonRedisSerializer object * @return */ private Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer() { Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); objectMapper.configure(MapperFeature.USE_ANNOTATIONS, false); objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); // This item must be configured, otherwise it will report Java lang.ClassCastException: java. util. LinkedHashMap cannot be cast to XXX objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL , JsonTypeInfo.As.PROPERTY); objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); jackson2JsonRedisSerializer.setObjectMapper(objectMapper); return jackson2JsonRedisSerializer; } }
Control layer
package com.example.springbootredis.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @author wzh * @date 2021/12/22 10:47 */ @RestController @RequestMapping("/redisTest") public class RedisTestController { @Autowired private RedisTemplate redisTemplate; @GetMapping public String testRedis(){ //Set value to redis redisTemplate.opsForValue().set("name","lucy"); //Get value from redis String name = (String) redisTemplate.opsForValue().get("name"); return name; } }
9. Redis transaction operation
Redis transaction definition
Redis transaction is a separate isolation operation: all commands in the transaction will be serialized and executed sequentially. During the execution of the transaction, it will not be interrupted by command requests sent by other clients.
The main function of Redis transaction is to concatenate multiple commands to prevent other commands from jumping in the queue
Multi,Exec,discard
Starting from entering the Multi command, the entered commands will enter the command queue in turn, but will not be executed. After entering Exec, Redis will execute the commands in the previous command queue in turn
In the process of team formation, you can give up team formation through discard
Error handling of case transactions
Team successfully submitted
An error is reported in the queue forming stage, and the submission fails: when an error occurs in a command in the queue, all queues of the whole queue will be cancelled during execution
Teaming success and submission failure: if an error is reported in a command in the execution phase, only the error reported command will not be executed, and other commands will be executed
Transaction conflict problem
Pessimistic lock
Every time I go to get the data, I think others will modify it, so I lock it every time I get the data, so that others will block the data until they get the lock. Many such locking mechanisms are used in traditional relational databases, such as row lock, table lock, read lock and write lock, which are locked before operation
Optimistic lock
Every time I go to get the data, I think others will not modify it, so it will not be locked. However, when updating, I will judge whether others have updated the whole data during this period. I can use mechanisms such as version number. Optimistic locking is suitable for multi read applications, which can improve throughput. Redis uses this check and set mechanism to implement transactions
WATCH key [key ...]
Before executing multi, execute watch key1 [key2] to listen to one (or more) keys. If this (or these) key is changed by other commands before the transaction is executed, the transaction will be interrupted
unwatch
Cancel the monitoring of all key s by the watch command
If the EXEC command or DISCARD command is executed first after the watch command is executed, there is no need to execute UNWATCH.
Redis transaction three features
-
Separate isolation operation
All commands in a transaction are serialized and executed sequentially. During the re execution of the transaction, it will not be interrupted by the command request sent by other clients.
-
There is no concept of isolation level
The commands in the queue will not be actually executed until the transaction is committed, because any instructions will not be actually executed until the transaction is committed
-
Atomicity is not guaranteed
If a command fails to execute in a transaction, the subsequent commands will still be executed without rollback
10. Second kill case
Stand alone version
package com.example.springbootredis.controller; import org.springframework.beans.factory.annotation.Autowired; import redis.clients.jedis.Jedis; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.io.IOException; /** * @author wzh * @date 2021/12/22 10:47 */ @RestController @RequestMapping("/redisTest") public class RedisTestController { @Autowired private RedisTemplate redisTemplate; @GetMapping public String testRedis(){ //Set value to redis redisTemplate.opsForValue().set("name","lucy"); //Get value from redis String name = (String) redisTemplate.opsForValue().get("name"); return name; } //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. Everyone's privacy is not shown Jedis jedis = new Jedis("*****", &&&&&); jedis.auth("****"); //3 splicing key //3.1 inventory key String kcKey = "sk:"+prodid+":qt"; //3.2 second kill successful user key String userKey = "sk:" + prodid+":user"; //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 has not started yet. The request failed"); 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 //7.1 inventory-1 jedis.decr(kcKey); //7.2 add successful users to the list jedis.sadd(userKey,uid); System.out.println("Second kill success"); jedis.close(); return true; } }
test
@Test public void test1() throws IOException { Random random = new Random(); int j; for (int e = 0; e < 10; e++) { String uid = ""; for (int i = 0; i <10 ; i++) { j = random.nextInt(10); uid += j; } RedisTestController.doSecKill(uid,"0101"); } }
Second kill concurrent resolution
Install ab
Install on ECS
yum install httpd-tools ab --help Ask for help and you can see many ways to use it
Install this machine on the official website and configure environment variables
https://www.apachehaus.com/cgi-bin/download.plx
Use ab
ab -c concurrency Concurrency Number of multiple requests to make at a time The number of concurrent multiple requests issued at one time ab -n requests request Number of requests to perform Number of requests executed -p postfile Publish file File containing data to POST. Remember also to set -T Include to POST File of data. Also remember the settings-T -T content-type Content-type header to use for POST/PUT data, eg. 'application/x-www-form-urlencoded'Default is 'text/plain'be used for POST/PUT The content type header of the data, for example. “ application/ x-www-form-urlencoded"The default is text/"Normal" ab -n 1000 -c 100 -p /home/test/postfile -T application/x-www-form-urlencoded http://172.16.17.225:8080/redisTest/doSecKill
ab -n 1000 -c 100 -p /home/test/postfile -T application/x-www-form-urlencoded localhost:8080/redisTest/doSecKill
Connection timeout problem handling
After increasing the concurrency of ab test and the total number of requests, there will be a connection timeout problem due to optimistic lock. The first point does not arrive in seconds, and the second point grabs it.
processing method
1. Create JedisPoolUtils tool class
package com.example.springbootredis.util; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; public class JedisPoolUtils { private static volatile JedisPool jedisPool = null; private JedisPoolUtils(){ } public static JedisPool getJedisPoolInstance() { if(null == jedisPool) { synchronized (JedisPoolUtils.class) { if(null == jedisPool) { JedisPoolConfig poolConfig = new JedisPoolConfig(); poolConfig.setMaxIdle(32); poolConfig.setMaxWaitMillis(100*1000); poolConfig.setTestOnBorrow(true); jedisPool = new JedisPool(poolConfig,"8.130.16.99",7777,60000,"123qwe"); } } } return jedisPool; } public static void release(JedisPool jedisPool, Jedis jedis) { if(null != jedis) { jedisPool.returnResource(jedis); } } }
2. Get jedis object using connection pool
//Get jedis object through connection pool JedisPool jedisPoolInstance = JedisPoolUtils.getJedisPoolInstance(); Jedis jedis = jedisPoolInstance.getResource();
Oversold problem
View inventory as - 190
resolvent
1. Monitor inventory
jedis.watch(kcKey);
2. Use transaction
//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; }
Solve inventory problems
LUA script
Lua is a small script language that can be called by C + + code or 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 applications independently, but an embedded script language.