概述
Redis 的集合不是一个线性结构,而是一个哈希表结构,它的内部会根据 hash 分子来存储和查找数据,理论上一个集合可以存储2的32次方减一(约42亿)个元素。
因为采用哈希表结构,所以对于 Redis 集合的插入、删除和查找的复杂度都是 0(1),只是我们需要注意 3 点
- 对于集合而言,它的每一个元素都是不能重复的,当插入相同记录的时候都会失败
- 集合是无序的
- 集合的每一个元素都是 String 数据结构类型
常用集合命令
官网: https://redis.io/commands#set
Redis 的集合可以对于不同的集合进行操作,比如求出两个或者以上集合的交集、 差集和并集等
上述命令的前缀都包含 了 一个 s,用来表达这是集合的命令 , 集合是无序的 , 并且支持并集 、 交集和差集的运算。
下面通过命令行客户端来演示这些命令
127.0.0.1:6379> FLUSHDB OK 127.0.0.1:6379> SADD set1 v1 v2 v3 v4 v5 v6 (integer) 6 127.0.0.1:6379> SADD set2 v0 v2 v4 v6 v8 (integer) 5 127.0.0.1:6379> SCARD set1 (integer) 6 127.0.0.1:6379> SDIFF set1 set2 1) "v5" 2) "v1" 3) "v3" 127.0.0.1:6379> SINTER set1 set2 1) "v4" 2) "v6" 3) "v2" 127.0.0.1:6379> SISMEMBER set2 v2 (integer) 1 127.0.0.1:6379> SISMEMBER set1 v2 (integer) 1 127.0.0.1:6379> SISMEMBER set v2 (integer) 0 127.0.0.1:6379> SMEMBERS set2 1) "v4" 2) "v8" 3) "v6" 4) "v0" 5) "v2" 127.0.0.1:6379> SPOP set1 "v5" 127.0.0.1:6379> SMEMBERS set1 1) "v4" 2) "v6" 3) "v1" 4) "v2" 5) "v3" 127.0.0.1:6379> SRANDMEMBER set1 2 1) "v4" 2) "v3" 127.0.0.1:6379> SREM set1 v1 (integer) 1 127.0.0.1:6379> SMEMBERS set1 1) "v4" 2) "v6" 3) "v2" 4) "v3" 127.0.0.1:6379> SUNION set1 set2 1) "v4" 2) "v8" 3) "v6" 4) "v0" 5) "v2" 6) "v3" 127.0.0.1:6379> SMEMBERS set1 1) "v4" 2) "v6" 3) "v2" 4) "v3" 127.0.0.1:6379> SMEMBERS set2 1) "v4" 2) "v8" 3) "v6" 4) "v0" 5) "v2" 127.0.0.1:6379>
交集、并集和差集保存命令的用法
127.0.0.1:6379> FLUSHDB OK 127.0.0.1:6379> SADD set1 v1 v2 v3 v4 v5 v6 (integer) 6 127.0.0.1:6379> SADD set2 v2 v4 v6 v8 (integer) 4 127.0.0.1:6379> SDIFFSTORE diff_set set1 set2 (integer) 3 127.0.0.1:6379> SMEMBERS diff_set 1) "v5" 2) "v1" 3) "v3" 127.0.0.1:6379> SUNIONSTORE union_set set1 set2 (integer) 7 127.0.0.1:6379> SMEMBERS union_set 1) "v4" 2) "v8" 3) "v5" 4) "v1" 5) "v6" 6) "v3" 7) "v2" 127.0.0.1:6379> SINTERSTORE inter_set set1 set2 (integer) 3 127.0.0.1:6379> SMEMBERS inter_set 1) "v6" 2) "v2" 3) "v4" 127.0.0.1:6379>
上述命令主要是求差集、并集和交集 , 并保存到新的集合中。
Spring中操作Redis 集合命令
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:property-placeholder location="classpath:redis/redis.properties" /> <!--2,注意新版本2.3以后,JedisPoolConfig的property name,不是maxActive而是maxTotal,而且没有maxWait属性,建议看一下Jedis源码或百度。 --> <!-- redis连接池配置 --> <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"> <!--最大空闲数 --> <property name="maxIdle" value="${redis.maxIdle}" /> <!--连接池的最大数据库连接数 --> <property name="maxTotal" value="${redis.maxTotal}" /> <!--最大建立连接等待时间 --> <property name="maxWaitMillis" value="${redis.maxWaitMillis}" /> <!--逐出连接的最小空闲时间 默认1800000毫秒(30分钟) --> <property name="minEvictableIdleTimeMillis" value="${redis.minEvictableIdleTimeMillis}" /> <!--每次逐出检查时 逐出的最大数目 如果为负数就是 : 1/abs(n), 默认3 --> <property name="numTestsPerEvictionRun" value="${redis.numTestsPerEvictionRun}" /> <!--逐出扫描的时间间隔(毫秒) 如果为负数,则不运行逐出线程, 默认-1 --> <property name="timeBetweenEvictionRunsMillis" value="${redis.timeBetweenEvictionRunsMillis}" /> <property name="testOnBorrow" value="true"></property> <property name="testOnReturn" value="true"></property> <property name="testWhileIdle" value="true"></property> </bean> <!--redis连接工厂 --> <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" destroy-method="destroy"> <property name="poolConfig" ref="jedisPoolConfig"></property> <!--IP地址 --> <property name="hostName" value="${redis.host.ip}"></property> <!--端口号 --> <property name="port" value="${redis.port}"></property> <!--如果Redis设置有密码 --> <property name="password" value="${redis.password}" /> <!--客户端超时时间单位是毫秒 --> <property name="timeout" value="${redis.timeout}"></property> <property name="usePool" value="true" /> <!--<property name="database" value="0" /> --> </bean> <!-- 键值序列化器设置为String 类型 --> <bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"/> <!-- redis template definition --> <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" p:connection-factory-ref="jedisConnectionFactory" p:keySerializer-ref="stringRedisSerializer" p:defaultSerializer-ref="stringRedisSerializer" p:valueSerializer-ref="stringRedisSerializer"> </bean> </beans>
package com.artisan.redis.baseStructure.set; import java.util.List; import java.util.Set; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.data.redis.core.RedisTemplate; /** * * * @ClassName: SpringRedisSetDemo * * @Description: 记得将 RedisTemplate 值序列化器设置为 StringRedisSerializer 然后运行该代码 * * @author: Mr.Yang * * @date: 2018年9月26日 下午3:22:57 */ public class SpringRedisSetDemo { private static final String SET1 = "set1"; private static final String SET2 = "set2"; public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:spring/spring-redis-set.xml"); RedisTemplate<String, String> redisTemplate = (RedisTemplate<String, String>) ctx.getBean("redisTemplate"); Set<String> set = null; // 127.0.0.1:6379> FLUSHDB // OK // 127.0.0.1:6379> SADD set1 v1 v2 v3 v4 v5 v6 // (integer) 6 // 127.0.0.1:6379> SADD set2 v0 v2 v4 v6 v8 // (integer) 5 // 将元素加入列表 redisTemplate.boundSetOps(SET1).add("v1", "v2", "v3", "v4", "v5", "v6"); redisTemplate.boundSetOps(SET2).add("v0", "v2", "v4", "v6", "v8"); // 127.0.0.1:6379> SCARD set1 // (integer) 6 // 求集合长度 System.out.println(SET1 + "的长度为:" + redisTemplate.opsForSet().size(SET1)); System.out.println(SET2 + "的长度为:" + redisTemplate.opsForSet().size(SET2)); // 127.0.0.1:6379> SDIFF set1 set2 // 1) "v5" // 2) "v1" // 3) "v3" // 求差集 set = redisTemplate.opsForSet().difference(SET1, SET2); scanSet(set); // 求并集 set = redisTemplate.opsForSet().intersect(SET1, SET2); scanSet(set); // 判断是否是集合中的元素 boolean exist = redisTemplate.opsForSet().isMember(SET1, "v1"); System.out.println(SET1 + "中存在v1:" + exist); // 获取集合所有元素 set = redisTemplate.opsForSet().members(SET1); scanSet(set); set = redisTemplate.opsForSet().members(SET2); scanSet(set); // 从集合中随机弹出一个元素,集合中会删除该元素 String randomValue = redisTemplate.opsForSet().pop(SET2); System.out.println(SET2 + "中弹出的随机元素为" + randomValue); System.out.println(SET2 + "的长度为:" + redisTemplate.opsForSet().size(SET2)); // 随机获取一个集合的元素 ,该元素仍然在集合中 randomValue = (String) redisTemplate.opsForSet().randomMember(SET1); System.out.println(randomValue); System.out.println(SET1 + "的长度为:" + redisTemplate.opsForSet().size(SET1)); System.out.println("------------"); // 随机获取集合中 的4 个元素 List<String> list = redisTemplate.opsForSet().randomMembers(SET1, 4); for (String string : list) { System.out.println(string); } // 删除一个集合的元素,参数可以是多个 Long value = redisTemplate.opsForSet().remove(SET1, "v1", "v2"); System.out.println(SET1 + "中删除了" + value + "个元素"); System.out.println(SET1 + "的长度为:" + redisTemplate.opsForSet().size(SET1)); // 求两个集合的并集 set = redisTemplate.opsForSet().union(SET1, SET2); scanSet(set); // 求两个集合的差集,并保存到集合 diff_set 中 redisTemplate.opsForSet().differenceAndStore(SET1, SET2, "diff_set"); scanSet(redisTemplate.opsForSet().members("diff_set")); // 求两个集合的交集,并保存到集合 inter_set 中 redisTemplate.opsForSet().intersectAndStore(SET1, SET2, "inter_set"); scanSet(redisTemplate.opsForSet().members("inter_set")); // 求两个集合的并集,并保存到集合 union_set 中 redisTemplate.opsForSet().unionAndStore(SET1, SET2, "union_set"); scanSet(redisTemplate.opsForSet().members("union_set")); } private static void scanSet(Set<String> set) { for (String value : set) { System.out.println(value); } System.out.println("----------------"); } }
输出
INFO : org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@73a8dfcc: startup date [Wed Sep 26 16:18:55 CST 2018]; root of context hierarchy INFO : org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [spring/spring-redis-set.xml] set1的长度为:6 set2的长度为:5 v5 v1 v3 ---------------- v4 v6 v2 ---------------- set1中存在v1:true v4 v5 v1 v6 v2 v3 ---------------- v4 v8 v6 v2 v0 ---------------- set2中弹出的随机元素为v4 set2的长度为:4 v3 set1的长度为:6 ------------ v1 v1 v4 v4 set1中删除了2个元素 set1的长度为:4 v4 v5 v8 v6 v0 v3 v2 ---------------- v5 v3 v4 ---------------- v6 ---------------- v4 v5 v8 v6 v0 v3 v2 ----------------
注意
使用 Spring 提供的 RedisTemplate 去展示多个命令可以学习到如何使用 RedisTemplate 操作 Redis 。 实际工作中并不是那么用的,因为每一 个操作会尝试从连接池里获取 一 个新的 Redis 连接,多个命令应该使用SessionCallback 接口进行操作 。