dsg_24 Redis


redis命令大全字典: https://m.php.cn/manual/view/16071.html

Redis的介绍

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

NOSQL

适用场景
  • 对数据高并发的读写
  • 海量数据的读写
  • 对数据高可扩展性的
不适用场景
  • 需要事务支持
  • 基于sql的结构化查询存储,处理复杂的关系,需要即席查询。
常见的NOSQL数据库
  • Memcache
    • 很早出现的NoSql数据库
    • 数据都在内存中,一般不持久化
    • 支持简单的key-value模式,支持类型单一
    • 一般是作为缓存数据库辅助持久化的数据库
  • Redis
    • 几乎覆盖了Memcached的绝大部分功能
    • 数据都在内存中,支持持久化,主要用作备份恢复
    • 除了支持简单的key-value模式,还支持多种数据结构的存储,比如 list、set、hash、zset等。
    • 一般是作为缓存数据库辅助持久化的数据库
  • MongoDB
    • 高性能、开源、模式自由(schema free)的文档型数据库
    • 数据都在内存中, 如果内存不足,把不常用的数据保存到硬盘
    • 虽然是key-value模式,但是对value(尤其是json)提供了丰富的查询功能
    • 支持二进制数据及大型对象
    • 可以根据数据的特点替代RDBMS ,成为独立的数据库。或者配合RDBMS,存储特定的数据。

Redis的应用场景

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

Redis底层原理

Redis底层采用的是单线程 + 多路IO复用技术,而memcached底层使用的是串行 vs 多线程 + 锁。Redis默认16个数据库,类似数组下标从0开始,初始默认使用0号库。使用命令 select <dbid>来切换数据库。例如:select 1,切换到1号库。

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应用

Redis命令
切换库
#默认有16个库,index从0开始
127.0.0.1:6379> select index			#format
127.0.0.1:6379> select 1				#切换1号库(demo)
key的相关操作
#1. 查看当前库下的key
127.0.0.1:6379> keys pattern			#format
127.0.0.1:6379> keys *					#查看当前库下所有的key(demo)
127.0.0.1:6379> keys k*					#查看当前库下以k开头的所有key(demo)

#2. 判断key是否存在
127.0.0.1:6379> exists key [key ...]	#format
127.0.0.1:6379> exists k1 k2			#判断当前库下是否有key是k1、k2,返回存在的个数(demo)

#3. 查看key的值的数据类型
127.0.0.1:6379> type key				#format
127.0.0.1:6379> type k1					#查看key为k1的值的数据类型(demo)

#4. 删除key
127.0.0.1:6379> del key [key ...]		#format
127.0.0.1:6379> del name k1				#删除key为name和k1,返回删除的个数(demo)

#5. 为key设置过期时间,单位秒
127.0.0.1:6379> expire key seconds		#format
127.0.0.1:6379> expire k2 10			#为key是k2设定10秒过期时间(demo)

#6. 查看key的剩余过期时间,单位秒
127.0.0.1:6379> ttl key					#format
127.0.0.1:6379> ttl k2					#查看k2的剩余过期时间(demo)

#7. 查看当前库里key的数量
127.0.0.1:6379> dbsize

#8. 清空当前库(慎用)
127.0.0.1:6379> flushdb

#9. 清空所有库(慎用)
127.0.0.1:6379> flushall
字符串操作
#添加键值对
	#NX:当数据库中key不存在时,可以将key-value添加数据库
	#XX:当数据库中key存在时,可以将key-value添加数据库,与NX参数互斥
	#EX:key的超时秒数
	#PX:key的超时毫秒数,与EX互斥
127.0.0.1:6379> set key value [EX seconds] [PX milliseconds] [NX|XX]	#format
127.0.0.1:6379> set name zhansan					#设置name=zhangsan(demo)
127.0.0.1:6379> set k1 test1 EX 10					#设置k1=test1,超时时间是10s(demo)
127.0.0.1:6379> set k2 test2 PX 10000				#设置k2=test2,超时时间是10000ms(demo)
127.0.0.1:6379> set name lisi NX					#当没有name这个key时,可以设置name=lisi(demo)
127.0.0.1:6379> set name lisi XX					#当有name这个key时,可以设置name=lisi(demo)	

