Introduction and comparison of a common redis client
Jedis api online web address: http://tool.oschina.net/uploads/apidocs/redis/clients/jedis/Jedis.html
Redisson website address: https://redisson.org/
redisson git project address: https://github.com/redisson/redisson
Letuce website address: https://lettuce.io/
Letuce git project address: https://github.com/lettuce-io/lettuce-core
First, after spring boot2, support for redis connections defaulted to lettuce. This explains to some extent the advantages and disadvantages of lettuce and Jedis.
Concepts:
Jedis: Old brand Redis Of Java Implementing the client provides a more comprehensive Redis Command support, Redisson: Distributed and scalable Java Data structure. Lettuce: senior Redis Client, for thread-safe synchronization, asynchronous and responsive use, supports clustering, Sentinel,Pipelines and coders.
Advantage:
Jedis: More comprehensive offers Redis Operational characteristics Redisson: Promote users to Redis Separation of concerns, providing many distributed related operations services, such as distributed locks, distributed collections, through Redis Supports Delayed Queues Lettuce: Be based on Netty The event-driven communication layer of the framework whose method calls are asynchronous. Lettuce Of API Thread-safe, so you can operate on a single Lettuce Connect to complete various operations
Scalable:
Jedis: Use blocked I/O,And its method calls are synchronous, the program flow needs to wait sockets Finished processing I/O Can be executed, asynchronous is not supported. Jedis Client instances are not thread-safe, so they need to be used through connection pools Jedis. Redisson: Be based on Netty The event-driven communication layer of the framework whose method calls are asynchronous. Redisson Of API Thread-safe, so you can operate on a single Redisson Connect to complete various operations Lettuce: Be based on Netty The event-driven communication layer of the framework whose method calls are asynchronous. Lettuce Of API Thread-safe, so you can operate on a single Lettuce Connect to complete various operations lettuce Ability to support redis4,Need java8 And above. lettuce Is based on netty Realized and redis Make synchronous and asynchronous communication.
Letuce vs. jedis:
jedis Make Direct Connection redis server,If it is non-thread safe in a multi-threaded environment, then only the connection pool is used for each jedis Instances add physical connections; lettuce Connections are based on Netty Of the connection instance ( StatefulRedisConnection)It can be accessed concurrently across multiple threads. StatefulRedisConnection Thread-safe, so a connection instance can accommodate concurrent access in a multi-threaded environment, which is also scalable design. If a connection instance is not enough, you can add it as needed. Redisson Distributed and scalable Java Data structure, and Jedis It is simpler, does not support string operations, does not support sorting, transactions, pipelines, partitions, etc. Redis Characteristic. Redisson The purpose is to promote user pairing Redis Separate concerns allow users to focus more on their business logic.
Summary:
priority of use Lettuce,If you need advanced distributed features such as distributed locks, distributed collections, etc., add Redisson Use in combination because Redisson Operations on strings are poorly supported by themselves. In some high concurrent scenarios, such as second-hand killing, ticket-grabbing, and purchasing, there is competition for core resources, commodity inventory, poor control, inventory may be reduced to a negative number, oversold, or a unique increase ID,Because web Applications are deployed on multiple machines and simple synchronous locking is not possible. For high concurrency, 1000/s Concurrent, databases can change from row locks to table locks, which can cause severe performance degradation. Relatively, redis Distributed locks are a relatively good choice. redis Officially recommended Redisson Distributed locks and related services are provided.
Some Java client access is listed on the official website, including Jedis/Redisson/Jredis/JDBC-Redis, where Jedis and Redisson are officially recommended. Common Jedis.
Two SpringBoot Integration Jedis
brief introduction
When we use springboot to build micro-services, we still need redis cache to cache some data and store some high-frequency accessed data in many cases. It is more difficult to use redis directly. Here, we use jedis to implement redis cache for efficient caching
Introducing Jedis dependencies
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency>
Because the jedis version is referenced by default in SpringBoot.
So we don't need to configure the version number of jedis to introduce the jedis dependency directly.
application.yml
For example, in application. The following information is configured in yml:
spring: redis: port: 6379 password: guoweixin host: 192.168.20.135 jedis: pool: max-idle: 6 #Maximum number of idles max-active: 10 #maximum connection min-idle: 2 #Minimum number of idles timeout: 2000 #connection timed out
Write Config
Create a class: com.qfjy.config.jedis.JedisConfig
package com.qfjy.config.jedis; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; /** * @ClassName JedisConfig * @Description TODO * @Author guoweixin * @Version 1.0 */ @Configuration public class JedisConfig { private Logger logger = LoggerFactory.getLogger(JedisConfig.class); @Value("${spring.redis.host}") private String host; @Value("${spring.redis.port}") private int port; @Value("${spring.redis.password}") private String password; @Value("${spring.redis.timeout}") private int timeout; @Value("${spring.redis.jedis.pool.max-active}") private int maxActive; @Value("${spring.redis.jedis.pool.max-idle}") private int maxIdle; @Value("${spring.redis.jedis.pool.min-idle}") private int minIdle; @Bean public JedisPool jedisPool(){ JedisPoolConfig jedisPoolConfig=new JedisPoolConfig(); jedisPoolConfig.setMaxIdle(maxIdle); jedisPoolConfig.setMinIdle(minIdle); jedisPoolConfig.setMaxTotal(maxActive); JedisPool jedisPool=new JedisPool(jedisPoolConfig,host,port,timeout,password); logger.info("JedisPool Connection Successful:"+host+"\t"+port); return jedisPool; } }
Test Configuration
@SpringBootTest public class JedisApplicationTests { @Autowired private JedisPool jedisPool; @Test public void contextLoads() { System.out.println(jedisPool); //Get a Jedis connection in the connection pool Jedis jedis=jedisPool.getResource(); jedis.set("haha","Hello"); jedis.set("name","guoweixin"); //Close the current connection jedis.close(); }
Encapsulation Tool Class
JedisUtil
package com.qfjy.config.jedis; import org.springframework.beans.factory.annotation.Autowired; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; /** * @ClassName JedisUtils * @Description TODO * @Author guoweixin * @Version 1.0 */ @Component public class JedisUtils { @Autowired private JedisPool jedisPool; /** * Get Jedis Resources */ public Jedis getJedis(){ return jedisPool.getResource(); } /** * Release Jedis Connection */ public void close(Jedis jedis){ if(jedis!=null){ jedis.close(); } } ....................... }
test
Jedis Operation String Type
Business JedisServiceImpl Class
/** * @ClassName JedisServiceImpl * @Description TODO * @Author guoweixin * @Version 1.0 */ @Service @Log //Log Processing public class JedisServiceImpl { @Autowired private JedisUtils jedisUtils; /** * Test String * Query value based on key */ public String getString(String key){ Jedis jedis=jedisUtils.getJedis(); String val=null; if(!jedis.exists(key)){ val="QianFeng Nanjing"; log.info(key+"stay MYSQL The results of a query in the database are:"+val); jedis.set(key,val); log.info(key+"Save in Redis Medium. Value is:"+val); }else{ val=jedis.get(key); log.info(key+"Yes Redis Data queried in. Value is:"+val); } jedisUtils.close(jedis); //Release Resources return val; } }
unit testing
@SpringBootTest public class JedisTests { @Autowired private JedisServiceImpl jedisService; @Test void t1(){ String val= jedisService.getString("name"); System.out.println(val); } }
Jedis operation Hash type
Business JedisServiceImpl Class
@Service @Log public class JedisServiceImpl { @Autowired private JedisUtils jedisUtils; /** * Testing the jedis operation hash type * Query user information based on user ID * First determine if there is a Redis. * If it does not exist, query in the database. Coexist in Redis * If so, query Redis directly and return */ public User selectBy(String id){ String key="user:id"; //Generate key s for the same specification based on Rules User user=new User();; Jedis jedis=jedisUtils.getJedis(); if(!jedis.exists(key)){ //Query in database and store user.setId(id); user.setName("QianFeng Nanjing JAVA"); user.setAge(20); log.info("The user information queried in the database is:"+user); Map<String,String> map=new HashMap(); map.put("id",user.getId()); map.put("name",user.getName()); jedis.hset(key,map); log.info(key+"Save Successfully Redis:"+user); } else{ Map<String,String> map= jedis.hgetAll(key); user.setId(map.get("id")); user.setName(map.get("name")); log.info(key+"Redis Queried out in:"+map); } jedisUtils.close(jedis); return user; } }
unit testing
@SpringBootTest public class JedisTests { @Autowired private JedisServiceImpl jedisService; @Test void hash(){ User user= jedisService.selectBy("1001"); System.out.println(user); } }
Three SpringBoot2. redis in X (lettuce)
Jedis -"is further encapsulated. --" RedisTemplate
JDBCTemplate
RestTemplate
java Code Operation Redis,Need to use Jedis,that is redis Support java Third Party Class Library Be careful:Jedis2.7 Cluster operations are supported in versions above
maven configuration
New SpringBoot2.0.3 WEB Project, in MAVEN pom. Add the following dependencies to the XML file
<dependencies> <!--Default is lettuce Client--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!-- redis rely on commons-pool This dependency must be added --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency> <!-- Test Library Dependency --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
hibernate–>JPA–>SpringData
Profile Configuration
application.yml
spring: redis: port: 6379 password: guoweixin host: 192.168.20.135 lettuce: pool: max-active: 8 # Maximum number of connections in connection pool (use a negative value to indicate no limit) max-idle: 8 # Maximum idle connection in connection pool min-idle: 0 # Minimum idle connection in connection pool max-wait: 1000 # Maximum blocking wait time for connection pool (use a negative value to indicate no limit) shutdown-timeout: 100 # Close timeout
redis configuration class
JdbcTemplate ->JDBC is further encapsulated.
RedisTemplate ->redis is further encapsulated (lettuce)
brief introduction
Write cache configuration class RedisConfig to tune cache default configuration, RedisTemplate < String, Object > is more type compatible
You can see that in the redisTemplate() method, Jackson JsonRedisSerializer replaces Redis's default serialization: JdkSerializationRedisSerializer
There are several serialized classes in spring-data-redis:
GenericToStringSerializer: Any object can be generalized to character creation and serialization
Jackson 2JsonRedisSerializer: Serialize Object object to json character creation (same as Jackson JsonRedisSerializer)
JdkSerializationRedisSerializer: Serialize java objects
String RedisSerializer: Simple string serialization
JdkSerializationRedisSerializer Serialization Serializer Serialized object must implement Serializable interface. Serialized object has other content besides attribute content. It is long and difficult to read. By default, this serialization method is used
The contents of the storage are as follows:
"\xac\xed\x00\x05sr\x00!com.oreilly.springdata.redis.User\xb1\x1c \n\xcd\xed%\xd8\x02\x00\x02I\x00\x03ageL\x00\buserNamet\x00\x12Ljava/lang/String;xp\x00\x00\x00\x14t\x00\x05user1"
Jackson Json RedisSerializer serialization, serialized object does not need to implement Serializable interface, serialized result is clear, easy to read, less bytes stored, fast
The contents of the storage are as follows:
"{"userName":"guoweixin","age":20}"
String RedisSerializer Serialization
In general, if the key and value are string strings, you can just use this
RedisConfig class
package com.qfjy.config; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.cache.RedisCacheConfiguration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.cache.RedisCacheWriter; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import org.springframework.data.redis.serializer.StringRedisSerializer; import java.lang.reflect.Method; /** * @ClassName RedisConfig * @Description TODO * @Author guoweixin * @Version 1.0 */ @Configuration public class RedisConfig extends CachingConfigurerSupport { /** * Customize the cache key generation policy. The default build strategy is to make configuration injections that you don't understand (scrambled content) through Spring's Dependency Injection feature and that this class is a configuration class that can be more customized * * @return */ @Bean @Override public KeyGenerator keyGenerator() { return new KeyGenerator() { @Override public Object generate(Object target, Method method, Object... params) { StringBuilder sb = new StringBuilder(); sb.append(target.getClass().getName()); sb.append(method.getName()); for (Object obj : params) { sb.append(obj.toString()); } return sb.toString(); } }; } /** * Cache Configuration Manager */ @Bean public CacheManager cacheManager(LettuceConnectionFactory factory) { //Create RedisCacheWriter object as lock write RedisCacheWriter writer = RedisCacheWriter.lockingRedisCacheWriter(factory); //Create default cache configuration object RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig(); RedisCacheManager cacheManager = new RedisCacheManager(writer, config); return cacheManager; } @Bean public RedisTemplate<String,Object> redisTemplate(LettuceConnectionFactory factory){ RedisTemplate<String,Object> template = new RedisTemplate <>(); template.setConnectionFactory(factory); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); // Configure how hashKey and hashValue are serialized when RedisTemplate is returned using the annotation @Bean. // key uses String serialization template.setKeySerializer(stringRedisSerializer); // value serialization using jackson template.setValueSerializer(jackson2JsonRedisSerializer); // hash's key also uses String's serialization template.setHashKeySerializer(stringRedisSerializer); // hash's value serialization uses jackson template.setHashValueSerializer(jackson2JsonRedisSerializer); template.afterPropertiesSet(); return template; } }
Code Samples
RedisServiceImpl
Test String Type
@Service @Log public class RedisServiceImpl { @Autowired private RedisTemplate<String,Object> redisTemplate; /** * Plain Cache * @param key key * @return true Successful false failed */ public String getString(String key) { if(redisTemplate.hasKey(key)) { log.info("Redis Query in"); return (String) redisTemplate.opsForValue().get(key); }else{ String val="guoweixin"; redisTemplate.opsForValue().set(key, val); log.info("Queried in the database"); return val; } } /** * Normal Cache Placement * @param key key * @param value value * @param expireTime Timeout (seconds) * @return true Successful false failed */ public Boolean set(String key, Object value, int expireTime) { try { redisTemplate.opsForValue().set(key, value, expireTime, TimeUnit.SECONDS); return true; } catch (Exception e) { e.printStackTrace(); return false; } } }
Test Hash Type
@Service @Log public class RedisServiceImpl { @Autowired private RedisTemplate<String,Object> redisTemplate; @Resource(name="redisTemplate") private HashOperations<String,String,User> hash; /** * Determine if the key exists, and if it exists, query in Redis * If it does not exist, query in MYSQL and get the result, adding it to Redis Hash * @param id * @return */ public User selectUserById1(String id){ if(hash.hasKey("user",id)){ log.info("Redis Query Objects in"); return hash.get("user",id); }else{ User u=new User(); u.setId(id); u.setName("guoweixin"); u.setAge(22); log.info("mysql Query Objects in"); hash.put("user",id,u); return u; } } }
hash Type Code Example
package com.qfjy.redis.demo.service.impl; import com.qfjy.redis.demo.service.HashCacheService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.Cursor; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ScanOptions; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; @Service("hashCacheService") public class HashCacheServiceImpl implements HashCacheService { private final static Logger log = LoggerFactory.getLogger(HashCacheServiceImpl.class); @Autowired private RedisTemplate<String, Object> redisTemplate; /** * Get a value in MAP * @param key key * @param item term * @return value */ public Object hget(String key, String item) { return redisTemplate.opsForHash().get(key, item); } /** * Get all keys corresponding to hashKey * @param key key * @return Corresponding multiple key values */ public Map <Object, Object> hmget(String key) { return redisTemplate.opsForHash().entries(key); } /** * Add key-value pairs as map sets * @param key key * @param map Corresponds to multiple key values * @return true Successful false failed */ public boolean hmset(String key, Map <String, Object> map) { try { redisTemplate.opsForHash().putAll(key, map); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * HashSet And set the time * @param key key * @param map Corresponds to multiple key values * @param time Time (seconds) * @return true Successful false failed */ public boolean hmset(String key, Map <String, Object> map, long time) { try { redisTemplate.opsForHash().putAll(key, map); if (time > 0) { expire(key, time); } return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * Put data into a hash table and create it if it doesn't exist * @param key key * @param item term * @param value value * @return true Successful false failed */ public boolean hset(String key, String item, Object value) { try { redisTemplate.opsForHash().put(key, item, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * Put data into a hash table and create it if it doesn't exist * @param key key * @param item term * @param value value * @param time Time (seconds) Note: If the existing hash table has time, this will replace the original time * @return true Successful false failed */ public boolean hset(String key, String item, Object value, long time) { try { redisTemplate.opsForHash().put(key, item, value); if (time > 0) { expire(key, time); } return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * Delete values from hash table * @param key Key cannot be null * @param item Item can make multiple cannot be null */ public void hdel(String key, Object... item) { redisTemplate.opsForHash().delete(key, item); } /** * Determine if there is a value for this item in the hash table * @param key Key cannot be null * @param item Item cannot be null * @return true Existence false does not exist */ public boolean hHasKey(String key, String item) { return redisTemplate.opsForHash().hasKey(key, item); } /** * hash Increment, if it does not exist, creates one and returns the added value * @param key key * @param item term * @param by To add a few (greater than 0) * @return */ public long hincr(String key, String item, long by) { return redisTemplate.opsForHash().increment(key, item, by); } /** * hash Decreasing * @param key key * @param item term * @param by To reduce memory (less than 0) * @return */ public long hdecr(String key, String item, long by) { return redisTemplate.opsForHash().increment(key, item, -by); } /** * Gets the hashMap value in the specified variable. * @param key * @return Return LIST Object */ @Override public List<Object> values(String key) { return redisTemplate.opsForHash().values(key); } /** * Gets the key in the variable. * @param key * @return Return SET Collection */ @Override public Set<Object> keys(String key) { return redisTemplate.opsForHash().keys(key); } /** * Gets the length of the variable. * @param key key * @return Return Length */ @Override public long size(String key) { return redisTemplate.opsForHash().size(key); } /** * Gets the value in a variable as a collection. * @param key * @param list * @return Returns the LIST set value */ @Override public List multiGet(String key, List list) { return redisTemplate.opsForHash().multiGet(key,list); } /** * If the value of a variable exists, you can add key-value pairs that do not exist to the variable * If the variable does not exist, add a new variable and add key-value pairs to it. * @param key * @param hashKey * @param value */ @Override public void putIfAbsent(String key, String hashKey, Object value) { redisTemplate.opsForHash().putIfAbsent(key,hashKey,value); } /** * Match to get key-value pairs, ScanOptions.NONE To get all the key pairs, ScanOptions.scanOptions().match("map1").build() * Matching obtains the key-value pairs of the key bit map1 and cannot be mismatched. * @param key * @param options * @return */ @Override public Cursor<Map.Entry<Object, Object>> scan(String key, ScanOptions options) { return redisTemplate.opsForHash().scan(key,options); } /** * Deleting key-value pairs from a variable can pass in multiple parameters and delete multiple key-value pairs. * @param key key * @param hashKeys MAP KEY in */ @Override public void delete(String key, String... hashKeys) { redisTemplate.opsForHash().delete(key,hashKeys); } public boolean expire(String key, long seconds) { return redisTemplate.expire(key,seconds,TimeUnit.SECONDS); } /** * delete * @param keys */ @Override public void del(String... keys) { if (keys != null && keys.length > 0) { if (keys.length == 1) { redisTemplate.delete(keys[0]); } else { redisTemplate.delete(CollectionUtils.arrayToList(keys)); } } } @Override public long getExpire(String key) { return 0; } }