淘先锋技术网

首页 1 2 3 4 5 6 7

Redis的介绍

Redis是当前互联网最流行的NoSQL缓存数据库,它支持大数据存入缓存,它的读写速度比一般数据库的读写速度快几十到上百倍。Redis不能取代数据库,因为Redis不是持久化存储,它是存储在缓存之中的。

Redis的应用场景

  1. 经常需要使用的数据,写操作不是很频繁的,可以存入Redis
  2. 高速读、写场合,比如一瞬间需要向数据库插入千万级的数据,很容易造成数据库瘫痪,并且速度很慢,很难使系统达到高速响应的性能。这个时候我们可以使用异步写入数据库的方式,先将数据缓存到Redis中,当达到一定的条件,再批量将数据写入到数据库中,完成数据持久化的过程。

Redis取数据的流程

Redis支持的数据类型

  1. 字符串
  2. 哈希结构
  3. 列表
  4. 集合
  5. 可排序集合
  6. 基数

Redis的安装

windos安装

  1. 下载
    Redis下载地址
    在这里插入图片描述
  2. 解压
    在这里插入图片描述
  3. 在当前目录下创建一个startup.bat文件输入以下命令,方便开启redis服务
    redis-server redis.windows.conf
    
  4. 双击startup.bat,启动redis
    在这里插入图片描述
  5. 双击redis-cli.exe,启动redis自带的客户端工具,输入以下命令测试一下
    在这里插入图片描述

Linux安装

#1. 下载
[root@Centos100 redis]# wget http://download.redis.io/releases/redis-4.0.0.tar.gz

#2. 解压
[root@Centos100 redis]# tar -zvxf redis-4.0.0.tar.gz

#3. 将解压目录移动到/usr/local/下,并重命名为redis
[root@Centos100 redis]# mv redis-4.0.0 /usr/local/redis

#4. 前往redis根目录
[root@Centos100 redis]# cd /usr/local/redis

#5. 编译
[root@Centos100 redis]# make

#6. 安装
[root@Centos100 redis]# make PREFIX=/usr/local/redis install

#7. 修改redis.conf,确保外机可以访问
[root@Centos100 redis]# vi redis.conf
#bind 127.0.0.1
protected-mode no

#8. 编写启动脚本
[root@Centos100 redis]# vi start-redis.sh
./bin/redis-server redis.conf

#9. 启动脚本
[root@Centos100 redis]# ./start-redis.sh &

Redis应用

JAVA API环境搭建

