之前利用springBoot中的缓存机制,使用Redis作为缓存容器,做了一个缓存的简单Demo,当然Redis不仅仅可以用来做缓存的容器,还有很多开发的实际场景中会用到Redis的特性,通过几天的学习,现将学习的成果整理分享给大家。希望大家也多多讨论,提供跟多的使用场景,来熟练掌握在springboot中的使用。
个人博客:http://z77z.oschina.io/
此项目下载地址:https://git.oschina.net/z77z/springboot_mybatisplus
Redis的特性
Strings:Strings 数据结构是简单的key-value类型,value其实不仅是String,也可以是数字.
常用命令: set,get,decr,incr,mget 等。
常用方法:
- 获取字符串长度
- 往字符串append内容
- 设置和获取字符串的某一段内容
- 设置及获取字符串的某一位(bit)
- 批量设置一系列字符串的内容
Hashs:Redis Hash对应Value内部实际就是一个HashMap,常用命令:hget,hset,hgetall 等。
Lists:Redis list的实现为一个双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销,Redis内部的很多实现,包括发送缓冲队列等也都是用的这个数据结构。
常用命令:lpush,rpush,lpop,rpop,lrange等。
Sets:Sets 集合的概念就是一堆不重复值的组合。Redis还为集合提供了求交集、并集、差集等操作,可以非常方便的实现如共同关注、共同喜好、二度好友等功能。
常用命令:sadd,spop,smembers,sunion 等。
Sorted Sets:Redis sorted set的使用场景与set类似,区别是set不是自动有序的。sorted set可以通过用户额外提供一个优先级(score)的参数来为成员排序,并且是插入有序的,即自动排序。
常用命令:zadd,zrange,zrem,zcard等
下面就根据这几个特性,也就是Redis支持的数据类型,来完成以下场景的实现。
场景一:简单计数功能
Redis是一个很好的计数器,计数器是 Redis 的原子性自增操作可实现的最直观的模式了,它的想法相当简单:每当某个操作发生时,向 Redis 发送一个 INCR 命令。使用场景比如网站的访问数,注册用户数,文章的点赞数,高并发的秒杀活动,分布式序列号生成等等统计计数的功能实现。Redis 解决这类计数问题得心应手,相比关系数据库速度更快,消耗资源更少。还可以通过set()方法来重置计数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Test public void test1() { try { ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue(); System.out.println(opsForValue.get("test1")); for (int i = 0; i < 100; i++) { opsForValue.increment("test1", 1); } System.out.println(opsForValue.get("test1")); } catch (Exception e) { e.printStackTrace(); } }
|
场景二:按时间统计计数
有时候除了简单的计数外,比如注册用户数需要按日统计,处理方法比较简单,把日期带入计数器 key 就可以。以此类推,还可以按其他方式进行统计计数,只需要把统计的方式添加到key值就可以了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Test public void test2() { String key = "test2_" + new SimpleDateFormat("yyyy-MM-dd").format(new Date()); try { ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue(); System.out.println(opsForValue.get(key)); for (int i = 0; i < 100; i++) { opsForValue.increment(key, 1); } System.out.println(opsForValue.get(key)); } catch (Exception e) { e.printStackTrace(); } }
|
场景三:按模糊Key值查询
在按条件统计计数的时候,把时间加入到了key值中,有时候要只是查询某个对象的统计数时,就可以使用模糊Key值查询。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @Test public void test3() { try { ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue(); Set<String> keys = stringRedisTemplate.keys("test*"); for (String a : keys) { System.out.println(a + ":" + opsForValue.get(a)); } } catch (Exception e) { e.printStackTrace(); } }
|
场景四:设置Key的有效时间(防止高并发访问)
在redis中可以设置key值的有效时间,用户访问链接的时候,将用户的唯一信息比如ip地址等为key值,时间为value值,在redis中记录一下,在用户再次访问的时候,通过key值获取前一次访问的时间,比较时间的间隔,如果低于阀值,就拒绝这次请求,防止用户多次访问。这里只是写下在spring的RedisTemplate接口怎么使用。具体的逻辑实现自己搞定。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @Test public void test4() { try { ValueOperations<String, String> opsForValue = stringRedisTemplate.opsForValue(); opsForValue.set("test4", "test4"); System.out.println(opsForValue.get("test4")); if(stringRedisTemplate.expire("test4", 5, TimeUnit.SECONDS)){ System.out.println("设置过期时间成功,等待。。。。"); Thread.sleep(5001); } System.out.println(opsForValue.get("test4")); } catch (Exception e) { e.printStackTrace(); } }
|
场景五:使用hashs存储获取修改java对象
在实际开发中,我们经常将一些结构化的信息打包成HashMap,在客户端序列化后存储为一个字符串的值,比如用户的昵称、年龄、性别、积分等,这时候在需要修改其中某一项时,通常需要将所有值取出反序列化后,修改某一项的值,再序列化存储回去。这样不仅增大了开销,也不适用于一些可能并发操作的场合(比如两个并发的操作都需要修改积分)。
而Redis的Hash结构可以使你像在数据库中Update一个属性一样只修改某一项属性值。因为Redis的Hash结构是以对象的名字作为redis的key值,以对象的唯一属性值作为hash的key值,以对象来作为redis的value值。结构图如下:
hashs存储结构图
1 2 3 4 5 6 7 8
| @Test public void test5(){ BeautifulPictures beautifulPictures = beautifulPicturesService.selectById(1); HashOperations<String, Object, BeautifulPictures> hash = redisTemplate.opsForHash(); hash.put("test5",beautifulPictures.getId(),beautifulPictures); System.out.println(hash.get("test5", beautifulPictures.getId())); }
|
场景六:使用lists有序存储读取
适用于获取最近N个操作的数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @Test public void test6(){ ListOperations<String, String> list = stringRedisTemplate.opsForList(); list.leftPush("test6", "1"); list.leftPush("test6", "2"); list.leftPush("test6", "3"); list.leftPush("test6", "4"); list.leftPush("test6", "5"); list.leftPush("test6", "6"); list.leftPush("test6", "7"); list.trim("test6", 0, 2); System.out.println(list.range("test6", 0, list.size("test6")-1)); }
|
场景七:使用sets存储读取 无序 去重 求差集,交集,并集
1 2 3 4 5 6 7 8 9 10 11
| @Test public void test7(){ SetOperations<String, String> set = stringRedisTemplate.opsForSet(); set.add("test7_1", "2", "1","2","3","4","4","3"); set.add("test7_2", "2", "6","2","3","7","6","5"); System.out.println("全部成员"+set.members("test7_1")); System.out.println("差集"+set.difference("test7_1", "test7_2")); System.out.println("交集"+set.intersect("test7_1", "test7_2")); System.out.println("并集"+set.union("test7_1", "test7_2")); }
|
场景八:Sorted Set 存取数据 排序
1 2 3 4 5 6 7 8 9 10 11 12
| //Sorted Set 存取数据 排序 相比sets 保存时多一个权重参数score,相当于按照此参数来排序 @Test public void test8(){ ZSetOperations<String, String> zSet = stringRedisTemplate.opsForZSet(); zSet.add("test8", "use1", 9); zSet.add("test8", "use2", 1); zSet.add("test8", "use3", 5); zSet.add("test8", "use4", 9); //对应的score值增加 //zSet.incrementScore("test8", "use1", 1); System.out.println(zSet.reverseRange("test8", 0, zSet.size("test8")-1)); }
|
最后更新时间: