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的应用场景
- 经常需要使用的数据,写操作不是很频繁的,可以存入
Redis
session
共享- 高速读、写场合,比如一瞬间需要向数据库插入千万级的数据,很容易造成数据库瘫痪,并且速度很慢,很难使系统达到高速响应的性能。这个时候我们可以使用异步写入数据库的方式,先将数据缓存到
Redis
中,当达到一定的条件,再批量将数据写入到数据库中,完成数据持久化的过程。
Redis底层原理
Redis
底层采用的是单线程 +
多路IO
复用技术,而memcached
底层使用的是串行 vs
多线程 +
锁。Redis
默认16
个数据库,类似数组下标从0
开始,初始默认使用0
号库。使用命令 select <dbid>
来切换数据库。例如:select 1
,切换到1
号库。
Redis支持的数据类型
- 字符串
- 哈希结构
- 列表
- 集合
- 可排序集合
- 基数
Redis的安装
windos安装
- 下载
Redis下载地址
- 解压
- 在当前目录下创建一个
startup.bat
文件输入以下命令,方便开启redis
服务redis-server redis.windows.conf
- 双击
startup.bat
,启动redis
- 双击
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:
- 在执行事务之前修改了money的值,导致触发乐观锁,事务回滚
- 事务提交之前,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>