<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-pool2 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.9.0</version>
</dependency>
import java.util.Map;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public class JedisConnection {
	
	//最大空闲数
	private int maxIdle = 5;
	//最大连接数
	private int maxTotal = 10;
	//最大等待毫秒数
	private long maxwaitMillis = 2000L;
	//连接主机
	private String hostname = "localhost";
	//练级端口
	private int port = 6379;
	//jedisPool连接池
	private JedisPool jedisPool;
	
	private JedisConnection() {
		try {
			//这边是我自定义的xml配置文件读取jedis配置的
			Map<String, String> readXML = JedisConfLoad.readXML();
			this.maxIdle = Integer.parseInt(readXML.get("maxIdle"));
			this.maxTotal = Integer.parseInt(readXML.get("maxTotal"));
			this.maxwaitMillis = Long.parseLong(readXML.get("maxwaitMillis"));
			this.hostname = readXML.get("hostname");
			this.port = Integer.parseInt(readXML.get("port"));
			this.jedisPool = getJedisPool();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	private static JedisConnection jedisConnection = new JedisConnection();
	
	public static JedisConnection getInstance() {
		return jedisConnection;
	}
	
	/**
	 * redis连接池
	 * @return
	 */
	private JedisPool getJedisPool() {
		JedisPoolConfig poolConfig = new JedisPoolConfig();
		//最大空闲数
		poolConfig.setMaxIdle(maxIdle);
		//最大连接数
		poolConfig.setMaxTotal(maxTotal);
		//最大等待毫秒数
		poolConfig.setMaxWaitMillis(maxwaitMillis);
		JedisPool pool = new JedisPool(poolConfig, hostname, port);
		return pool;
	}
	
	/**
	 * Jedis连接
	 * @return
	 */
	public Jedis getJedisConnection() {
		return jedisPool.getResource();
	}

	/**
	 * 回收Jedis连接
	 * @return
	 */
	public void returnResource(Jedis jedis) {
		if(jedis != null){
			jedis.close();
		}
	}
}

Spring JAVA API环境搭建

<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-pool2 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.9.0</version>
</dependency>

<!--注意你选择的jedis版本和spring-data-redis之间的兼容问题-->
<!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-redis -->
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-redis</artifactId>
    <version>1.8.1.RELEASE</version>
</dependency>

<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-commons</artifactId>
    <version>1.8.1.RELEASE</version>
</dependency>

<!-- redis连接池配置 -->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
	<!-- 最大空闲数 -->
	<property name="maxIdle" value="5"></property>
	<!-- 最大连接数 -->
	<property name="maxTotal" value="10"></property>
	<!-- 最大等待时间 -->
	<property name="maxWaitMillis" value="2000"></property>
</bean>

<!-- redis连接工厂 -->
<bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
	<property name="hostName" value="localhost"></property>
	<property name="port" value="6379"></property>
	<property name="poolConfig" ref="poolConfig"></property>
</bean>

<bean id="jdkSerializationRedisSerializer" class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"></bean>
<bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer"></bean>

<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
	<property name="connectionFactory" ref="connectionFactory"></property>
	<!--key的可序列化类型-->
	<property name="keySerializer" ref="stringRedisSerializer"></property>
	<!--val的可序列化类型-->
	<property name="valueSerializer" ref="stringRedisSerializer"></property>
</bean>

字符串

Redis命令
#1. 设置键值对,有就修改,没有就增加一对键值对
#set key value [EX seconds] [PX milliseconds] [NX|XX]
127.0.0.1:6379> set name zhangsan
OK

#2. 设置键值对,有就修改,没有就增加一对键值对,有效时间为10秒
127.0.0.1:6379> set name wangwu EX 10
OK

#3. 设置键值对,有就修改,没有就增加一对键值对,有效时间为1000毫秒
127.0.0.1:6379> set name wangwu PX 1000
OK

#4. 设置键值对,有就不修改,没有就增加一对键值对
127.0.0.1:6379> set name wangwu NX
OK

#5. 设置键值对,有就修改,没有就不执行
127.0.0.1:6379> set name wangwu XX
OK

#6. 通过键获取值
#get key
127.0.0.1:6379> get name
"zhangsan"

#7. 通过键删除值,可以同时删除多个key
#del key [key ...]
127.0.0.1:6379> del name age

#8. 求key的value值得长度
#strlen key
127.0.0.1:6379> strlen name
(integer) 8

#9. 修改原来key的值,并将旧值返回,如果原来没有这个key,会返回(nil),并添加新的键值对
#getset key value
127.0.0.1:6379> getset name lisi
"zhangsan"

#10. 截取子串,从0开始计算
#getrange key start end
127.0.0.1:6379> getrange name 1 3
"isi"

#11. 将字符串拼接到原字符串的后面,如果没有这个key,就新增一对键值对
#append key value
127.0.0.1:6379> append name zhangsan
(integer) 12

#12. 如果字符串是整数类型。则可以使用以下命令在原字段上加1
#incr key
127.0.0.1:6379> incr age
(integer) 24

#13. 如果字符串是整数类型。则可以使用以下命令在原字段上加自定义的数值(可以为负数)
#incrby key increment
127.0.0.1:6379> incrby age 10
(integer) 34

#14. 如果字符串是整数类型。则可以使用以下命令在原字段上减1
#decr key
127.0.0.1:6379> decr age
(integer) 33

#15. 如果字符串是整数类型。则可以使用以下命令在原字段上减自定义的数值(可以为负数)
#decrby key decrement
127.0.0.1:6379> decrby age 10
(integer) 23

#16. 如果字符串是整数或浮点数类型。则可以使用以下命令在原字段上加自定义的浮点数(可以为负数)
#incrbyfloat key increment
127.0.0.1:6379> incrbyfloat age 1.5
"24.5"

#17. 查看key的超时时间(-1 代表没有超时时间,如果不存在 key 或者 key 已经超时则为 -2)
#ttl key(以秒计算)
127.0.0.1:6379> ttl name
(integer) 6
#pttl name(以毫秒计算)
127.0.0.1:6379> pttl name
(integer) 92733

#18. 持久化key
#persist key
127.0.0.1:6379> persist name

#19. 设置key的超时时间
#expire key seconds(以秒计算)
127.0.0.1:6379> expire name 100
(integer) 1
#pexpire key milliseconds(以毫秒计算)
127.0.0.1:6379> pexpire name 10000
(integer) 1

#20. 设置key的超时时间点
#expireat key timestamp(以秒计算的时间戳)
127.0.0.1:6379> expireat name 1620620650
#Pexpireat key milliseconds-timestamp(以毫秒计算的时间戳)
127.0.0.1:6379> Pexpireat name 1620620650000
JAVA API
	public static void stringJedisTest() {
		JedisConnection jedisConn = JedisConnection.getInstance();
		Jedis jedis = jedisConn.getJedisConnection();
		//set name zhangsan
		jedis.set("name", "zhangsan");
		jedis.set("age", "23");
		//set name lisi NX EX 10
		jedis.set("name", "lisi", "NX", "EX", 10);
		//get name
		String val = jedis.get("name");
		//strlen name
		Long strlen = jedis.strlen("name");
		//getset name wangwu
		String oldVal = jedis.getSet("name", "wangwu");
		//getrange name 0 2
		String substrName = jedis.getrange("name", 0, 2);
		//append name zhaoliu
		jedis.append("name", "zhaoliu");
		//incr age
		jedis.incr("age");
		//incrby age 10
		jedis.incrBy("age", 10);
		//decr age
		jedis.decr("age");
		//decrby age 10
		jedis.decrBy("age", 10);
		//incrbyfloat age 1.5
		jedis.incrByFloat("age", 1.5);
		//del name
		Long del = jedis.del("name");
	}
Spring API
@Autowired
private RedisTemplate<String,String> redisTemplate;

	public String redisTest() {
		//设值
		redisTemplate.opsForValue().set("name", "zhangsan");
		redisTemplate.opsForValue().set("age", "23");
		
		//获取name的长度
		Long size = redisTemplate.opsForValue().size("name");
		//获取老的name的值,并设置name的新值
		String oldName = redisTemplate.opsForValue().getAndSet("name", "lisi");
		
		//追加字符串到末尾返回新串的长度
		Integer newSize = redisTemplate.opsForValue().append("name", "_wangwu");
		
		//获取值
		String name = redisTemplate.opsForValue().get("name");
		//获取子串
		String nameChild = redisTemplate.opsForValue().get("name",0,2);
		
		//age + 1
		redisTemplate.opsForValue().increment("age", 1);
		
		String age = redisTemplate.opsForValue().get("age");
		System.out.println("oldName:" + oldName + ";newSize:" + newSize + ";name:" + name + ";nameChild:"+ nameChild +";age:" + age + ";name的长度:" + size);

		SessionCallback<String> callback = new SessionCallback<String>() {
			@Override
			public String execute(RedisOperations ops) throws DataAccessException {
				// TODO Auto-generated method stub	
				ops.boundValueOps("name").set("zhangsan");
		        String keyValue = (String)ops.boundValueOps("key1").get();
		        //ttl name
		        Long expSecond = ops.getExpire("name");
		        System.err.println(expSecond);
		        boolean b = false;
		        //expire name 120
		        b = ops.expire("name", 120L, TimeUnit.SECONDS);
		        //persist name
		        b = ops.persist("name");
		        Long l = 0L;
		        //ttl name
		        l = ops.getExpire("name");
		        Long now = System.currentTimeMillis();
		        Date date = new Date();
		        date.setTime(now + 120000);
		        //Pexpireat name now + 120000
		        ops.expireAt("name", date);
		        return null;
			}
		};
		String execute = redisTemplate.execute(callback);
		return null;
	}

Redis存储hash结构

#1. 存储一个hash值
#hmset key field value [field value ...]
127.0.0.1:6379> hmset student id 001 name zhangsan age 23 hobby tiaowu height 1.63
OK

#2. 查看hash结构所有的键值
#hgetall key
127.0.0.1:6379> hgetall student
1) "id"
2) "001"
3) "name"
4) "zhangsan"
5) "age"
6) "23"
7) "hobby"
8) "tiaowu"
9) "height"
10) "1.63"

