Redis-06Redis数据结构--集合Set

版权声明:【show me the code ,change the world】 https://blog.csdn.net/yangshangwei/article/details/82851869

概述

Redis 的集合不是一个线性结构,而是一个哈希表结构,它的内部会根据 hash 分子来存储和查找数据,理论上一个集合可以存储2的32次方减一(约42亿)个元素。

因为采用哈希表结构,所以对于 Redis 集合的插入、删除和查找的复杂度都是 0(1),只是我们需要注意 3 点

  1. 对于集合而言,它的每一个元素都是不能重复的,当插入相同记录的时候都会失败
  2. 集合是无序的
  3. 集合的每一个元素都是 String 数据结构类型

常用集合命令

官网: https://redis.io/commands#set

Redis 的集合可以对于不同的集合进行操作,比如求出两个或者以上集合的交集、 差集和并集等

命令 说明 备注
sadd key member1 [member2 member3 …] 给键为 key 的集合增加成员 可以同时增加多个
scard key 统计键为 key 的集合成员数
sdiff key1 [key2] 找出两个综合的差集 参数如果是单 key,那么 Redis 就返回这个 key 的所有元素
sdiffstore des key1 [key2] 先按 sdiff命令的规则,找出 key1 和 key2 两个集合的差集,然后将其保存到 des 集合
sinter key1 [key2] 求 key1 和 key2 两个集合的交集。 参数如果是单 key,那么 Redis 就返回这个 key 的所有元素
sinterstore des key1 key2 先按 sinter 命令的规则,找出 key1和 kye2两个集合的交集,然后保存到 des 中
sismember key member 判断 member 是否键为 key 的集合的成员 如果是返回1 , 否则返回0
smembers key 返回集合所有成员 如果数据最大,需要考虑法代泡历的问题
smove src des member 将成员 member 从集合 src 迁移到集合 des 中
spop key 随机弹出集合的一个元素 注意其随机性 , 因为集合是无序的
srandmember key [count] 随机返回集合 中一个或者多个元素 , count为限制返回总数,如果 count 为负数 , 则先求其绝对值 count 为整数,如果不填默认为1,如果count 大于等于集合总数, 则返回整个集合
srem key member1 [ member2 …] 移除集合中 的元素,可以是多个元素 对于很大 的集合可以通过它删除部分元素,避免删除大量数据引发 Redis 停顿
sunion key1 [key2] 求两个集合的并集 参数如果是单 key,那么 Redis 就返回这个 key 的所有元索
sunionstore des key1 key2 先执行 sunion 命令求 出并集,然后保存到键为 des 的集合中

上述命令的前缀都包含 了 一个 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 接口进行操作 。


代码

代码托管到了 https://github.com/yangshangwei/redis_learn

猜你喜欢

转载自blog.csdn.net/yangshangwei/article/details/82851869
今日推荐