#获取对应键的值
127.0.0.1:6379> get key
127.0.0.1:6379> get name

#在key的值的末尾添加响应的字符串,如果没有这个key则相当于set
127.0.0.1:6379> append key value					#format
127.0.0.1:6379> append name ge						#在key=name的值的末尾添加ge字符串(demo)

#获取key的值的长度
127.0.0.1:6379> strlen key							#format
127.0.0.1:6379> strlen name							#获取key=name的值的长度(demo)

#只有在key不存在时设置key的值
127.0.0.1:6379> setnx key value						#format
127.0.0.1:6379> setnx name wangwu					#当name这个key不存在时,设置name=wangwu,返回修改的个数(demo)

#只有在key的值是数字时才能使用,值自动加1,如果key不存在,则设置其值默认为0再加1
127.0.0.1:6379> incr key							#format
127.0.0.1:6379> incr age							#age的值自动加1(demo)

#只有在key的值是数字时才能使用,值自动减1,如果key不存在,则设置其值默认为0再减1
127.0.0.1:6379> decr key							#format
127.0.0.1:6379> decr age							#age的值自动减1(demo)

#只有在key的值是数字时才能使用,值自动加相应的步长,如果key不存在,则设置其值默认为0再加相应的步长
127.0.0.1:6379> incrby key increment				#format
127.0.0.1:6379> incrby age 5						#age的值自动加5(demo)

#只有在key的值是数字时才能使用,值自动减相应的步长,如果key不存在,则设置其值默认为0再减相应的步长
127.0.0.1:6379> decrby key decrement				#format
127.0.0.1:6379> decrby age 5						#age的值自动减5(demo)

#同时设置多个键值对
127.0.0.1:6379> mset key value [key value ...]		#format
127.0.0.1:6379> mset k1 v1 k2 v2 k3 v3				#批量添加k1=v1,k2=v2,k3=v3(demo)

#同时获取多个键的值
127.0.0.1:6379> mget key [key ...]					#format
127.0.0.1:6379> mget k1 k2 k3						#同时获取k1、k2、k3的值(demo)

#只有在key不存在时设置key的值(注意Redis的原子性,如果这条指令里有一个key存在,则都不执行)
127.0.0.1:6379> msetnx key value [key value ...]	#format
127.0.0.1:6379> msetnx k1 v5 k4 v4					#当k1和k4都不存在的时候,设置k1=v5,k4=v4(demo)

#截取指定key的值的start-end部分
127.0.0.1:6379> getrange key start end				#format
127.0.0.1:6379> getrange say 2 4					#截取key为say的值的2到4的部分,从0开始数(demo)

#从指定的位置开始替换字符串的内容
127.0.0.1:6379> setrange key offset value			#format
127.0.0.1:6379> setrange say 0 zhangsan				#从0开始替换say的值(demo)

#设置键值的同时设置过期时间,单位秒
127.0.0.1:6379> setex key seconds value				#format
127.0.0.1:6379> setex name 10 zhangsan				#设置name=zhangsan,过期10秒(demo)

#获取原来的值,并设置新的值
127.0.0.1:6379> getset key value					#format
127.0.0.1:6379> getset say lisi						#输出say原来的值,并设置新值为lisi(demo)

原子性:
这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)叫原子性。
(1)在单线程中, 能够在单条指令中完成的操作都可以认为是"原子操作",因为中断只能发生于指令之间。
(2)在多线程中,不能被其它进程(线程)打断的操作就叫原子操作。
Redis单命令的原子性主要得益于Redis的单线程,所以说Redis不会有并发操作出现,也就不会发生SQL数据库的那些由于并发导致的问题发生。

链表操作
# 从左边插入一个或多个值
127.0.0.1:6379> lpush key value [value ...]				#format
127.0.0.1:6379> lpush students zhangsan lisi wangwu		#向students链表的左边依次插入zhangsan、lisi、wangwu(demo)

# 从右边插入一个或多个值
127.0.0.1:6379> rpush key value [value ...]				#format
127.0.0.1:6379> rpush students2 zhangsan lisi wangwu	#向students2链表的右边依次插入zhangsan、lisi、wangwu(demo)