#3. 删除某个hash结构里面的field
#hdel key field [field ...]
127.0.0.1:6379> hdel student hobby
(integer) 1

#4. 判断某个hash结构里面是否包含field,包含返回1,不包含返回0
#hexists key field
127.0.0.1:6379> hexists student hobby
(integer) 0

#5. 给指定的hash结构里的field加指定整数,要求filed是整数字符串
#hincrby key field increment
127.0.0.1:6379> hincrby student age 1
(integer) 24

#6. 给指定的hash结构里的field加指定浮点数,要求field是浮点数字符串
#hincrbyfloat key field increment
127.0.0.1:6379> hincrbyfloat student height 0.2
"1.83"

#7. 返回hash结构中所有的filed
#hkeys key
127.0.0.1:6379> hkeys student
1) "id"
2) "name"
3) "age"
4) "height"

#8. 返回hash结构中key<->value的数量
#hlen key
127.0.0.1:6379> hlen student
(integer) 4

#9. 返回hash结构中指定的filed的值,可以是多个
#hmget key field [field ...]
127.0.0.1:6379> hmget student id name
1) "001"
2) "zhangsan"

#10. hash结构设置多个键值对,有就修改,没有就新增
#hmset key field value [field value ...]
127.0.0.1:6379> hmset student age 26 weight 50
OK

#11. hash结构设置单个键值对,有就修改,没有就新增
#hset key field value
127.0.0.1:6379> hset student age 28
(integer) 0

#12. hash结构中新增键值对,如果键值对已存在,不做任何操作
#hsetnx key field value
127.0.0.1:6379> hsetnx student age 30
(integer) 0

#13. 获取hash结构中所有的值
#hvals key
127.0.0.1:6379> hvals student
1) "001"
2) "zhangsan"
3) "28"
4) "1.63"
5) "50"
6) "beigin"
JAVA API

JAVA API里面的方法名都是命令的名称,一看就知道是什么意思,这里我就不多举例了

