Redis简介
Redis是一种非关系型数据库,是一个用C语言编写的、开源的、基于内存运行并支持持久化的、高性能的NoSQL数据库。
Redis的特点
- Redis支持数据的持久化
Redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件。 - 支持多种数据结构
Redis不仅仅支持简单的key-value类型的数据,同时还提供string(字符串)、list(链表),set(集合),zset(有序集合),hash(哈希类型)等数据结构的存储。 - 支持数据备份
Redis支持数据的备份,即master-slave模式的数据备份。
Redis的应用场景
- 配合关系型数据库做高速缓存
对于高频词、热门访问、且数据量不大的数据,可以存储在Redis中,降低数据库IO - 分布式架构,做session共享
- 多样的数据结构存储持久化数据
Redis常用命令
1、启动redis服务
前台启动:在任何目录下redis-server
后台启动:在任何目录下redis-server &
启动redis服务时,指定配置文件:redis-server redis.conf &
2、关闭redis服务
1、通过kill命令直接杀死redis服务(不推荐)
2、通过redis命令关闭:redis-cli shutdown
3、redis的客户端
用来连接redis服务的,向redis服务端发送命令,并且显示redis服务处理结果
redis-cli:
是redis自带的客户端,使用命令redis-cli就可以启动redis客户端
redis-cli:默认连接本机上的6379端口上的redis服务
redis-cli -p 端口号:连接本机指定端口号上的redis服务
例:redis-cli -p 6380
redis-cli -h ip地址 -p 端口号 :连接指定ip主机上的指定端口的redis服务
例:redis-cli -h 192.168.112.132 -p 6380
4、退出客户端
exit
Redis基本知识
1、测试redis服务的性能:redis-benchmark
2、查看redis服务是否正常运行:ping 如果正常返回pong
3、查看redis服务器的所有统计信息:info
4、redis的数据库实例:作用类似于mysql的数据库实例,redis中的数据库实例只能由redis服务来创建和维护
开发人员不能修改和自行创建数据库实例,默认情况下,redis会自动创建16个数据库实例,并且给这些
数据库实例进行编号,从0开始,一直到15,开发人员使用时,通过编号来使用数据库,可以通过配置文件
指定redis自动创建的数据库个数,redis的每一个数据库实例本身占用的存储空间是很少的,即使默认有
16个实例,也不会造成存储空间的浪费,默认情况下,redis客户端连接的是0号数据库实例:可以使用
select index(数据库编号) 来进行数据库切换
5、查看当前数据库中所有key的数量:dbsize
6、查看当前数据库实例中所有的key:keys *
7、清空当前数据库实例:flushdb
8、清空所有数据库实例:flushall
9、查看redis中的所有的配置信息:config get *
10、查看redis中的指定的配置信息:config get parameter
Redis的数据类型
## 字符串型(string)
String是Redis中最基本的数据类型,表现为k-v键值对的形式,一个key对应一个value。一个Redis中字符串
value最多可以存储512M数据
## redis中有关string类型数据的操作命令
1、将String类型的数据保存到redis中
set key value
例:set k1 v1
如果存储的时候 key已经存在了,则后来的value会把以前的value覆盖掉
2、从redis中获取string类型的数据
get key
例:get k1
3、从redis中删除String类型的数据
del key
例:del k1
4、追加字符串
append key value
例:append k1 v2
如果key不存在,则会新建key并且把value值设置为对应的value
5、获取字符串数据的长度
strlen key
例:strlen k1
6、将字符串数值进行加1运算
incr key
例:incr age
注:要求key所表示的value必须是数值,否则就报错
7、对字符串数字进行减1运算
decr key
例:decr age
8、将字符串数值进行加N运算
incrby key offset
例:incrby age 10
9、将字符串数值进行减N运算
decrby key offset
例:decrby age 10
10、批量新增String类型的数据到redis中
mset 键1 值1 键2 值2 ...
例:mset k1 v1 k2 v2 ...
11、批量获取String类型的数据
mget 键1 键2 键3 ...
例:mget k1 k2 k3 ...
## List类型
List类型的数据结构为快速链表quickList
首先在列表元素较少的情况下会使用一块连续的内存存储,这个结构是 ziplist,也即是压缩列表。它将所有的
元素紧挨着一起存储,分配的是一块连续的内存。
当数据量比较多的时候才会改成 quicklist。因为普通的链表需要的附加指针空间太大,会比较浪费空间。比如
这个列表里存的只是 int 类型的数据,结构上还需要两个额外的指针 prev 和 next。
## 有关list类型数据的操作命令
每一个元素都有下标,表头元素的下标是0,依次往后排序,最后一个元素下标是列表长度-1
每一个元素的下标还可以用负数表示,负下标表示从表尾开始计算,最右侧的元素下标为-1,依次往左
元素在列表中的顺序或者下标由放入的顺序来决定
1、将一个或者多个值依次插入到列表的表头(左侧)
lpush(leftpush) key value value ....
例:lpush num 1 2 3
2、将一个或多个值依次插入到列表的表尾(右侧)
rpush key value value ....
例:rpush num2 4 5 6
3、从指定列表中移除并且返回表头元素
lpop key
例:lpop num
4、从指定列表移除并返回表尾元素
rpop key
例:rpop num
5、获取指定列表中的元素
lrange key startIndex endIndex
例:lrange num 0 1 [下标是闭区间]
例:lrange num 0 -1 获取列表中全部元素
6、获取列表中指定下标的元素
lindex key index
例:lindex num 1
7、获取指定列表的长度
llen key
例:llen num
8、根据count的值移除列表中某一些数据
lrem key count value
注:
count > 0:表示从列表的左侧移除count个跟value相等的数据
count < 0:表示从列表的右侧移除count个跟value相等的数据
count = 0:表示从列表中移除所有跟value相等的数据
## set类型
单key-多value value无序不可重复
## set类型数据的操作指令
1、将一个或者多个元素添加到指定的集合中
sadd key value value....
例:sadd set01 a b c d e f
注:当有重复的value时,后一个数据会加入失败
2、获取指定集合中所有的元素
smembers key
例:smembers set01
注:取出的顺序与放入的顺序不一致,因为set集合是无序的
3、判断指定元素在集合中是否存在
sismember key member
例:sismember set01 hahah
注:存在返回1,不存在返回0
4、获取指定集合的长度
scard key
例:scard set01
5、移除集合中一个或者多个元素
srem key value value....
例:srem set01 hahah
注:返回值为成功移除的元素个数
6、随机获取指定集合中的一个或多个元素
srandmember key [count]
count > 0:随机获取的多个元素之间不能重复
count < 0:随机获取的多个元素之间可能重复
例:srandmember set02 2
7、从指定集合中随机移除一个或者多个元素
spop key [count]
例:spop set02 2
8、将指定集合中的指定元素移动到另一个集合
smove source dest member
例:smove set2 set3 a
注:把set2中的a移动到set3中
9、获取一个集合中有,其他集合中都没有的元素组成的新集合(差集)
sdiff key otherkey otherkey otherkey....
例:sdiff set3 set2
10、获取所有指定集合中都有的元素组成的新集合(交集)
sinter key key....
例:sinter set3 set2
11、获取所有指定集合中所有元素组成的大集合(并集)
sunion key key ...
例:sunion set1 set2 set3
## hash类型
单key:field-value field-value field-value ...
## 关于hash类型数据的操作指令
1、将一个或者多个field-value键值对设置到哈希表中
hset key field1-value1 ....
例:hset stu1001 id 1001 sname zhangsan
注:如果key和field已经存在,则以前的value会被覆盖
2、获取指定hash表中指定field的值
hget key field
例:hget stu001 id
hget stu1001 sname
3、将多个field-value对设置到哈希表中
hmset key field1 value1 ....
例:hmset stu1002 id 1002 sname lisi
4、批量获取指定哈希表中的field的值
hmget key field1 field2....
例:hmget stu1001 id sname
5、获取指定hash表中所有的field和value
hgetall key
例:hgetall stu1001
6、从指定hash表中,删除1个或者多个field
hdel key field1 field1 ....
例:hdel stu1002 id
7、获取指定hash表中所有的field个数
hlen key
例:hlen stu1002
8、判断指定hash表中是否存在某一个field
hexists key field
例:hexists stu1002 sname
9、获取指定hash表中所有的field列表
hkeys key
例:hkeys stu1002
10、获取指定hash表中所有的value列表
hvals key
例:hvals stu1002
11、对指定hash表中指定的field值进行加法运算
hincrby key field int
例:hincrby stu1002 age 10
注:只能增加整数
12、对指定hash表中指定的field值进行浮点数加法运算
hincrbyfloat key field float
例:hincrbyfloat stu1002 score 5.5
13、将一个field-value对设置到hash表中,当key-field已经存在时,则放弃设置
hsetnx key field value
例:hsetnx stu1002 address beijing
## zset类型
有序集合,本质上是集合,所有元素不能重复每一个元素都关联一个分数,redis会根据分数对元素进行排序,
分数可以重复,既然有序,那么也都有下标,有序集合中元素的排序规则和列表中的排序规则不一样
## 有关zset类型数据的操作命令
1、将一个或者多个元素及其分数值加入有序集合
zadd key score member score member ...
例:zadd zset01 20 z1 20 z2 30 z3 50 z4 33 z5
注:如果元素已存在,则把分数覆盖
2、获取指定有序集合中指定下标区间的元素
zrange key startIndex endIndex [withscores]
例:zrange zset01 0 -1
zrange zset01 0 -1 withscores
3、获取指定有序集合中指定分数区间的元素
zrangebyscore key min max [withscores]
例:zrangebyscore zset01 30 50 withscores [闭区间]
4、删除指定有序集合中一个或者多个元素
zrem key member ...
例:zrem zset01 z2 z3
5、获取指定集合中所有元素的个数
zcard key
例:zcard zset01
6、获取指定有序集合中指定元素的排名
zrank key member
例:zrank zset01 z3 (排名从0开始)
7、获取指定有序集合中分数在指定区间内的元素的个数
zcount key min max
例:zcount zset01 20 50
8、获取指定有序集合中指定元素的分数
zscore key member
例:zscore zset01 z5
9、获取指定有序集合中指定元素的排名(按分数从大到小排名)
zrevrank key member
例:zrevrank zset01 z5
Redis的持久化
redis提供持久化策略,在适当的时机,采用适当手段把内存中的数据持久化到磁盘中,每日redis服务启动时
都可以把磁盘上的数据再次加载到内存中使用。
## RDB策略(保存数据)
在指定时间间隔内,redis服务执行指定次数的写操作,会自动触发一次持久化操作
RDB策略是redis默认的持久化策略,redis服务开启时这种持久化策略就开启了
dump.rdb文件:RDB持久化数据存储的文件
dir:RDB持久化文件所在的目录
默认的策略:
save 900 1 #一条数据时,每900秒触发一次持久化
save 300 10 #十条数据时,每300秒触发一次持久化
save 60 10000 #10000条数据时,每60秒触发一次持久化
优点:不影响性能
缺点:不能保证数据的完整性
主动触发RDB策略:./redis-cli shutdown
## AOF策略(保存操作的命令)
采用操作日志来记录每一次写操作(读操作不记录),每次redis服务启动时都会重新执行一遍操作日志中的指令
效率低下,默认不开启
默认支持的策略:
# appendfsync always
appendfsync everysec
# appendfsync no
缺点:影响性能
优点:保证数据的完整性
Redis集群(cluster模式)
Redis集群采用去中心化的思想,没有中心节点的说法,对于客户端来说,整个集群可以看成一个整体,可以
连接任意一个节点进行操作,就像操作单一Redis实例一样,不需要任何代理中间件,当客户端操作的key没有
分配到该node上时,Redis会返回转向指令,指向正确的node。
Redis也内置了高可用机制,支持N个master节点,每个master节点都可以挂载多个slave节点,当master节点
挂掉时,集群会提升它的某个slave节点作为新的master节点。
Redis集群采用的算法是哈希槽分区算法。Redis集群中有16384个哈希槽(槽的范围是 0 -16383,哈希槽),
将不同的哈希槽分布在不同的Redis节点上面进行管理,也就是说每个Redis节点只负责一部分的哈希槽。在对
数据进行操作的时候,集群会对使用CRC16算法对key进行计算并对16384取模(slot = CRC16(key)%16383),
得到的结果就是 Key-Value 所放入的槽,通过这个值,去找到对应的槽所对应的Redis节点,然后直接到这个
对应的节点上进行存取操作
默认情况下,redis集群的读和写都是到master上去执行的,不支持slave节点读和写,跟Redis主从复制下读写
分离不一样,因为redis集群的核心的理念,主要是使用slave做数据的热备,以及master故障时的主备切换,
实现高可用的。Redis的读写分离,是为了横向任意扩展slave节点去支撑更大的读吞吐量。而redis集群架构下,
本身master就是可以任意扩展的,如果想要支撑更大的读或写的吞吐量,都可以直接对master进行横向扩展。
Redis集群搭建步骤(docker)
一、搭建三主三从redis集群环境
1、启动docker容器
systemctl start docker
2、新建6个docker容器redis实例
docker run -d --name redis-node-1 --net host --privileged=true -v /data/redis/share/redis-node-1:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6381
docker run -d --name redis-node-2 --net host --privileged=true -v /data/redis/share/redis-node-2:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6382
docker run -d --name redis-node-3 --net host --privileged=true -v /data/redis/share/redis-node-3:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6383
docker run -d --name redis-node-4 --net host --privileged=true -v /data/redis/share/redis-node-4:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6384
docker run -d --name redis-node-5 --net host --privileged=true -v /data/redis/share/redis-node-5:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6385
docker run -d --name redis-node-6 --net host --privileged=true -v /data/redis/share/redis-node-6:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6386
命令解释:
docker run:创建并运行docker容器实例
--name redis-node-6:容器名字
--net host:使用宿主机的IP和端口,默认
--privileged=true:获取宿主机的root用户权限
-v /data/redis/share/redis-node-6:/data:容器卷,宿主机地址:docker内部地址
redis:6.0.8:redis镜像和版本号
--cluster-enabled yes:开启redis集群
--appendonly yes:开启持久化
--port 6386:redis端口号
3、进入容器redis-node-1并为6台机器构件集群关系(也可以进入其他容器,这里只是选取一个落脚点)
1、进入容器:docker exec -it redis-node-1 /bin/bash
2、构件主从关系:
redis-cli --cluster create 192.168.112.128:6381 192.168.112.128:6382 192.168.112.128:6383 192.168.112.128:6384 192.168.112.128:6385 192.168.112.128:6386 --cluster-replicas 1
这里的ip地址写自己宿主机的ip
--cluster-replicas 1 表示为每个master创建一个slave节点
三主三从集群搞定!
4、进入redis6381作为切入点,查看集群状态
1、进入6381:redis-cli -p 6381
2、cluster info
3、cluster nodes
可以看到节点对应关系
6381--6385
6382--6386
6383--6384
随机对应,多次尝试可能会不一样
二、主从容错切换迁移案例
1、数据读写存储
1、启动6机构成的集群并通过exec进入 docker exec -it redis-nodes-1 /bin/bash
2、进入6381(这里要注意,进入6381的时候,命令要加-c,防止路由失效)
redis-cli -p 6381 -c
3、对6381新增两个key:
set k1 v1 set k2 v2
4、查看集群信息:
redis-cli --cluster check 192.168.112.128:6381
2、容错切换实例
1、先停止主机6381,这时对应的6384从机会自动上位
2、再次查看集群状态:redis-cli --cluster check 192.168.112.128:6382
可以看到这时的6384的状态从slave变成了master
3、还原之前的三主三从
1、先启动6381,这时的6381会作为从机进入集群
2、停止6384,让6381上位主机
因为一般选择主机的时候都会选择性能比较好的机器,所以6381恢复服务后要恢复它的主机地位
3、再次启动6384
4、查看集群状态
redis-cli --cluster check 192.168.112.128:6381
三、主从扩容案例(重点!!!)
1、新建6387、6388两个新节点,并启动,然后查看docker中是否有8个节点
docker run -d --name redis-node-7 --net host --privileged=true -v /data/redis/share/redis-node-7:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6387
docker run -d --name redis-node-8 --net host --privileged=true -v /data/redis/share/redis-node-8:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6388
docker ps
2、进入6387节点(空槽号)内部
docker exec -it redis-node-7 /bin/bash
3、将新增的6387节点作为master节点加入原集群
redis-cli --cluster add-node 192.168.112.128:6387 192.168.112.128:6381
这里就是将要作为master新增节点 6381 就是原来集群节点里面的领路人,相当于6387拜拜6381的码头从而找到组织加入集群
4、第一次检查集群情况
redis-cli --cluster check 192.168.112.128:6381
5、重新分配槽号
redis-cli --cluster reshard 192.168.112.128:6381
这里选择将16384个槽号平均分配给4个master节点 16384/4=4096
6、第二次检查集群情况
redis-cli --cluster check 192.168.112.128:6381
这里会发现新加入的6387节点的槽号是由3个区间组成的
这是因为重新分配成本太高,所以前3家各自匀出来一部分,从6381/6382/6383三个旧节点分别匀出1364个坑位给新节点6387
7、为主节点6387分配从节点6388
redis-cli --cluster add-node 192.168.112.128:6388 192.168.112.128:6387 --cluster-slave --cluster-master-id 135fe8e173ad35fa45b7281cc4a055e2b45dca83
-------这个是6387的编号,按照自己实际情况
8、第三次检查集群情况
redis-cli --cluster check 192.168.112.128:6381
会发现两个节点都已经搭建好了
四、主从缩容案例
目的:6387和6388下线
1、检查集群情况,获取到6388的节点id
redis-cli --cluster check 192.168.111.147:6382
因为6388是从节点,下线要从从节点开始
2、将6388从集群中删除
redis-cli --cluster del-node 192.168.112.128:6388 715165e8ce439f9612b8e86c14f0794663de82fc
3、将6387的槽号清空,重新分配,本例将清出来的槽号都给6381
redis-cli --cluster reshard 192.168.111.147:6381
4、检查集群情况
redis-cli --cluster check 192.168.111.147:6382
5、将6387节点删除
redis-cli --cluster del-node 192.168.112.128:6387 e4781f644d4a4e4d4b4d107157b9ba8144631451
6、查看集群情况
redis-cli --cluster check 192.168.111.147:6382
这时集群从四主四从恢复到了三主三从
RedisTemplate的配置与使用
1、添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2、配置类
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisTemplateConfig {
@Bean
public RedisTemplate<String,Object> getRedisTemplate(RedisConnectionFactory factory){
RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
//设置通用的序列化器(解决序列化乱码问题)
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
return redisTemplate;
}
}
3、RedisClient
因为RedisTemplate自带的对redis数据库的操作指令有区别,不方便我们记忆,所以我们可以将其进行封装,将对数据的操作指令变为我们熟悉的redis风格。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@Component
public class RedisClient {
@Autowired
private RedisTemplate<String,Object> redisTemplate;
/**
* 指定缓存失效时间
* @param key 键
* @param time 时间(秒)
* @return
*/
public boolean expire(String key,long time){
try {
if(time>0){
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据key 获取过期时间
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
public long ttl(String key){
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 判断key是否存在
* @param key 键
* @return true 存在 false不存在
*/
public Boolean exists(String key){
return redisTemplate.hasKey(key);
}
//============================String=============================
/**
* 普通缓存获取
* @param key 键
* @return 值
*/
public Object get(String key){
return key==null?null:redisTemplate.opsForValue().get(key);
}
/**
* 普通缓存放入
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public boolean set(String key,Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除缓存
* @param key 可以传一个值 或多个
*/
public Boolean del(String key){
return redisTemplate.delete(key);
}
/**
* 递增
* @param key 键
* @param delta 要增加几(大于0)
* @return
*/
public long incr(String key, long delta){
if(delta<0){
throw new RuntimeException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 递减
* @param key 键
* @param delta 要减少几(小于0)
* @return
*/
public long decr(String key, long delta){
if(delta<0){
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().decrement(key, -delta);
}
//================================hash=================================
/**
* HashGet
* @param key 键 不能为null
* @param item 项 不能为null
* @return 值
*/
public Object hget(String key,String item){
return redisTemplate.opsForHash().get(key, item);
}
/**
* 向一张hash表中放入数据,如果不存在将创建
* @param key 键
* @param item 项
* @param value 值
* @return true 成功 false失败
*/
public boolean hset(String key,String item,Object value) {
try {
redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除hash表中的值
* @param key 键 不能为null
* @param item 项 可以使多个 不能为null
*/
public void hdel(String key, Object... item){
redisTemplate.opsForHash().delete(key,item);
}
//============================set=============================
/**
* 根据key获取Set中的所有值
* @param key 键
* @return
*/
public Set<Object> smembers(String key){
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 将数据放入set缓存
* @param key 键
* @param values 值 可以是多个
* @return 成功个数
*/
public long sadd(String key, Object...values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 移除值为value的
* @param key 键
* @param values 值 可以是多个
* @return 移除的个数
*/
public long srem(String key, Object ...values) {
try {
Long count = redisTemplate.opsForSet().remove(key, values);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
//===============================list=================================
/**
* 获取list缓存的内容
* @param key 键
* @param start 开始
* @param end 结束 0 到 -1代表所有值
* @return
*/
public List<Object> lrange(String key, long start, long end){
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 将list放入缓存
* @param key 键
* @param value 值
* @return
*/
public boolean rpush(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
* @param key 键
* @param value 值
* @return
*/
public boolean lpush(String key, List<Object> value) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 移除N个值为value
* @param key 键
* @param count 移除多少个
* @param value 值
* @return 移除的个数
*/
public long lrem(String key,long count,Object value) {
try {
Long remove = redisTemplate.opsForList().remove(key, count, value);
return remove;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
}