# 从左边依次移除值并将该值返回(当所有的值全部被移除,key也会被删除)
127.0.0.1:6379> lpop key								#format
127.0.0.1:6379> lpop students							#移除students链表左边的值并返回(demo)

# 从右边依次移除值并将该值返回(当所有的值全部被移除,key也会被删除)
127.0.0.1:6379> rpop key
127.0.0.1:6379> rpop students							#移除students链表右边的值并返回(demo)

# 从source的右边移除一个值并插入到destination的左边
127.0.0.1:6379> rpoplpush source destination			#format
127.0.0.1:6379> rpoplpush students students2			#移除students右边的值并插入到students2的左边(demo)

# 按照下标元素的范围获取值,左右都能取到
127.0.0.1:6379> lrange key start stop					#format
127.0.0.1:6379> lrange students2 1 3					#获取students2的下标1~3的值(demo)
127.0.0.1:6379> lrange students2 0 -1					#特殊:0左边第一个;-1右边第一个,这个代表获取students2里面所有的值(demo)

# 根据下标获取key里面元素的值
127.0.0.1:6379> lindex key index						#format
127.0.0.1:6379> lindex students2 3						#获取students2下标为3的值(demo)

# 获取链表的长度
127.0.0.1:6379> llen key								#format
127.0.0.1:6379> llen students2							#获取链表students2的长度(demo)

# 在链表从左边开始,第一个pivot值的前面|后面插入值value
127.0.0.1:6379> linsert key BEFORE|AFTER pivot value		#format
127.0.0.1:6379> linsert students2 BEFORE zhangsan huaqiangu	#在链表students2从左边数第一个zhangsan前面插入元素huaqiangu(demo)

# 从左边删除count个value
127.0.0.1:6379> lrem key count value					#format
127.0.0.1:6379> lrem students2 2 zhangsan				#在链表students2左边开始数删除前2个zhangsan(demo)

# 从左边数第index值替换为value
127.0.0.1:6379> lset key index value					#format
127.0.0.1:6379> lset students2 2 zhangsan				#在链表students2左边开始数下标为2的元素替换为zhangsan(demo)
set集合
# 将一个或多个元素添加到set集合中
127.0.0.1:6379> sadd key member [member ...]			#format
127.0.0.1:6379> sadd k1 v1 v2 v3						#在set集合k1中添加值v1、v2、v3(demo)

#取出set集合中所有的值
127.0.0.1:6379> smembers key							#format
127.0.0.1:6379> smembers k1								#取出set集合k1中所有的值(demo)

#判断集合key中是否含有元素member,有返回1,没有返回0
127.0.0.1:6379> sismember key member					#format
127.0.0.1:6379> sismember k1 v3							#判断set集合k1中是否含有v3(demo)

#返回set集合中元素的个数
127.0.0.1:6379> scard key								#format
127.0.0.1:6379> scard k1								#查询集合k1中元素的个数(demo)

#删除集合中的某个元素或多个元素
127.0.0.1:6379> srem key member [member ...]			#format
127.0.0.1:6379> srem k1 v3								#删除集合k1中的元素v3(demo)

#随机从集合中移除一个值或count个值并返回
127.0.0.1:6379> spop key [count]						#format
127.0.0.1:6379> spop k1 2								#随机从集合k1中移除2个值并返回(demo)

#随机从集合中取出一个值或count个值
127.0.0.1:6379> srandmember key [count]					#format
127.0.0.1:6379> srandmember k1 2						#随机从集合k1中取出2个值(demo)

#从集合source中移除v8,并添加到集合destination 
127.0.0.1:6379> smove source destination member			#format
127.0.0.1:6379> smove k1 k2 v8							#从集合k1中移除v8,并添加v8到集合k2中(demo)

#获取多个集合之间的交集
127.0.0.1:6379> sinter key [key ...]					#format
127.0.0.1:6379> sinter k1 k2							#获取k1和k2之间的交集(demo)

#获取多个集合之间的并集
127.0.0.1:6379> sunion key [key ...]					#format
127.0.0.1:6379> sunion k1 k2							#获取k1和k2之间的并集(demo)