Spring JAVA API
	public String redisHashTest() {
		Map<String,String> studentMap = new HashMap<String,String>();
		studentMap.put("id", "001");
		studentMap.put("name", "zhangsan");
		studentMap.put("age", "23");
		studentMap.put("height", "1.62");
		//hmset student001 id 001 name zhangsan age 23 height 1.62
		redisTemplate.opsForHash().putAll("student001", studentMap);
		
		//hgetall student001
		Map map = redisTemplate.opsForHash().entries("student001");
		for(Object key : map.keySet()) {
			System.out.println(key + "..." + map.get(key));
		}
		
		//hdel student001 name
		redisTemplate.opsForHash().delete("student001", "name");
		
		//hexists student001 name
		Boolean flag = redisTemplate.opsForHash().hasKey("student001", "name");
		
		//hincrby student001 age 1
		redisTemplate.opsForHash().increment("student001", "age", 1);
		
		//hincrbyfloat student001 height 0.2
		redisTemplate.opsForHash().increment("student001", "height", 0.2);
		
		//hkeys student001
		Set<Object> keys = redisTemplate.opsForHash().keys("student001");
		
		//hlen student001
		Long size = redisTemplate.opsForHash().size("student001");
		
		//hmget student001 id age
		List<Object> keyList = new ArrayList<Object>();
		keyList.add("id");
		keyList.add("age");
		List<Object> list = redisTemplate.opsForHash().multiGet("student001", keyList);
		
		//hsetnx student001 hobby tiaowu
		redisTemplate.opsForHash().putIfAbsent("student001", "hobby", "tiaowu");
		return null; 
	}

链表

#1. 生成一个链表,左插入操作
#lpush key value [value ...]
127.0.0.1:6379> lpush students student1 student2 student3 student4 student5 student6
(integer) 6

#2. 生成一个链表,右插入操作
#rpush key value [value ...]
127.0.0.1:6379> rpush students2 s1 s2 s3 s4 s5 s6
(integer) 6

#3. 根据索引获取元素(从左往右数,从0开始)
#lindex key index
127.0.0.1:6379> lindex students 4
"student2"
127.0.0.1:6379> lindex students2 4
"s5"

#4. 计算链表的长度
#llen key
127.0.0.1:6379> llen students
(integer) 6

#5. 删除左边第一个节点并返回
#lpop key
127.0.0.1:6379> lpop students
"student6"

#6. 删除右边第一个节点并返回
#rpop key
127.0.0.1:6379> rpop students2
"s6"

#7. 在指定节点前面|后面插入一个节点
#linsert key BEFORE|AFTER pivot value
127.0.0.1:6379> linsert students before|after student3 studentBF
(integer) 6

#8. 如果链表存在就在最左边插入一个新的节点
#lpushx key value
127.0.0.1:6379> lpushx students2 s2
(integer) 6

#9. 如果链表存在就在最右边插入一个新的节点
#rpushx key value
127.0.0.1:6379> rpushx students2 s7
(integer) 7

#10. 获取索引[start,end]的值
#lrange key start stop
127.0.0.1:6379> lrange students 0 4
1) "student5"
2) "student4"
3) "studentBF"
4) "student3"
5) "student2"

#11. 如果count为0,则删除所有等于s2节点的节点,否则,则删除指定数量等于s2的节点,从左往右删
#lrem key count value
127.0.0.1:6379> lrem students2 1 s2
(integer) 1

#12. 设置链表下标为2的节点的值为s5
#lset key index value
127.0.0.1:6379> lset students2 2 s5
OK

#13. 截取链表,指保留索引[start,end]的链表,其余全部删除
#ltrim key start stop
127.0.0.1:6379> ltrim students 0 2
OK

#14. 清空链表
#del key [key ...]
127.0.0.1:6379> del students
(integer) 1
JAVA API

JAVA API里面的方法名都是命令的名称,一看就知道是什么意思,这里我就不多举例了

Spring JAVA API
	public String redisLinkTest() throws Exception {
		//lpush students student1
		redisTemplate.opsForList().leftPush("students", "student1");
		
		//lpush students student2 student3 student4 student5
		List<String> list = new ArrayList<String>();
		for(int i = 2; i < 6 ; i++) {
			list.add("student" + i);
		}
		redisTemplate.opsForList().leftPushAll("students", list);
		
		//rpush students2 student2 student3 student4 student5
		redisTemplate.opsForList().rightPushAll("students2", list);
		
		//lindex students 2
		redisTemplate.opsForList().index("students", 2);
		
		//llen students2
		Long size = redisTemplate.opsForList().size("students2");
		
		//lpop students
		String leftPop = redisTemplate.opsForList().leftPop("students2");
		
		//rpop students2
		String rightPop = redisTemplate.opsForList().rightPop("students");
		
		//linsert students before student4 studentBF
		redisTemplate.getConnectionFactory().getConnection().lInsert("students".getBytes("UTF-8"), Position.BEFORE, "student4".getBytes("UTF-8"), "studentBF".getBytes("UTF-8"));
		
		//lpushx students studentHead
		redisTemplate.opsForList().leftPushIfPresent("students", "studentHead");
		
		//rpushx students2 studentEnd
		redisTemplate.opsForList().rightPushIfPresent("students2", "studentEnd");
		
		//lrange students 0 4
		List<String> range = redisTemplate.opsForList().range("students", 0, 4);
		
		//lrem students 3 students2
		redisTemplate.opsForList().remove("students", 3, "students2");
		
		//lset students 0 studentsNew
		redisTemplate.opsForList().set("students", 0, "studentsNew");
		
		//ltrim students 0 4
		redisTemplate.opsForList().trim("students", 0, 4);

		//del students
		redisTemplate.delete("students");
		
		return null;
	}

