SpringBoot提供了对redis继承的组件包:spring-boot-starter-data-redis,依赖于spring-data-redis和lettuce,SpringBoot1.0默认使用的是Jedis客户端,2.0换成了Lettuce。
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-pool2artifactId>
dependency>
commons-pool2在使用Lettuce的时候用来创建Redis连接池
# Redis 数据库索引(默认为 0)
spring.redis.database=0
# Redis 服务器地址
spring.redis.host=localhost
# Redis 服务器连接端⼝
spring.redis.port=6379
# Redis 服务器连接密码(默认为空)
spring.redis.password=
# 连接池最⼤连接数(使⽤负值表示没有限制) 默认 8
spring.redis.lettuce.pool.max-active=8
# 连接池最⼤阻塞等待时间(使⽤负值表示没有限制) 默认 -1
spring.redis.lettuce.pool.max-wait=-1
# 连接池中的最⼤空闲连接 默认 8
spring.redis.lettuce.pool.max-idle=8
# 连接池中的最⼩空闲连接 默认 0
spring.redis.lettuce.pool.min-idle=0
在这里可以为Redis设置一些全局配置,比如配置主键的生产策略KeyGenerator,如不配置会默认使用参数名作为主键。
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {
@Override
@Bean
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 param : params) {
sb.append(param.toString());
}
return sb.toString();
}
}
}
}
在单元测试中注入RedisTemplate,String是一种最常用的数据类型,普通的key/value存储都可以归为此类,value不仅是String,也可以是数字。
@RunWith(SpringRunner.class)
@SpringBootTest
class RedisConfigTest {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Test
public void testString(){
redisTemplate.opsForValue().set("Hello","world");
Assert.assertEquals("world",redisTemplate.opsForValue().get("Hello"));
}
}
@Test
public void testObj(){
User user = new User("[email protected]", "LiMing", "smile", 18);
ValueOperations<String,User> valueOperations = redisTemplate.opsForValue();
valueOperations.set("com.example",user);
User u = valueOperations.get("com.example");
System.out.println("user: "+u.toString());
}
Redis在存入每一个数据的时候都可以设置一个超时时间,过了这个时间就会自动删除数据,这种特性非常适合我们对阶段数据的缓存。
@Test
public void testExpire() throws InterruptedException {
User user = new User("[email protected]", "LiMing", "smile", 18);
ValueOperations<String,User> valueOperations = redisTemplate.opsForValue();
valueOperations.set("com.example",user,100, TimeUnit.MILLISECONDS);
Boolean exists;
Thread.sleep(50);
exists = redisTemplate.hasKey("com.example");
System.out.println(exists?"it is exists":"it is not exists");
Thread.sleep(1000);
exists = redisTemplate.hasKey("com.example");
System.out.println(exists?"it is exists":"it is not exists");
}
redisTemplate.delete("deletekey");
一般我们存储一个键,很自然的就会使用get/set去存储,实际上这并不很好的做法,redis存储一个key会有一个很小的内存,不管你存的这个键有多小,都不会低于这个内存,因此合理的使用Hash可以帮我们节省很多内存。
Hash Set就在哈希表Key中的域的值设为value,如果Key不存在,一个新的哈希表被创建并进行HSET操作,如果域存在于哈希表,旧值将被覆盖。
@Test
public void testHash(){
HashOperations<String,Object,Object> hash = redisTemplate.opsForHash();
hash.put("hash","hello","world");
String value =(String) hash.get("hash", "hello");
System.out.println("hash value :"+value);
}
使用List可以轻松实现一个队列,List典型的应用场景就是消息队列,可以利用List和Push操作,将任务存在List中,然后工作线程再用POP操作将任务取出进行执行。
@Test
public void testList(){
ListOperations<String,String> list = redisTemplate.opsForList();
list.leftPush("list","it");
list.leftPush("list","you");
list.leftPush("list","know");
String value = (String) list.leftPop("list");
System.out.println(value.toString());
}
List 还有很多操作API
Redis List的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销,Redis内部的很多实现,包括发送缓冲队列等也都是用这个数据结构。
当你需要一个存储列表数据,又不希望出现重复数据时,Set是一个很好地选择,并且Set提供了判断某个成员是否在一个Set集合内的重要接口,这个也是List不能提供的。
@Test
public void testSet(){
String key = "set";
SetOperations<String,String> set = redisTemplate.opsForSet();
set.add(key,"it");
set.add(key,"you");
set.add(key,"you");
set.add(key,"know");
Set<String> members = set.members(key);
for (String member : members) {
System.out.println("set value :" + member);
}
}
@Test
public void testSet(){
String key1 = "setMore1";
String key2 = "setMore2";
SetOperations<String,String> set = redisTemplate.opsForSet();
set.add(key1,"it");
set.add(key1,"you");
set.add(key1,"you");
set.add(key1,"know");
set.add(key2,"xx");
set.add(key2,"know");
Set<String> members = set.difference(key1,key2);
Set<String> members1 = set.difference(key2,key1);
for (String member : members) {
System.out.println("set value :" + member);
}
for (String member : members1) {
System.out.println("set value :" + member);
}
}
Redis Sorted Set的使用场景与Set类似,区别是Set不是自动有序的,而Sorted Set可以通过用户额外提供一个优先级参数来为成员排序,并且是插入有序,即自动排序。
可以实现有权重的队列;
@Test
public void testSet(){
String key1 = "zset";
ZSetOperations<String,String> set = redisTemplate.opsForZSet();
set.add(key1,"it",1);
set.add(key1,"you",3);
set.add(key1,"you",2);
set.add(key1,"know",0);
set.add(key1,"xx",1);
set.add(key1,"know",6);
Set<String> zsets = set.range(key1, 0, 3);
for (String zset : zsets) {
System.out.println("zset value: "+zset);
}
System.out.println("******************************");
Set<String> zsetb = set.rangeByScore(key1, 0, 3);
for (String s : zsetb) {
System.out.println("zsetB value: "+s);
}
}
Redis Sorted Set 的内部使⽤ HashMap 和跳跃表(SkipList)来保证数据的存储和有序,HashMap ⾥
放的是成员到 Score 的映射,⽽跳跃表⾥存放的是所有的成员,排序依据是 HashMap ⾥存的 Score,
使⽤跳跃表的结构可以获得⽐较⾼的查找效率,并且在实现上⽐较简单。
在实际使用过程中,不会给每一个使用的类都注入redisTemplate来直接使用,一般都会对业务进行简单包装,最后提供出来对外使用。
封账简单插入操作:
@Service
public class RedisService {
@Autowired
private RedisTemplate redisTemplate;
// 封装set操作
public boolean set(final String key,Object value){
boolean result = false;
try {
ValueOperations<Serializable,Object> operation = redisTemplate.opsForValue();
operation.set(key,value);
result = true;
}catch (Exception e){
// 日志打印
}
return result;
}
// 匹配格式删除
public void removePattern(final String pattern){
Set<Serializable> keys = redisTemplate.keys(pattern);
if (keys.size()>0){
redisTemplate.delete(keys);
}
}
}