Redis
1 Redis中的数据结构
![image-20201204114339215](Redis day02.assets/image-20201204114339215.png)
传统键值存储是关联字符串值到字符串键,但是 Redis 的值不仅仅局限于简单字符串,还可以持有更复杂的数据结构。下面列的是 Redis 支持的所有数据结构,后面将逐一介绍:
- String(字符串)
- List(列表)
- Set(集合)
- Hash(哈希,键值对集合)
- SortedSet(zset,排序集合)
1.1 String(字符串)
应用场景:粉丝数,投票数
Redis中的String,除了字符串外,还可以存:储数字以及二进制数据。
增:
127.0.0.1:6379> set name zhangsan
OK
127.0.0.1:6379> set age 18
OK
批量添加
127.0.0.1:6379> mset score 100.0 sex M
OK
删:
127.0.0.1:6379> del name
(integer) 1
127.0.0.1:6379> get name
(nil)
改:
数字自增
127.0.0.1:6379> incr age
(integer) 19
指定自增的数值
127.0.0.1:6379> incrby age 2
(integer) 21
数字自减
127.0.0.1:6379> decr age
(integer) 20
指定自减的数值
127.0.0.1:6379> decrby age 2
(integer) 18
查:
127.0.0.1:6379> get age
"18"
批量获取
127.0.0.1:6379> mget score sex
1) "100.0"
2) "M"
127.0.0.1:6379> set name zhangsan
OK
获取值的长度
127.0.0.1:6379> strlen name
(integer) 5
1.2 List(列表)
应用场景:排队。队列
增:
向mylist列表尾部添加多个数据
127.0.0.1:6379> rpush mylist xiao1hei xiao2hei xiao3hei
(integer) 3
向mylist列表头部添加多个数据
127.0.0.1:6379> lpush mylist xiao0hei xiao-1hei
(integer) 5
查:
127.0.0.1:6379> llen mylist
(integer) 5
127.0.0.1:6379> lrange mylist 0 4
1) "xiao-1hei"
2) "xiao0hei"
3) "xiao1hei"
4) "xiao2hei"
5) "xiao3hei"
查看全部,固定写法
lrange mylist 0 -1
127.0.0.1:6379> lindex mylist 2
"xiao-1hei"
删:
127.0.0.1:6379> lpop mylist
"xiao-1hei"
127.0.0.1:6379> rpop mylist
"xiao3hei"
//lrem list count value
//从list中删除count个value,
//count=0删除全部 count>0从左开始删除 count<0从右开始删除
127.0.0.1:6379> rpush mylist xiao0hei xiao0hei xiao0hei
(integer) 6
127.0.0.1:6379> lrem mylist 3 xiao0hei
(integer) 3
改:
127.0.0.1:6379> lset mylist 0 xiaoheihei
OK
127.0.0.1:6379> lindex mylist 0
"xiaoheihei"
1.3 Set(集合)
应用场景:共同好友
增:
127.0.0.1:6379> sadd myset a b
(integer) 2
127.0.0.1:6379> sadd myset b c d
(integer) 2
查:
127.0.0.1:6379> smembers myset
1) "d"
2) "c"
3) "b"
4) "a"
删:
删除元素
127.0.0.1:6379> srem myset a
(integer) 1
127.0.0.1:6379> sismember myset a
(integer) 0
集合间的运算:
127.0.0.1:6379> sadd myset1 a b c
(integer) 3
127.0.0.1:6379> sadd myset2 b d e
(integer) 3
127.0.0.1:6379> sadd myset3 c d f
(integer) 3
myset1: a b c
myset2: b d e
myset3: c d f
获取多个集合间的差集
127.0.0.1:6379> sdiff myset1 myset2
1) "c"
2) "a"
127.0.0.1:6379> sdiff myset2 myset1
1) "d"
2) "e"
127.0.0.1:6379> sdiff myset1 myset2 myset3
1) "a"
获取多个集合间的交集
127.0.0.1:6379> sinter myset1 myset2
1) "b"
127.0.0.1:6379> sinter myset2 myset3
1) "d"
127.0.0.1:6379> sinter myset1 myset2 myset3
(empty list or set)
获取多个集合间的并集
127.0.0.1:6379> sunion myset1 myset2 myset3
1) "d"
2) "b"
3) "e"
4) "f"
5) "c"
6) "a"
1.4 Hash(键值对集合)
应用场景:存储有关联的数据,对象的另一种存放。
增:
127.0.0.1:6379> hset myhash name zhangsan
(integer) 1
127.0.0.1:6379> hmset myhash age 18 sex m
OK
不存在某个key时才添加
127.0.0.1:6379> hsetnx myhash name lisi
(integer) 0
127.0.0.1:6379> hsetnx myhash phone 18530031576
(integer) 1
查:
127.0.0.1:6379> hget myhash name
"zhangsan"
127.0.0.1:6379> hmget myhash name age sex
1) "zhangsan"
2) "18"
3) "m"
删:
127.0.0.1:6379> hdel myhash phone
(integer) 1
127.0.0.1:6379> hget myhash phone
(nil)
1.5 SortedSet(ZSet 排序集合)
应用场景:排行榜
增:
127.0.0.1:6379> zadd mysortset 18 zhaoxs 20 fanmw 22 liuyh 30 wangmj
(integer) 4
查:
获取特定元素的分数
127.0.0.1:6379> zscore mysortset zhaoxs
"18"
获取排序后指定下标范围的元素
127.0.0.1:6379> zrange mysortset 0 2
1) "zhaoxs"
2) "fanmw"
3) "lisih"
获取所有元素
127.0.0.1:6379> zrange mysortset 0 -1
1) "zhaoxs"
2) "fanmw"
3) "lisih"
4) "wangmj"
按照分数大小范围获取元素
127.0.0.1:6379> zrangebyscore mysortset 20 22
1) "fanmw"
2) "lisih"
按照分数大小范围获取元素,并进行过滤
127.0.0.1:6379> zrangebyscore mysortset 16 30
1) "zhaoxs"
2) "fanmw"
3) "lisih"
4) "wangmj"
127.0.0.1:6379> zrangebyscore mysortset 16 30 limit 2 2
1) "lisih"
2) "wangmj"
删:
127.0.0.1:6379> zrem mysortset wangmj
(integer) 1
127.0.0.1:6379> zscore mysortset wangmj
(nil)
改:
修改分数
127.0.0.1:6379> zincrby mysortset 2 zhaoxs
"20"
127.0.0.1:6379> zscore mysortset zhaoxs
"20"
注意:redis根据数据类型不同将命令分成不同的组,可以通过help命令获取帮助。
help @string
help @list
help @set
help @hash
help @sorted_set
flushdb:清除数据库里面所有的数据
2 Redis中设置key的过期时间
Redis中可以设置数据的过期时间,一旦过期自动删除数据。
-
设置过期时间
127.0.0.1:6379> set name ok //设置10s后过期,expire单位秒 127.0.0.1:6379> expire name 10 //设置10s后过期,pexpire 单位毫秒 127.0.0.1:6379> pexpire age 10000 (integer) 1
-
查看剩余时间
查看剩余存活时长,单位秒 127.0.0.1:6379> ttl name (integer) 7 查看剩余存活时长,单位毫秒 127.0.0.1:6379> pttl name (integer) 4006
-
取消过期
127.0.0.1:6379> set age 18 OK 127.0.0.1:6379> expire age 20 (integer) 1 127.0.0.1:6379> ttl age (integer) 15 取消过期 127.0.0.1:6379> persist age (integer) 1 ttl返回-1表示没有设置过期时间,返回-2表示数据不存在 127.0.0.1:6379> ttl age (integer) -1 127.0.0.1:6379> get age "18"
应用:手机验证码、黑名单、缓存
3 Springboot操作Redis
Spring的spring-data-redis模块中封装了RedisTemplate对象来进行对Redis的各种操作,它支持所有的Redis原生的api。提供了连接池自动管理、高度统一的Api、灵活的定制化,简化Java操作Redis的编码工作。
3.1 第1个RedisTemplate示例
-
导入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.9.0</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!--springboot做单元测试的依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency>
-
配置
application.yml
spring: redis: host: 192.168.232.107 port: 6379 jedis: pool: max-active: 500 max-idle: 50 min-idle: 10 max-wait: 30000
-
入口类:配置RedisTemplate
@Configuration public class RedisConfig{ @Bean //使用配置类把redisTemplate管理起来,参数ConnectionFactory在spring容器里面已经提供 public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){ RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); StringRedisSerializer stringRedisSerializer= new StringRedisSerializer(); GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer(); redisTemplate.setKeySerializer(stringRedisSerializer); redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer); redisTemplate.setHashKeySerializer(stringRedisSerializer); redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer); return redisTemplate; } }
-
编码
@RuntWith(SpringRunner.class) @SpringBootTest(classes=RedisDay02Application.class) public class RedisTemplateTest{ @Autowired //获取RedisTemplate工具,注意使用泛型,否则注入时提示spring容器中有多个redisTemplate private RedisTemplate<String,Object> redisTemplate; @Test public void testValueSet(){ //获取操作特定数据类型的operations对象 ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue(); //通过operations对象操作数据 valueOperations.set("name","zhangsan"); valueOperations.set("age",18, Duration.ofSeconds(60)); } @Test public void testValueGet(){ ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue(); Object name = valueOperations.get("name"); Object age = valueOperations.get("age"); System.out.println("name = " + name); System.out.println("age = " + age); } }
3.2 RedisTemplate的常用api
spring-data-redis针对jedis客户端中大量api进行了归类封装,将同一类型操作封装为operations接口。根据Redis的不同数据类型,定义了如下的operations接口:
- ValueOperations:提供了对String类型的操作
- ListOperations :提供了对List类型的数据操作
- SetOperations:提供了对Set类型的数据操作
- HashOperations:提供了对Map类型的数据操作
- ZSetOprations:提供了对ZSet类型的数据操作
3.2.1 ValueOperations
-
添加:set
ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue(); valueOperations.set("name","zhangsan");//添加 valueOperations.set("age",18, Duration.ofSeconds(60));//添加有时效的数据 Map<String, Object> map = new HashMap<>(); map.put("score",100.0); map.put("sex","男"); valueOperations.multiSet(map);//批量添加
-
删除:delete
redisTemplate.delete("name");
-
修改:set、increment、decremnt
ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue(); valueOperations.set("age",18); valueOperations.increment("age");//自增 valueOperations.increment("age",10);//加10 valueOperations.decrement("age");//自减 valueOperations.decrement("age",10);//减10
-
查询:get、mget
ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue(); Object name = valueOperations.get("name"); Object age = valueOperations.get("age"); System.out.println("name = " + name); System.out.println("age = " + age); //批量查询 Collection<String> keys = new ArrayList<String>(0); keys.add("score"); keys.add("sex"); List<Object> values = valueOperations.multiGet(keys);
3.2.2 ListOperations
-
添加:push
ListOperations<String, Object> listOperations = redisTemplate.opsForList(); listOperations.leftPush("list","1"); listOperations.leftPushAll("list","2,","3"); listOperations.rightPush("list","2"); listOperations.rightPushAll("list","3","4");
-
删除:pop
ListOperations<String, Object> listOperations = redisTemplate.opsForList(); Object leftPop = listOperations.leftPop("list"); Object rightPop = listOperations.rightPop("list"); System.out.println("leftPop = " + leftPop); System.out.println("rightPop = " + rightPop);
-
修改:set
ListOperations<String, Object> listOperations = redisTemplate.opsForList(); listOperations.set("list",1,"new value");
-
查询:index、size、range
ListOperations<String, Object> listOperations = redisTemplate.opsForList(); Object value = listOperations.index("list", 0); Long size = listOperations.size("list"); List<Object> list = listOperations.range("list", 0, -1);
3.2.3 SetOperations
-
添加:add
SetOperations<String, Object> setOperations = redisTemplate.opsForSet(); setOperations.add("set", "xiao1hei", "xiao2hei");
-
删除:remove
SetOperations<String, Object> setOperations = redisTemplate.opsForSet(); setOperations.remove("set", "xiao1hei");
-
集合运算:difference、union、intersect
SetOperations<String,Object> setOperations = redisTemplate.opsForSet(); setOperations.add("set1",1,2,3,4); setOperations.add("set2",2,3,4,5); setOperations.add("set3",3,4,5,6); Set<Object> difference = setOperations.difference("set1", "set2");//set1-set2 Collection<String> keys = new ArrayList<String>(); keys.add("set1"); keys.add("set2"); keys.add("set3"); Set<Object> union = setOperations.union(keys);//set1 + set2 + set3 Set<Object> intersect = setOperations.intersect(keys);//求交集
3.2.4 HashOperations
-
添加:put、putAll
HashOperations<String, String, Object> hashOperations = redisTemplate.opsForHash(); hashOperations.put("map","k1","v1"); Map<String, String> values = new HashMap<>(); values.put("k2", "v2"); values.put("k3", "v3"); hashOperations.putAll("map",values);
-
删除:
HashOperations<String, String, Object> hashOperations = redisTemplate.opsForHash(); hashOperations.delete("map","k1","k2");
-
修改:
HashOperations<String, String, Object> hashOperations = redisTemplate.opsForHash(); hashOperations.put("map","age",18); hashOperations.increment("map","age",10);
-
查询:
HashOperations<String, String, Object> hashOperations = redisTemplate.opsForHash(); Object v1 = hashOperations.get("map", "k1"); Collection<String> keys = new ArrayList<>(); keys.add("k2"); keys.add("k3"); List<Object> values = hashOperations.multiGet("map", keys); Boolean hasKey = hashOperations.hasKey("map", "k1"); Long size = hashOperations.size("map");
3.2.5 ZSetOperations
-
添加:
ZSetOperations<String, Object> zSetOperations = redisTemplate.opsForZSet(); zSetOperations.add("zset","zhangsan",100); zSetOperations.add("zset","lisi",60);
-
删除:
zSetOperations.remove("zset","zhangsan","lisi"); zSetOperations.removeRange("zset",0,2); zSetOperations.removeRangeByScore("zset",50,99.0);
-
修改:
zSetOperations.incrementScore("zset", "lisi", 10.0);
-
查询:
Set<Object> zset1 = zSetOperations.range("zset", 0, -1); Set<Object> zset2 = zSetOperations.rangeByScore("zset", 40, 100); Set<ZSetOperations.TypedTuple<Object>> scores = zSetOperations.rangeWithScores("zset", 0, -1); for (ZSetOperations.TypedTuple e:scores) { System.out.println(e.getValue()+" "+e.getScore()); } Set<ZSetOperations.TypedTuple<Object>> scores2 = zSetOperations.rangeByScoreWithScores("zset", 40, 100); for (ZSetOperations.TypedTuple e:scores2) { System.out.println(e.getValue()+" "+e.getScore()); }
4 Redis缓存
4.1 Redis缓存引言
缓存:对于数据库中的热点数据的备份,便于访问,提高应用性能。
单机架构的缓存方案
![image-20201107230421208](Redis day02.assets/image-20201107230421208.png)
EhCache直接将数据缓存在JVM中,速度快,效率高。但是在分布式集群环境下,缓存管理非常麻烦,比如:当修改了一条数据后,必须通知到缓存了该数据的所有缓存。
![image-20201107231737082](Redis day02.assets/image-20201107231737082.png)
解决方案:Redis缓存
Redis基于内存操作,性能远超关系型数据库,可以代替EhCache充当缓存。单独部署Redis服务,应用通过网络和Redis通信,虽效率略低于EhCache,但处理集群分布式缓存有成熟的方案。
![image-20201107232452489](Redis day02.assets/image-20201107232452489.png)
4.2 Redis缓存的使用
Redis缓存的设计思路:
- 在执行查询时,先查询redis缓存,如果有数据则不在调用dao直接返回;如果查不到,才调用dao,并将数据保存到缓存
- 在执行增删改后,需要清空缓存,避免脏数据
![image-20201108102214591](Redis day02.assets/image-20201108102214591.png)
为避免操作缓存的代码和原始业务代码耦合,不能将操作缓存的代码硬编码到业务方法中。可以将操作Redis缓存的代码定义成Advice(增强),使用AOP的方式动态增强。
4.3实战中Spring Boot的Redis缓存配置
事实上,在Springboot中配置缓存无需手写增强类,Springboot对缓存的AOP方式进行了抽取封装,内置了一套开箱即用的缓存机制。
**注解:**Spring内置了缓存注解
- Cacheable:用在方法上,执行方法前先查询缓存,如果缓存有数据,直接返回;如果缓存中没有数据,则执行查询方法,并将结果保存到缓存中
- CacheEvict:用在方法上,可以在方法执行前或后删除缓存
增强类: Spring内置了RedisCacheManager缓存增强类
具体步骤:
-
引入依赖。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <version>2.1.4.RELEASE</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.9.0</version> </dependency>
-
在目标方法上面使用缓存。
@CacheEvict(value="NewsServiceImpl",allEntries = true,beforeInvocation = true) @Cacheable(value="NewsServiceImpl",key="#root.methodName+#id")
-
在Application启动类上面添加开启缓存注解。
@EnableCaching//开启缓存 public class Application {}
-
在配置类中对redis连接进行管理。
//配置缓存管理器 @Bean public CacheManager cacheManager(RedisConnectionFactory connectionFactory){ RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory); return new RedisCacheManager(redisCacheWriter,getRedisCacheConfiguration(20), getRedisCacheConfigurationMap()); } private Map<String, RedisCacheConfiguration> getRedisCacheConfigurationMap(){ Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>(); //以NewsServiceImpl开头的缓存要存储100秒 redisCacheConfigurationMap.put("NewsServiceImpl", getRedisCacheConfiguration(100)); return redisCacheConfigurationMap; } //RedisCacheConfiguration 用于负责Redis的缓存配置 private RedisCacheConfiguration getRedisCacheConfiguration(int seconds){ RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig(); GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer(); return redisCacheConfiguration .serializeValuesWith( RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer) ) .entryTtl(Duration.ofSeconds(seconds)); }
-
在application.yml中对redis服务器进行配置。
spring: redis: lettuce: pool: max-active: 500 max-idle: 50 min-idle: 10 max-wait: 30000 host: 192.168.232.107 port: 6379