无序集合

  1. 对集合而言,他们的每一个元素都是不能重复的,当插入相同记录的时候都会失败
  2. 集合是无序的
  3. 集合的每一个元素都是String类型
#1. 给key为list的集合增加成员
#sadd key member [member ...]
127.0.0.1:6379> sadd list s1 s2 s3 s4 s5 s6 s7
(integer) 7
127.0.0.1:6379> sadd list2 s1 s3 s5 s7 s9 s11 s13 s15
(integer) 8

#2. 计算key为list集合的成员数
#scard key
127.0.0.1:6379> scard list
(integer) 7

#3. 计算第一个集合中成员不属于后面的集合的成员
#sdiff key [key ...]
127.0.0.1:6379> sdiff list list2
1) "s2"
2) "s6"
3) "s4"

#4. 计算第一个集合中成员不属于后面的集合的成员,并存入集合des
#sdiffstore destination key [key ...]
127.0.0.1:6379> sdiffstore des list list2
(integer) 3

#5. 计算两个集合的交集
#sinter key [key ...]
127.0.0.1:6379> sinter list list2
1) "s1"
2) "s3"
3) "s5"
4) "s7"

#6. 将list和list2的交集集合赋给集合des
#sinterstore destination key [key ...]
127.0.0.1:6379> sinterstore des list list2
(integer) 4

#7. 判断s5是否为集合list的成员,是则返回1,否则返回0
#sismember key member
127.0.0.1:6379> sismember list s5
(integer) 1

#8. 返回集合list所有的成员
#smembers key
127.0.0.1:6379> smembers list
1) "s1"
2) "s3"
3) "s2"
4) "s6"
5) "s5"
6) "s4"
7) "s7"

#9. 从集合list中将成员s4移除出来存入集合des
#smove source destination member
127.0.0.1:6379> smove list des s4
(integer) 1

#10. 随机弹出一个集合成员
#spop key [count]
127.0.0.1:6379> spop list
"s7"

#11. 随机弹出3个集合成员
#srandmember key [count]
127.0.0.1:6379> srandmember list 3
1) "s5"
2) "s6"
3) "s1"

#12. 移除集合中的成员,可以是多个
#srem key member [member ...]
127.0.0.1:6379> srem list s1
(integer) 1

#13. 求两个集合的并集
#sunion key [key ...]
127.0.0.1:6379> sunion list list2
1) "s11"
2) "s3"
3) "s1"
4) "s15"
5) "s13"
6) "s6"
7) "s5"
8) "s9"
9) "s7"

#14. 将list和list2的并集存入des集合中
#sunionstore destination key [key ...]
127.0.0.1:6379> sunionstore des list list2
(integer) 9
JAVA API

JAVA API里面的方法名都是命令的名称,一看就知道是什么意思,这里我就不多举例了

Spring JAVA API
	public String redisSetTest() {
		//sadd set1 s1 s2 s3 s4
		redisTemplate.boundSetOps("set1").add("s1","s2","s3","s4");
		redisTemplate.boundSetOps("set2").add("s1","v2","s3","v4","s5","v6");
		
		//scard set1
		Long size = redisTemplate.opsForSet().size("set1");
		
		//sdiff set1 set2
		Set<String> difference = redisTemplate.opsForSet().difference("set1", "set2");
		
		//sinter set1 set2
		Set<String> intersect = redisTemplate.opsForSet().intersect("set1", "set2");
		
		//sismember set1 s3
		Boolean member = redisTemplate.opsForSet().isMember("set1", "s3");
		
		//smembers set1
		Set<String> members = redisTemplate.opsForSet().members("set1");
		
		//spop set1
		String pop = redisTemplate.opsForSet().pop("set1");
		
		//spop set1
		String randomMember = redisTemplate.opsForSet().randomMember("set1");
		
		//srandmember set1 3
		List<String> randomMembers = redisTemplate.opsForSet().randomMembers("set1", 3);
		
		//srem set1 s1 s2
		Long remove = redisTemplate.opsForSet().remove("set1", "s1","s2");
		
		//sunion set1 set2
		Set<String> union = redisTemplate.opsForSet().union("set1", "set2");
		
		//sdiffstore set3 set1 set2
		redisTemplate.opsForSet().differenceAndStore("set1", "set2", "set3");
		
		//sinterstore set3 set1 set2
		redisTemplate.opsForSet().intersectAndStore("set1", "set2", "set3");
		
		//sunionstore set3 set1 set2
		redisTemplate.opsForSet().unionAndStore("set1", "set2", "set3");
		return null;
	}

有序集合

