🌿欢迎来到@衍生星球的CSDN博文🌿
🍁本文主要学习【Spring Boot】操作Redis数据结构 🍁
🌱我是衍生星球,一个从事集成开发的打工人🌱
⭐️喜欢的朋友可以关注一下🫰🫰🫰,下次更新不迷路⭐️
💠作为一名热衷于分享知识的程序员,我乐于在CSDN上与广大开发者交流学习。
💠我希望通过每一次学习,让更多读者了解我,也希望能结识更多志同道合的朋友。
💠在今后的日子里,我将继续努力,不断提升自己的专业技能,创造更多价值。
学习目标:
- 如何使用Redis的5个常用数据结构
- 学习使用Redis的基础
1.String
String(字符串)是常用的数据格式,一般在缓存数据时基本是使用String格式来进行存储的。
@Test
public void testString() {
ValueOperations<Serializable,Object> operations =redisTemplate.opsForValue();
// 调用set()方法创建缓存
operations.set("string","you");
//获取缓存数据
String value=(String) operations.get("string");
System.out.println("string value :"+value);
}
通过上面的示例可以看到,我们调用了set()和get()方法来创建与获取缓存数据,使用起来特别简单方便。
Redis除了提供set()、get()方法之外,还提供了decr()和incr()方法。当String类型的值为整数时,Redis可以把它当作整数一样执行自增(incr)和自减(decr)操作。由于Redis所有的操作都是原子性的,因此不必担心在多客户端连接时可能出现的事务处理问题。具体示例代码如下:
@Test
public void testStringIncr() {
// 设置当前在线用户数
redisTemplate.opsForValue().set("user:online", "100");
//当前在线用户数+1
redisTemplate.opsForValue().increment("user:online");
// 获取缓存数据
Integer value = (Integer) redisTemplate.opsForValue().get("user:online");
System.out.println("string value :" + value);
}
通过上面的示例可以看到,RedisTemplate提供了increment()和decrement()方法实现String数据类型的自增(incr)、自减(decr)操作。
2.Hash
我们使用缓存时基本都是使用String进行存储的,但是有些场景String类型存储可能不太适用。因为Redis每存储一个key都会占用一个内存空间,key太多会消耗不必要的内存,也不方便数据的管理,因此合理地使用Hash(哈希)可以减少key的数量,也能节省内存。
Hash是一个String类型的field和value的映射表。如果key不存在,就会创建新的哈希表并进行HSET操作;如果字段(field)已经存在于哈希表中,则旧值将被覆盖。Hash适用于存储对象。
@Test
public void testHash() {
HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
//调用put()方法创建Hash数据缓存
hash.put("hash","test","hello");
hash.put("hash","test","spring");
hash.put("hash","test","boot");
//获取Hash数据
String value=(String)hash.get("hash","you");
System.out.println("hash value :"+value);
}
通过上面的示例可以看到,使用Hash存储数据时需要传入3个参数:第一个参数为key,第二个参数为字段(field),第三个参数为要存储的值(value)。
Hash删除时特别方便,比如将同类的数据聚集在一个Hash中,删除key就可以实现全部删除,清理数据比较方便。除此之外,另一种是删除Hash中的部分key。
//删除Hash 中的部分key
redisTemplate.opsForHash().delete("hash","test");
如上面的示例所示,如果需要删除Hash集合中的某个数据,传入对应的key和field参数即可。
3.List
List(列表)的应用场景非常广泛,是Redis重要的数据结构之一。使用List可以轻松地实现数据队列,List典型的应用场景就是消息队列,通过List的Push操作将消息数据存储到List中,然后在“消费”线程中再用POP操作将消息取出并进行相应的处理。
List的实现为一个双向链表,可以按照插入顺序排序。另外,也可以把一个元素到添加列链表的头部(左边)或者链表的尾部(右边),操作起来更加方便。
@Test
public void testList() {
ListOperations<String, String > list = redisTemplate.opsForList();
// 把数据插入到List的左边
list.leftPush("list", "hello");
list.leftPush("list", "spring");
list.leftPush("list", "boot");
// 从左边取出List中的数据
String value = (String)list.leftPop("list");
System.out.println("list value :" + value.toString());
}
上面的例子表示把值从左侧插入(leftPush)一个key为“list”的队列中,然后从该队列的最左侧取出(leftPop)一个数据。
List还有很多其他API操作函数,比如从右侧插入(rightPush)队列,从右侧读取(rightPop)数据,或者调用range()方法读取队列的一部分。接着上面的例子,我们调用range()方法进行读取。
@Test
public void testListRange() {
ListOperations<String, String> list = redisTemplate.opsForList();
// 从List的左边插入数据
list.leftPush("list", "ysxq");
list.leftPush("list", "spring");
list.leftPush("list", "boot");
// 调用range()方法获取部分List
List<String> values = list.range("list", 0, 2);
for (String v : values) {
System.out.println("list range :" + v);
}
}
Range()方法包含3个参数:第一个参数是key,第二个参数是读取的起始位置,第三个参数是读取的结束位置。输入不同的参数就可以从队列中读取对应的数据。
4.Set
Set(集合)是String类型的无序集合。集合成员是唯一的,所以集合中不能出现重复的数据。Set的功能与List类似,不同之处在于Set可以自动去除重复的数据。因此,当我们需要存储一个列表数据又不希望其中出现重复的数据时,Set类型就是一个很好的选择。示例代码如下:
@Test
public void testSet() {
String key = "set";
SetOperations<String, String> set = redisTemplate.opsForSet();
// 在Set 中插入数据
set.add(key, "hello");
set.add(key, "spring");
set.add(key, "boot");
set.add(key, "hello");
// 调用members()方法判断某个数据
Set<String> values = set.members(key);
for (String v : values) {
System.out.println("set value :" + v);
}
}
通过上面的例子可以发现,Set提供了members()方法获取集合中全部的数据。而且,当存入两个相同的数据“hello”时,全部读取Set时只剩下一个数据,说明Set对队列进行了自动去重操作。
Set还为集合提供了求交集、并集、差集等操作函数,使用起来非常方便,适用于各种业务需求。示例代码如下:
@Test
public void testSetUnion() {
SetOperations<String,String> set = redisTemplate.opsForSet();
//在seta 中插入数据
set.add("set:a","spring");
set.add("set:a","ysxq");
set.add("set:a", "test");
//在setb 中插入数据
set.add("set:b","spring");
set.add("set:b","ysxq");
set.add("set:b","test");
// 返回多个集合的并集
redisTemplate.opsForSet().union("set:a","set:b");
// 返回多个集合的交集
redisTemplate.opsForSet().intersect("set:a","set:b");
//返回集合key1中存在但是key2中不存在的数据集合,即差集
redisTemplate.opsForSet().difference("set:a","set:b");
}
5.ZSet
ZSet的使用场景与Set类似,区别在于Set是无序的,而ZSet可以通过一个优先级(Score)参数来为成员排序。示例代码如下:
@Test
public void testZset() {
String key = "zset";
redisTemplate.delete(key);
ZSetOperations<String, String> zset = redisTemplate.opsForZSet();
zset.add(key, "hello", 1);
zset.add(key, "ysxq", 6);
zset.add(key, "boot", 4);
zset.add(key, "spring", 3);
// 调用range()方法获取数据
Set<String> zsets = zset.range(key, 0, 3);
for (String v : zsets) {
System.out.println("zset value :" + v);
}
}
通过上面的例子可以发现,保存ZSet类型的缓存时会传入key、value、Score三个参数,然后通过range获取数据。
ZSet还可以根据Score的值对集合进行排序。我们可以利用这个特性来实现具有权重的队列,比如普通消息的Score为1,重要消息的Score为2,然后消费线程可以选择按Score的倒序来获取相关数据。
@Test
public void testZset1() {
String key = "zset";
redisTemplate.delete(key);
ZSetOperations<String, String> zset = redisTemplate.opsForZSet();
zset.add(key, "it", 1);
zset.add(key, "you", 6);
zset.add(key, "know", 4);
zset.add(key, "neo", 3);
// 调用range()方法获取数据并排序
Set<String> zsetB = zset.rangeByScore(key, 0, 3);
for (String v : zsetB) {
System.out.println("zsetB value :" + v);
}
}
通过上面的示例可以发现,插入ZSet的数据会自动根据Score进行排序,还可以调用rangeByScore()方法获取Score范围内排序后的数据。根据这个特性可以实现优先队列的功能。