#获取集合key与其他集合之间的差集
127.0.0.1:6379> sdiff key [key ...]						#format
127.0.0.1:6379> sdiff k1 k2								#获取k1和k2之间的差集(demo)
Hash
#设置hash值
127.0.0.1:6379> hset key field value						#format
127.0.0.1:6379> hset student name zhangsan					#设置student的name=zhangsan(demo)

#获取hash里的field的值
127.0.0.1:6379> hget key field								#format
127.0.0.1:6379> hget student name							#获取student的field为name的值(demo)

#批量设置Hash值
127.0.0.1:6379> hmset key field value [field value ...]		#format
127.0.0.1:6379> hmset student age 23 hobby lanqiu			#批量设置student的(demo)age=23,hobby=lanqiu

#判断该hash里是否有这个field,有返回1,没有返回0
127.0.0.1:6379> hexists key field							#format
127.0.0.1:6379> hexists student name						#判断student里面是否有field为name(demo)

#获取hash里所有field
127.0.0.1:6379> hkeys key									#format
127.0.0.1:6379> hkeys student								#获取student的所有field(demo)

#获取hash里所有的value
127.0.0.1:6379> hvals key									#format							
127.0.0.1:6379> hvals student								#获取student里所有的value(demo)

#当hash里某个field的值数字时,使用此命令增加increment步长
127.0.0.1:6379> hincrby key field increment					#format
127.0.0.1:6379> hincrby student age 1						#设置student的field是age的加1(demo)

#当hash里不存在这个field时,设置这个field=value
127.0.0.1:6379> hsetnx key field value						#format
127.0.0.1:6379> hsetnx student bj 3							#当student里没有bj这个field时,设置bj=3(demo)
有序集合ZSet
# 批量添加有序集合,并设置每个元素的分值
127.0.0.1:6379> zadd key [NX|XX] [CH] [INCR] score member [score member ...]	#format
127.0.0.1:6379> zadd lauage 200 java 300 c# 400 php								#批量添加集合lauage元素:java:200分;c#:300分;php:400分

# 获取对应下标范围之间的元素
127.0.0.1:6379> zrange key start stop [WITHSCORES]	#format
127.0.0.1:6379> zrange lauage 0 1									#获取lauage集合下标[0,1]之间的元素

# 获取对应分数范围从小到大排序的元素
127.0.0.1:6379> zrangebyscore key min max [WITHSCORES] [LIMIT offset count]	#format
127.0.0.1:6379> zrangebyscore lauage 300 400	#获取lauage里300-400分之间的元素,按分数从小到大排序

# 获取对应分数范围从大到小排序的元素
127.0.0.1:6379> zrevrangebyscore key max min [WITHSCORES] [LIMIT offset count]	#format
127.0.0.1:6379> zrevrangebyscore lauage 400 300	#获取lauage里300-400分之间的元素,按分数从大到小排序

# 给指定元素的分数加上increment分
127.0.0.1:6379> zincrby key increment member		#format
127.0.0.1:6379> zincrby lauage 50 java				#给元素java加上50分

#删除指定的元素
127.0.0.1:6379> zrem key member [member ...]		#format
127.0.0.1:6379> zrem lauage php						#删除元素php	

#统计分数范围内的元素个数
127.0.0.1:6379> zcount key min max					#format
127.0.0.1:6379> zcount lauage 300 400				#统计300-400分之间元素的个数

#获取指定元素的排名,按照分数从小到大排序,默认从0开始
127.0.0.1:6379> zrank key member					#format
127.0.0.1:6379> zrank lauage java					#获取java元素的排名
Redis配置文件
#注掉下面的配置,让redis可以远程访问
bind 127.0.0.1

#把protected-mode改成no,让redis支持远程访问
protected-mode no
发布订阅
# 监听一个或多个通道
127.0.0.1:6379> subscribe channel [channel ...]			#format
127.0.0.1:6379> subscribe c1							#监听通道c1			

#向通道发送消息
127.0.0.1:6379> publish channel message					#format
127.0.0.1:6379> publish c1 hello						#发布消息hello到c1
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>
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;
	}
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; 
	}
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;
	}
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;
	}
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>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值