#1. 向有序集合中添加成员和成员的排序分数,分数是浮点数类型
#zadd key [NX|XX] [CH] [INCR] score member [score member ...]
127.0.0.1:6379> zadd list1 1 s1 2 s2 3 s3 4 s4
(integer) 4
127.0.0.1:6379> zadd list2 2 s1 3 s2 4 s3 5 s7
(integer) 4

#2. 获取有序集合的成员数
#zcard key
127.0.0.1:6379> zcard list1
(integer) 4

#3. 根据分数范围返回满足条件的成员数,默认包括边界,如果想去除边界,可以使用(2,注意,不支持[]和)
#zcount key min max
127.0.0.1:6379> zcount list1 2 3
(integer) 2

#4. 给集合list1的成员s1增加5分
#zincrby key increment member
127.0.0.1:6379> zincrby list1 5 s1
"6"

#5. 将2个有序集合的交集放入集合des中
#zinterstore destination numkeys key [key ...] [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX]  
127.0.0.1:6379> zinterstore des 2 list1 list2
(integer) 3

#6. 先对集合按照分数从小到大排序,然后计算其下标,从0计数,然后截取下标[1,3]的成员返回,withscores表示是否携带分数返回
#zrange key start stop [WITHSCORES]
127.0.0.1:6379> zrange list1 1 3 [withscores]
1) "s3"
2) "s4"
3) "s1"

#7. 求成员s2在集合list1按照分数从小到大排序后的下标,从0开始计数
#zrank key member
127.0.0.1:6379> zrank list1 s2
(integer) 0

#8. 根据分数区间进行删除,默认包含边界,想不包含可以使用(1
#zremrangebyscore key min max
127.0.0.1:6379> zremrangebyscore list1 1 3
(integer) 2

#9. 按照分数排行从小到大的排序删除,下标从 0 开始计算,包含边界
#zremrangebyrank key start stop
127.0.0.1:6379> zremrangebyrank list1 0 2
(integer) 3

#10. 从大到小的按分数排序展示,下标从 0 开始计算,包含边界
#zrevrange key start stop [WITHSCORES]
127.0.0.1:6379> zrevrange list2 0 4
1) "s7"
2) "s3"
3) "s2"
4) "s1"

#11. 从大到小的按分数排序,求元素的下标,从 0 开始计算
#zrevrank key member
127.0.0.1:6379> zrevrank list2 s2
(integer) 2

#12. 返回成员的分数值
#zscore key member
127.0.0.1:6379> zscore list2 s2
"3"

#13. 求多个有序集合的并集,赋值给des
#zunionstore destination numkeys key [key ...] [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX]
127.0.0.1:6379> zunionstore des 2 list1 list2
(integer) 5

#14. 计算有序集合list2从小到大排序后,[s2,s3]两个成员之间的成员数,[包含,(不包含
#zlexcount key min max
127.0.0.1:6379> zlexcount list2 [s2 [s3
(integer) 2

#15. 计算有序集合list2从小到大排序后,[s2,s3]两个成员之间的成员,[包含,(不包含
#zrangebylex key min max [LIMIT offset count]
127.0.0.1:6379> zrangebylex list2 [s1 [s3 
1) "s1"
2) "s2"
3) "s3"

#16. 对上一个操作进行过滤,下标从0开始,取2个成员
#zrangebylex key min max [LIMIT offset count]
127.0.0.1:6379> zrangebylex list2 [s1 [s3 limit 0 2
1) "s1"
2) "s2"

#17. 根据积分求符合范围的成员,包含边界,从小到大排序
#zrangebyscore key min max [WITHSCORES] [LIMIT offset count]
127.0.0.1:6379> zrangebyscore list2 0 3
1) "s1"
2) "s2"

#18. 根据积分求符合范围的成员,包含边界,从小到大排序,带积分
127.0.0.1:6379> zrangebyscore list2 0 3 withscores
1) "s1"
2) "2"
3) "s2"
4) "3"

#19. 对上一个操作进行过滤,下标从0开始,取1个成员
127.0.0.1:6379> zrangebyscore list2 0 3 withscores limit 0 1
1) "s1"
2) "2"

#20. 根据积分求符合范围[5,3]的成员,包含边界,从大到小排序,注意这里是max在前,min在后
#zrevrangebyscore key max min [WITHSCORES] [LIMIT offset count]
127.0.0.1:6379> zrevrangebyscore list2 5 3
1) "s7"
2) "s3"
3) "s2"

#21. 计算有序集合list2从小到大排序后,删除[s2,s3]两个成员之间的成员,[包含,(不包含
#zremrangebylex key min max
127.0.0.1:6379> zremrangebylex list2 [s2 [s3
(integer) 2
JAVA API
JAVA Spring API
	public String redisTupleTest() {
		Set<TypedTuple<String>> set1 = new HashSet<TypedTuple<String>>();
		Set<TypedTuple<String>> set2 = new HashSet<TypedTuple<String>>();
		
		int j = 9;
		for(int i = 1 ; i <= 9 ; i++) {
			j --;
			Double score1 = Double.valueOf(i);
			String value1 = "x" + i;
			Double score2 = Double.valueOf(j);
			String value2 = j % 2 == 1 ? "y" + i : "x" + i;
			TypedTuple<String> tuple1 = new DefaultTypedTuple<>(value1, score1);
			set1.add(tuple1);
			TypedTuple<String> tuple2 = new DefaultTypedTuple<>(value2, score2);
			set2.add(tuple2);
		}
		
		//zadd zset1 1 x1 2 x2 3 x3 4 x4 5 x5 6 x6 7 x7 8 x8 9 x9
		redisTemplate.opsForZSet().add("zset1", set1);
		redisTemplate.opsForZSet().add("zset2", set2);
		
		//zcard zset1
		Long zCard = redisTemplate.opsForZSet().zCard("zset1");
		
		//zcount zset1 3 5
		Long count = redisTemplate.opsForZSet().count("zset1", 3, 5);
		
		//zrange zset1 1 5
		Set<String> range = redisTemplate.opsForZSet().range("zset1", 1, 5);
		
		//zrange zset1 1 -1 withscores
		Set<TypedTuple<String>> rangeWithScores = redisTemplate.opsForZSet().rangeWithScores("zset1", 0, -1);
		
		//zinterstore inter_zset 2 zset1 zset2
		Long intersectAndStore = redisTemplate.opsForZSet().intersectAndStore("zset1", "zset2", "inter_zset");
		
		//zrangebylex zset1 [x4 (x8
		Range range2 = Range.range();
		range2.lt("x8"); //小于
		range2.gte("x4");
		Set<String> rangeByLex = redisTemplate.opsForZSet().rangeByLex("zset1", range2);
		
		//zrangebylex list2 [x4 (x8 limit 4 5
		Limit limit = Limit.limit();
		limit.count(4);
		limit.offset(5);
		Set<String> rangeByLex2 = redisTemplate.opsForZSet().rangeByLex("zset1", range2, limit);
		
		//zrank zset1 x4
		Long rank = redisTemplate.opsForZSet().rank("zset1", "x4");
		
		//
		Long remove = redisTemplate.opsForZSet().remove("zset1", "x5" , "x6");
		
		//zremrangebyrank zset1 1 2
		Long removeRange = redisTemplate.opsForZSet().removeRange("zset1", 1, 2);
		
		//zincrby zset1 11 x1
		redisTemplate.opsForZSet().incrementScore("zset1", "x1", 11);
		
		//zremrangebyscore zset1 1 2
		redisTemplate.opsForZSet().removeRangeByScore("zset1", 1, 2);
		
		//zrevrange zset1 1 10
		Set<TypedTuple<String>> reverseRangeWithScores = redisTemplate.opsForZSet().reverseRangeWithScores("zset1", 1, 10);
		
		return null;
	}

事务

  • 事务是一个被隔离的操作,事务中的方法都会被 Redis 进行序列化并按顺序执行,事务在执行的过程中不会被其他客户端发生的命令所打断。
  • 事务是一个原子性的操作,它要么全部执行,要么就什么都不执行。
  • 在执行事务命令的时候,在命令入队的时候,Redis 就会检测事务的命令格式是否正确,如果不正确则会产生错误。无论之前和之后的命令都会被事务所回滚,就变为什么都没有执行。
  • 当命令格式正确,而因为操作数据结构引起的错误,则该命令执行出现错误,而其之前和之后的命令都会被正常执行。这点和数据库很不一样,这是需要注意的地方。
# 开启事务
127.0.0.1:6379> multi
OK

127.0.0.1:6379> set name zhangsan
QUEUED
127.0.0.1:6379> get name
QUEUED

#回滚事务
#127.0.0.1:6379> discard
#OK

#提交事务
127.0.0.1:6379> exec
1) OK
2) "zhangsan"
127.0.0.1:6379> get name
"zhangsan"
JAVA API
Spring JAVA API
	public String redisMultiTest() {
		SessionCallback<String> callback = new SessionCallback<String>() {
			@Override
			public String execute(RedisOperations ops) throws DataAccessException {
				// TODO Auto-generated method stub	
				//开启事务
				ops.multi();
				ops.boundValueOps("name").set("zhangsan");
				//提交事务
				List exec = ops.exec();
				return redisTemplate.opsForValue().get("name");
			}
		};
		//执行事务
		String execute = redisTemplate.execute(callback);
		System.out.println(execute);
		return null;
	}

乐观锁

在multi命令之前使用watch命令监控某些键值对,来决定事务是执行还是回滚。当Redis使用exec命令执行事务的时候,它首先会去比对watch命令所监控的键值对,没有发生变化,则提交事务,否则,回滚事务,这就是Redis所支持的乐观锁
demo:

  1. 在执行事务之前修改了money的值,导致触发乐观锁,事务回滚在这里插入图片描述
  2. 事务提交之前,money没有发生变化,乐观锁没有触发,所以事务提交成功
    在这里插入图片描述

Redis流水线

在实际操作中,一个一个的发送命令给Redis服务器,这会造成非常大的网络延时,Redis也会因此将大部分时间用于等待接收命令上,为了提高Redis的使用效率,减少网络延时,可以使用Redis流水线,Redis流水线是一种通信协议,没有办法使用客户端演示,但可以使用JAVA API和Spring API操作。

JAVA API
public static void testPipeline() {
	JedisConnection jedisConn = JedisConnection.getInstance();
	Jedis jedis = jedisConn.getJedisConnection();
	//开启流水线
	Pipeline pipelined = jedis.pipelined();
	for(int i = 0; i < 100000; i++) {
		int j = i + 1;
		//写
		pipelined.set("key" + j, "value" + i);
		//读
		pipelined.get("key" + j);
	}
	//执行同步但不携带返回结果
	//pipelined.sync();
	//执行同步携带返回结果
	List<Object> syncAndReturnAll = pipelined.syncAndReturnAll();
}
Spring API
public String testPipeline() {
	SessionCallback<String> callback = new SessionCallback<String>() {
		@Override
		public String execute(RedisOperations ops) throws DataAccessException {
			// TODO Auto-generated method stub
			for(int i = 0; i < 10000; i++) {
				int j = i + 1;
				ops.boundValueOps("key" + i).set("value" + j);
				ops.boundValueOps("key" + i).get();
			}
			return null;
		}
	};
	
	List<Object> result = redisTemplate.executePipelined(callback);
	return null;
}

发布订阅

#1. 打开一个客户端输入以下命令监听chat频道
127.0.0.1:6379> SUBSCRIBE chat
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "chat"
3) (integer) 1
#2. 打开新的客户端,向chat频道发送消息,注意有
127.0.0.1:6379> publish chat "hello world!"
(integer) 1

在这里插入图片描述

JAVA API
	/**
	 * 接收消息
	 */
	public static void getMessage() {
		JedisConnection jedisConn = JedisConnection.getInstance();
		Jedis jedis = jedisConn.getJedisConnection();
		
		jedis.subscribe(new JedisMessage(), "chat");
		
	}
	
	/**
	 * 发送消息
	 */
	public static void sendMessage() {
		JedisConnection jedisConn = JedisConnection.getInstance();
		Jedis jedis = jedisConn.getJedisConnection();
		jedis.publish("chat", "aaa");
	}

监听接收消息的类:

import redis.clients.jedis.JedisPubSub;
public class JedisMessage extends JedisPubSub{

	@Override
	public void onMessage(String channel, String message) {
		// TODO Auto-generated method stub
		System.out.println(channel + "....." + message);
	}
}
Spring API

发送消息:

public String sendMessage() {
	String channel = "chat";
    redisTemplate.convertAndSend(channel, "I am lazy!!");
    return null;
}

监听接收消息的类:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

@Component("redisMsgListener")
public class RedisMessageListener implements MessageListener {
	
	@Autowired
	private RedisTemplate<String,String> redisTemplate;

	@Override
	public void onMessage(Message message, byte[] bytes) {
		// TODO Auto-generated method stub
		// 获取消息
        byte[] body = message.getBody();
        // 使用值序列化器转换
        String msgBody = (String) redisTemplate.getValueSerializer()
                .deserialize(body);
        System.err.println(msgBody);
        // 获取 channel
        byte[] channel = message.getChannel();
        // 使用字符串序列化器转换
        String channelStr =  redisTemplate.getStringSerializer()
                .deserialize(channel);
        System.err.println(channelStr);
        // 渠道名称转换
        String bytesStr = new String(bytes);
        System.err.println(bytesStr);
	}
}

Springmvc配置消息订阅:

<bean id="topicContainer" class="org.springframework.data.redis.listener.RedisMessageListenerContainer" destroy-method="destroy">
   	<!--Redis连接工厂 -->
   	<property name="connectionFactory" ref="connectionFactory" />
   	<!--连接池,这里只要线程池生存,才能继续监听 -->
   	<property name="taskExecutor">
       	<bean class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler">
           	<property name="poolSize" value="3" />
       	</bean>
   	</property>
   	<!--消息监听Map -->
   	<property name="messageListeners">
       	<map>
           	<!-- 配置监听者,key-ref和bean id定义一致 -->
           	<entry key-ref="redisMsgListener">
               	<list>
               		<!--监听具体的频道 -->
	               	<bean class="org.springframework.data.redis.listener.ChannelTopic">
	                   	<constructor-arg value="chat" />
	               	</bean>
	               	<!-- 模式订阅,支持模式匹配订阅,*为模糊匹配符 -->
                    <bean class="org.springframework.data.redis.listener.PatternTopic">
                        <constructor-arg value="topic.*" />
                    </bean>
                    <!-- 监听所有频道 -->
                	<bean class="org.springframework.data.redis.listener.PatternTopic">
                    	<constructor-arg value="*" />
                	</bean>
                </list>
           	</entry>
       	</map>
   	</property>
</bean>