Redis
1. 简介
1.1 NoSQL简介
-
目前市场主流数据存储都是使用关系型数据库。每次操作关系型数据库时都是I/O操作,I/O操作是主要影响程序执行性能原因之一,连接数据库关闭数据库都是消耗性能的过程。尽量减少对数据库的操作,能够明显的提升程序运行效率。
-
针对上面的问题,市场上就出现了各种NoSQL(Not Only SQL,不仅仅可以使用关系型数据库)数据库,它们的宣传口号:不是什么样的场景都必须使用关系型数据库,一些特定的场景使用NoSQL数据库更好。
-
常见NoSQL数据库:
-
memcached :键值对,内存型数据库,所有数据都在内存中。
-
Redis:和Memcached类似,还具备持久化能力。
-
HBase:以列作为存储。
-
MongoDB:以Document做存储。
-
1.2 Redis简介
- Redis(Remote Dictionary Server) 是一个使用 C 语言编写的,开源的(BSD许可)高性能非关系型(NoSQL)的键值对数据库。平时操作的数据都在内存中,效率高,所以多把Redis当做缓存工具使用。另外,Redis 也经常用来做分布式锁。
- Redis以solt(槽)作为数据存储单元,每个槽中可以存储N多个键值对。Redis中固定具有16384。理论上可以实现一个槽是一个Redis。每个向Redis存储数据的key都会进行crc16算法得出一个值后对16384取余就是这个key存放的solt位置。
1.2.1 redis优点
- 读写性能优异, Redis能读的速度是110000次/s,写的速度是81000次/s。
- 支持数据持久化,支持AOF和RDB两种持久化方式。
- 支持事务,Redis的所有操作都是原子性的,同时Redis还支持对几个操作合并后的原子性执行。
- 数据结构丰富,除了支持string类型的value外还支持hash、set、zset、list等数据结构。
- 支持主从复制,主机会自动将数据同步到从机,可以进行读写分离。
1.2.2 redis缺点
- Redis受到物理内存的限制
- Redis 不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复。主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性。
- Redis 较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。为避免这一问题,运维人员在系统上线时必须确保有足够的空间,这对资源造成了很大的浪费。
2. Redis安装
2.1 Redis单机版安装
- redis使用C语言编写,所以需要安装C语言库
yum install -y gcc-c++ automake autoconf libtool make tcl
- 把redis-5.0.5.tar.gz上传到/usr/local/tmp中
cd /usr/local/tmp
tar zxf redis-5.0.5.tar.gz
- 进入解压文件夹,编译并安装
cd /usr/local/tmp/redis-5.0.5/
# 编译
make
# 安装
make install PREFIX=/usr/local/redis
- 开启守护进程
# 复制cd /usr/local/tmp/redis-5.0.5/中redis.conf配置文件
cp redis.conf /usr/local/redis/bin/
# 进入redis bin目录
cd /usr/local/redis/bin/
# 修改配置文件,把daemonize的值由no修改为yes
vim redis.conf
- 开启外部访问
# 在redis5中需要修改配置文件redis.conf允许外部访问
# 1.注释掉下面
# bind 127.0.0.1
# protected-mode 由yes改为no
protected-mode no
- 启动并测试
# 启动redis
./redis-server redis.conf
# 关闭redis-cli
./redis-cli shutdown
# 启动客户端工具(使用exit退出)
./redis-cli
3. Redis数据类型及常用命令
-
Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,zset,hash等数据结构的存储,它还支持数据的备份,即master-slave模式的数据备份,同样Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。
-
Redis支持的五大数据类型包括String(字符串 用法: 键 值),Hash(哈希 类似Java中的 map 用法: 键 键值对),List(列表 用法:键 集合 不可以重复),Set(集合 用法:键 集合 可以重复),Zset(sorted set 有序集合 用法: 键 值 值)
3.1 String(字符串)
-
string 是 redis 最基本的类型,你可以理解成与 Memcached 一模一样的类型,一个 key 对应一个 value。string 类型是二进制安全的。意思是 redis 的 string 可以包含任何数据。比如jpg图片或者序列化的对象。string 类型是 Redis 最基本的数据类型,string 类型的值最大能存储 512MB
-
应用场景:String是最常用的一种数据类型,普通的key/value存储都可以归为此类,value其实不仅是String,也可以是数字:比如想知道什么时候封锁一个IP地址(访问超过几次)。
3.1.1 set
设置指定key的值
语法:set key value
返回值:成功OK
3.1.2 get
获取指定key的值
语法:get key
返回值:key的值。不存在返回nil
3.1.3 exists
判断key是否存在。
语法:exists key名称
返回值:存在返回数字,不存在返回0
3.1.4 expire
设置key的过期时间,单位秒
语法:expire key 秒数
返回值:成功返回1,失败返回0
3.1.5 ttl
查看key的剩余过期时间
语法:ttl key
返回值:返回剩余时间,如果不过期返回-1
3.1.6 del
根据key删除键值对。
语法:del key
返回值:被删除key的数量
3.1.7 setnx
当且仅当key不存在时才新增。
语法:setnx key value
返回值:不存在时返回1,存在返回0
3.1.8 setex
设置key的存活时间,无论是否存在指定key都能新增,如果存在key覆盖旧值。同时必须指定过期时间。
语法:setex key seconds value
返回值:OK
# 设置name为“xiaoming”,可以不加双引号
127.0.0.1:6379> set name "xiaoming"
OK
127.0.0.1:6379> get name
"xiaoming"
127.0.0.1:6379> set name xiaoming
OK
127.0.0.1:6379> get name
"xiaoming"
# 判断key是否存在
127.0.0.1:6379> exists name
(integer) 1
127.0.0.1:6379> exists age
(integer) 0
# 设置key存在时间
127.0.0.1:6379> expire name 100
(integer) 1
# 查看key存活时间,-2表示过期
127.0.0.1:6379> ttl name
(integer) 95
127.0.0.1:6379> ttl name
(integer) 46
127.0.0.1:6379> ttl name
(integer) -2
# -1表示key没有过期时间
127.0.0.1:6379> set age 18
OK
127.0.0.1:6379> ttl age
(integer) -1
# 删除key
127.0.0.1:6379> get age
"18"
127.0.0.1:6379> del age
(integer) 1
127.0.0.1:6379> get age
(nil)
127.0.0.1:6379>
# setnx 存在返回0,不存在返回1并设置该值,这个命令及其特性可以作为分布式锁
127.0.0.1:6379> setnx age 20
(integer) 0
127.0.0.1:6379> get age
"18"
127.0.0.1:6379> setnx age2 20
(integer) 1
127.0.0.1:6379> get age2
"20"
# 设置key的存活时间,无论是否存在指定key都能新增,如果存在key覆盖旧值。同时必须指定过期时间。
127.0.0.1:6379> setex age 50 10
OK
127.0.0.1:6379> ttl age
(integer) 41
3.2 Hash(哈希)
- Redis hash 是一个键值(key=>value)对集合,Redis hash 是一个 string 类型的 field 和 value 的映射表,hash 特别适合用于存储对象。
- 使用场景:存储、读取、修改用户属性
3.2.1 hset
给key中field设置值。
语法:hset key field value
返回值:成功1,失败0
3.2.2 hget
获取key中某个field的值
语法:hget key field
返回值:返回field的内容
3.2.3 hmset
给key中多个filed设置值
语法:hmset key field value field value
返回值:成功OK
3.2.4 hmget
一次获取key中多个field的值
语法:hmget key field field
返回值:value列表
3.2.5 hvals
获取key中所有field的值
语法:hvals key
返回值:value列表
3.2…6 hgetall
获取所有field和value
语法:hgetall key
返回值:field和value交替显示列表
3.2.7 hdel
删除key中任意个field
语法:hdel key field field
返回值:成功删除field的数量
127.0.0.1:6379> hset name id 1
(integer) 1
127.0.0.1:6379> hget name id
"1"
127.0.0.1:6379> hmset person name xiaoming age 18 addr bj
OK
127.0.0.1:6379> hmget person name age addr
1) "xiaoming"
2) "18"
3) "bj"
127.0.0.1:6379> hvals person
1) "xiaoming"
2) "18"
3) "bj"
127.0.0.1:6379> hgetall person
1) "name"
2) "xiaoming"
3) "age"
4) "18"
5) "addr"
6) "bj"
127.0.0.1:6379> hdel person addr
(integer) 1
127.0.0.1:6379> hvals person
1) "xiaoming"
2) "18"
127.0.0.1:6379>
3.3 List(列表)
-
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。
-
应用场景:如实现最新消息排行等功能。Lists的另一个应用就是消息队列,可以利用Lists的PUSH操作,将任务存在Lists中,然后工作线程再用POP操作将任务取出进行执行。
3.1.1 Rpush
向列表末尾中插入一个或多个值
语法;rpush key value value
返回值:列表长度
3.1.2 lrange
返回列表中指定区间内的值。可以使用-1代表列表末尾
语法:lrange list 0 -1
返回值:查询到的值
3.1.3 lpush
将一个或多个值插入到列表前面
语法:lpush key value value
返回值:列表长度
3.1.4 llen
获取列表长度
语法:llen key
返回值:列表长度
3.1.5 lrem
删除列表中元素。count为正数表示从左往右删除的数量。负数从右往左删除的数量。
语法:lrem key count value
返回值:删除数量。
127.0.0.1:6379> rpush list1 aa bb cc
(integer) 3
127.0.0.1:6379> lrange list1 0 1
1) "aa"
2) "bb"
127.0.0.1:6379> lrange list1 0 -1
1) "aa"
2) "bb"
3) "cc"
127.0.0.1:6379>
127.0.0.1:6379> lpush list1 dd ee ff gg
(integer) 7
127.0.0.1:6379> lrange list1 0 -1
1) "gg"
2) "ff"
3) "ee"
4) "dd"
5) "aa"
6) "bb"
7) "cc"
127.0.0.1:6379> llen list1
(integer) 7
127.0.0.1:6379> lpush list2 aa aa bb bb cc cc aa
(integer) 7
127.0.0.1:6379> lrem list2 2 aa
(integer) 2
127.0.0.1:6379> lrange list2 0 -1
1) "cc"
2) "cc"
3) "bb"
4) "bb"
5) "aa"
3.4 Set(集合)
- Redis的Set是string类型的无序集合。
- 应用场景:Redis set对外提供的功能与list类似是一个列表的功能,特殊之处在于set是可以自动排重的,当你需要存储一个列表数据,又不希望出现重复数据时,set是一个很好的选择,并且set提供了判断某个成员是否在一个set集合内的重要接口,这个也是list所不能提供的。Redis还为集合提供了求交集、并集、差集等操作。
3.4.1 sadd
向集合中添加内容。不允许重复。
语法:sadd key value value value
返回值:集合长度
3.4.2 scard
返回集合元素数量
语法:scard key
返回值:集合长度
3.4.3 smembers
查看集合中元素内容
语法:smembers key
返回值:集合中元素
3.5 zset(sorted set:有序集合)
- Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。zset的成员是唯一的,但分数(score)却可以重复。
3.5.1 zadd
向有序集合中添加数据
语法:zadd key score value score value
返回值:长度
3.5.2 zrange
返回区间内容,withscores表示带有分数
语法:zrange key 区间 [withscores]
返回值:值列表
4. Redis持久化策略
4.1 什么是Redis持久化
- 持久化就是把内存的数据写到磁盘中去,防止服务宕机了内存数据丢失。
4.2 Redis持久化机制
- Redis 提供两种持久化机制 RDB(默认) 和 AOF 机制
4.2.1 RDB
-
rdb模式是默认模式,可以在指定的时间间隔内生成数据快照(snapshot),默认保存到dump.rdb文件中。当redis重启后会自动加载dump.rdb文件中内容到内存中。
-
用户也可以使用SAVE(同步)或BGSAVE(异步)手动保存数据。
可以设置服务器配置的save选项,让服务器每隔一段时间自动执行一次BGSAVE命令,可以通过save选项设置多个保存条件,但只要其中任意一个条件被满足,服务器就会执行BGSAVE命令。 例如: save 900 1 save 300 10 save 60 10000 那么只要满足以下三个条件中的任意一个,BGSAVE命令就会被执行 服务器在300秒之内,对数据库进行了至少10次修改 服务器在60秒之内,对数据库进行了至少10000次修改
-
优点:
- rdb文件是一个紧凑文件,直接使用rdb文件就可以还原数据。
- 性能最大化,fork 子进程来完成写操作,让主进程继续处理命令,所以是 IO 最大化。使用单独子进程来进行持久化,主进程不会进行任何 IO 操作,保证了 redis 的高性能
- 恢复数据的效率要高于aof
-
缺点:
- 每次保存点之间导致redis不可意料的关闭,可能会丢失数据。
- 由于每次保存数据都需要fork()子进程,在数据量比较大时可能会比较耗费性能。
4.2.2 AOF
- AOF持久化(即Append Only File持久化),则是将Redis执行的每次写命令记录到单独的日志文件中,当重启Redis会重新将持久化的日志中文件恢复数据。
- AOF默认是关闭的,需要在配置文件中开启AOF。Redis支持AOF和RDB同时生效,如果同时存在,AOF优先级高于RDB(Redis重新启动时会使用AOF进行数据恢复)
- 优点:
- 相对RDB数据更加安全。
- 缺点:
- 相同数据集AOF要大于RDB。
- 相对RDB可能会慢一些。
- 开启方法:
- 修改redis.conf
# 默认no
appendonly yes
# aof文件名
appendfilename "appendonly.aof"
5. 主从复制
- Redis支持集群功能。为了保证单一节点可用性,redis支持主从复制功能。每个节点有N个复制品(replica),其中一个复制品是主(master),另外N-1个复制品是从(Slave),也就是说Redis支持一主多从。一个主可有多个从,而一个从又可以看成主,它还可以有多个从。
- 增加单一节点的健壮性,从而提升整个集群的稳定性。(Redis中当超过1/2节点不可用时,整个集群不可用)
- 从节点可以对主节点数据备份,提升容灾能力。
- 读写分离。在redis主从中,主节点一般用作写(具备读的能力),从节点只能读,利用这个特性实现读写分离,写用主,读用从。
5.1 一主多从环境搭建
- 模拟一主两从,由于懒得再开虚拟机,直接在一台虚拟机模拟了
5.1.1 准备工作
- 在已经搭建的单机版redis基础上进行操作,并且关闭redis单机版
./redis-cli shutdown
5.1.2 新建目录
# 新建存放从节点的目录
mkdir /usr/local/replica
5.1.3 复制目录
- 把之前安装的redis单机版中bin目录复制三份,分别叫做:master、slave1、slave2
# cp -r /usr/local/redis/bin /usr/local/replica/master
# cp -r /usr/local/redis/bin /usr/local/replica/slave1
# cp -r /usr/local/redis/bin /usr/local/replica/slave2
5.1.4 修改从的配置文件
- 修改2个从的redis.conf,指定主节点ip和端口。并修改自身端口号防止和其他redis冲突。
# 修改slave1
vim /usr/local/replica/slave1/redis.conf
# replicaof 192.168.93.10 6379
# port 6380
vim /usr/local/replica/slave2/redis.conf
# replicaof 192.168.93.10 6379
# port 6381
5.1.5 启动三个redis实例
- 注意:如果有单机版的redis,一定要关闭,否则端口冲突
- 为了方便,在replica目录下新建一个启动文件
# 进入目录
cd /usr/local/replica
# 修改文件
vim startup.sh
- 在文件内添加以下内容
cd /usr/local/replica/master/
./redis-server redis.conf
cd /usr/local/replica/slave1
./redis-server redis.conf
cd /usr/local/replica/slave2
./redis-server redis.conf
- 赋予权限,然后启动
chmod a+x startup.sh
./startup.sh
5.1.6 查看启动状态
ps aux|grep redis
5.1.7 测试
- 进入主节点,设置key,从节点测试是否可以读取
# 6379设置key
[root@10 master]# ./redis-cli
127.0.0.1:6379> set name lalala
OK
127.0.0.1:6379> get name
"lalala"
# 查看6380
[root@10 slave1]# ./redis-cli -p 6380
127.0.0.1:6380> get name
"lalala"
# 查看6381
[root@10 slave2]# ./redis-cli -p 6381
127.0.0.1:6381> get name
"lalala"
6. 哨兵模式(Sentinel)
- 在redis主从默认是只有主具备写的能力,而从只能读。如果主宕机,整个节点不具备写能力。但是如果这是让一个从变成主,整个节点就可以继续工作。即使之前的主恢复过来也当做这个节点的从即可。
- Redis的哨兵就是帮助监控整个节点的,当节点主宕机等情况下,帮助重新选取主。
- Redis中哨兵支持单哨兵和多哨兵。单哨兵是只要这个哨兵发现master宕机了,就直接选取另一个master。而多哨兵是根据我们设定,达到一定数量哨兵认为master宕机后才会进行重新选取主。我们以多哨兵演示。
6.1 没有哨兵下主从效果示例
- 从节只具备读的能力,而主挂掉,从节点身份不会主动变化
127.0.0.1:6381> set name ll
(error) READONLY You can't write against a read only replica.
# 杀死master线程后
127.0.0.1:6381> info replication
# Replication
role:slave
master_host:192.168.56.30
master_port:6379
master_link_status:down
master_last_io_seconds_ago:-1
master_sync_in_progress:0
slave_repl_offset:1430
master_link_down_since_seconds:33
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:7c8238302676bda655b8791ce2065d29fa50f0fd
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1430
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:1430
6.2 测试多哨兵模式
6.2.1 环境搭建
- 新建目录,复制redis到该目录,复制sentinel配置文件,并修改该配置文件
[root@10 ~]# mkdir /usr/local/sentinel
[root@10 ~]# cp -r /usr/local/redis/bin/* /usr/local/sentinel
[root@10 ~]# cd /usr/local/tmp/redis-5.0.5/
[root@10 redis-5.0.5]# cp sentinel.conf /usr/local/sentinel/
[root@10 redis-5.0.5]# cd /usr/local/sentinel
[root@10 sentinel]# vim sentinel.conf
port 26379
daemonize yes
logfile “/usr/local/sentinel/26379.log”
sentinel monitor mymaster 192.168.93.10 6379 2
[root@10 sentinel]# cp sentinel.conf sentinel-26380.conf
[root@10 sentinel]# vim sentinel-26380.conf
port 26380
daemonize yes
logfile “/usr/local/sentinel/26380.log”
sentinel monitor mymaster 192.168.93.10 6379 2
[root@10 sentinel]# cp sentinel.conf sentinel-26381.conf
[root@10 sentinel]# vim sentinel-26381.conf
port 26381
daemonize yes
logfile “/usr/local/sentinel/26381.log”
sentinel monitor mymaster 192.168.93.10 6379 2
6.2.2 哨兵模式启动主从
- PS:现有运行实例启动前记得退出
# 查看redis进程
ps aux|grep redis
# kill进程
kill -9 进程号
- 启动主从节点后,启动哨兵
[root@10 sentinel]# cd /usr/local/replica
[root@10 replica]# ./startup.sh
# 启动日志
1420:C 03 Aug 2022 13:06:56.969 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1420:C 03 Aug 2022 13:06:56.969 # Redis version=5.0.5, bits=64, commit=00000000, modified=0, pid=1420, just started
1420:C 03 Aug 2022 13:06:56.969 # Configuration loaded
1422:C 03 Aug 2022 13:06:56.994 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1422:C 03 Aug 2022 13:06:56.994 # Redis version=5.0.5, bits=64, commit=00000000, modified=0, pid=1422, just started
1422:C 03 Aug 2022 13:06:56.994 # Configuration loaded
1427:C 03 Aug 2022 13:06:57.002 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1427:C 03 Aug 2022 13:06:57.002 # Redis version=5.0.5, bits=64, commit=00000000, modified=0, pid=1427, just started
1427:C 03 Aug 2022 13:06:57.002 # Configuration loaded
# 启动3个哨兵
[root@10 replica]# cd /usr/local/sentinel
[root@10 sentinel]# ./redis-sentinel sentinel.conf
[root@10 sentinel]# ./redis-sentinel sentinel-26380.conf
[root@10 sentinel]# ./redis-sentinel sentinel-26381.conf
# 查看 26379.log
[root@10 sentinel]# cat 26379.log
1436:X 03 Aug 2022 13:07:12.326 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1436:X 03 Aug 2022 13:07:12.326 # Redis version=5.0.5, bits=64, commit=00000000, modified=0, pid=1436, just started
1436:X 03 Aug 2022 13:07:12.326 # Configuration loaded
1437:X 03 Aug 2022 13:07:12.327 * Increased maximum number of open files to 10032 (it was originally set to 1024).
1437:X 03 Aug 2022 13:07:12.328 * Running mode=sentinel, port=26379.
1437:X 03 Aug 2022 13:07:12.328 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
1437:X 03 Aug 2022 13:07:12.330 # Sentinel ID is 140710b436cdd3171257eb11bdc80ef6164f1e63
1437:X 03 Aug 2022 13:07:12.330 # +monitor master mymaster 192.168.56.30 6379 quorum 2
1437:X 03 Aug 2022 13:07:12.331 * +slave slave 192.168.56.30:6380 192.168.56.30 6380 @ mymaster 192.168.56.30 6379
1437:X 03 Aug 2022 13:07:12.332 * +slave slave 192.168.56.30:6381 192.168.56.30 6381 @ mymaster 192.168.56.30 6379
1437:X 03 Aug 2022 13:07:21.117 * +sentinel sentinel b8f8c2e845b6fb424267b95caee61be5d77bcb7b 192.168.56.30 26380 @ mymaster 192.168.56.30 6379
1437:X 03 Aug 2022 13:07:27.109 * +sentinel sentinel 3eb6b69bc9fd016b9b717e6c590d552644da39bf 192.168.56.30 26381 @ mymaster 192.168.56.30 6379
6.2.3 测试宕机
- kill主节点线程
- 查看两个从节点信息,发现6381这个节点变为主节点,写操作确认成功
127.0.0.1:6381> info replication
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.56.30,port=6380,state=online,offset=54023,lag=1
master_replid:6c35bccbf6154c3d397a1b29bdfca13c8f8b4e6b
master_replid2:0d70592473908342c2899010c2a84ecd25032ab7
master_repl_offset:54023
second_repl_offset:37091
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:54023
127.0.0.1:6381> set name 6381
OK
- 重启6379节点,发现该节点未恢复为主节点,而是变成从节点
[root@10 master]# ./redis-server redis.conf
1467:C 03 Aug 2022 13:15:11.815 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1467:C 03 Aug 2022 13:15:11.815 # Redis version=5.0.5, bits=64, commit=00000000, modified=0, pid=1467, just started
1467:C 03 Aug 2022 13:15:11.815 # Configuration loaded
[root@10 master]# ./redis-cli -p 6379
127.0.0.1:6379> info replication
# Replication
role:slave
master_host:192.168.56.30
master_port:6381
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_repl_offset:105579
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:6c35bccbf6154c3d397a1b29bdfca13c8f8b4e6b
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:105579
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:94771
repl_backlog_histlen:10809
- 当然也可以通过日志(如26379.log)查看节点变更信息,这里不做介绍了
[root@10 sentinel]# ll
total 32852
-rw-r--r--. 1 root root 2424 Aug 3 13:15 26379.log
-rw-r--r--. 1 root root 3848 Aug 3 13:15 26380.log
-rw-r--r--. 1 root root 2550 Aug 3 13:15 26381.log
-rw-r--r--. 1 root root 261 Aug 3 12:51 dump.rdb
-rwxr-xr-x. 1 root root 4366576 Aug 3 12:51 redis-benchmark
-rwxr-xr-x. 1 root root 8111824 Aug 3 12:51 redis-check-aof
-rwxr-xr-x. 1 root root 8111824 Aug 3 12:51 redis-check-rdb
-rwxr-xr-x. 1 root root 4806808 Aug 3 12:51 redis-cli
-rw-r--r--. 1 root root 61798 Aug 3 12:51 redis.conf
lrwxrwxrwx. 1 root root 12 Aug 3 12:51 redis-sentinel -> redis-server
-rwxr-xr-x. 1 root root 8111824 Aug 3 12:51 redis-server
-rw-r--r--. 1 root root 10104 Aug 3 13:10 sentinel-26380.conf
-rw-r--r--. 1 root root 10104 Aug 3 13:10 sentinel-26381.conf
-rw-r--r--. 1 root root 10104 Aug 3 13:10 sentinel.conf
7. 集群(Cluster)
- 前提:已经安装好redis单机版
- 当集群中超过或等于1/2节点不可用时,整个集群不可用。为了搭建稳定集群,都采用奇数节点。
7.1 集群环境搭建准备
-
复制conf文件,修改复制的文件
-
修改内容如下
port 7001 cluster-enabled yes cluster-config-file nodes-7001.conf cluster-node-timeout 15000 appendonly no如果开启aof默认,需要修改为yes。如果使用rdb,此处不需要修改 daemonize yes protected-mode no pidfile /var/run/redis_7001.pid
-
新复制的5个配置文件都需要需改三处。例如nodes-7002.conf中需要把所有7001都换成7002。可以使用
:%s/7001/7002/g
进行全局修改。
[root@10 bin]# cp /usr/local/redis/bin/redis.conf /usr/local/redis/bin/redis-7001.conf
[root@10 bin]# vim redis-7001.conf
[root@10 bin]# cp redis-7001.conf redis-7002.conf
[root@10 bin]# cp redis-7001.conf redis-7003.conf
[root@10 bin]# cp redis-7001.conf redis-7004.conf
[root@10 bin]# cp redis-7001.conf redis-7005.conf
[root@10 bin]# cp redis-7001.conf redis-7006.conf
[root@10 bin]# vim redis-7002.conf
[root@10 bin]# vim redis-7003.conf
[root@10 bin]# vim redis-7004.conf
[root@10 bin]# vim redis-7005.conf
[root@10 bin]# vim redis-7006.conf
7.2 启动6个redis
- 删除之前的
dump.rdb
文件,配置startup.sh
文件,一键启动
[root@10 bin]# rm -f dump.rdb
[root@10 bin]# vim startup,sh
# 配置内容如下
./redis-server redis-7001.conf
./redis-server redis-7002.conf
./redis-server redis-7003.conf
./redis-server redis-7004.conf
./redis-server redis-7005.conf
./redis-server redis-7006.conf
# 启动redis
[root@10 bin]# chmod a+x startup.sh
[root@10 bin]# ./startup.sh
1547:C 03 Aug 2022 14:19:39.615 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1547:C 03 Aug 2022 14:19:39.615 # Redis version=5.0.5, bits=64, commit=00000000, modified=0, pid=1547, just started
1547:C 03 Aug 2022 14:19:39.615 # Configuration loaded
1549:C 03 Aug 2022 14:19:39.622 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1549:C 03 Aug 2022 14:19:39.622 # Redis version=5.0.5, bits=64, commit=00000000, modified=0, pid=1549, just started
1549:C 03 Aug 2022 14:19:39.622 # Configuration loaded
1551:C 03 Aug 2022 14:19:39.633 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1551:C 03 Aug 2022 14:19:39.633 # Redis version=5.0.5, bits=64, commit=00000000, modified=0, pid=1551, just started
1551:C 03 Aug 2022 14:19:39.633 # Configuration loaded
1556:C 03 Aug 2022 14:19:39.642 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1556:C 03 Aug 2022 14:19:39.642 # Redis version=5.0.5, bits=64, commit=00000000, modified=0, pid=1556, just started
1556:C 03 Aug 2022 14:19:39.642 # Configuration loaded
1561:C 03 Aug 2022 14:19:39.651 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1561:C 03 Aug 2022 14:19:39.651 # Redis version=5.0.5, bits=64, commit=00000000, modified=0, pid=1561, just started
1561:C 03 Aug 2022 14:19:39.651 # Configuration loaded
1566:C 03 Aug 2022 14:19:39.659 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1566:C 03 Aug 2022 14:19:39.659 # Redis version=5.0.5, bits=64, commit=00000000, modified=0, pid=1566, just started
1566:C 03 Aug 2022 14:19:39.659 # Configuration loaded
- 可以使用
ps aux|grep redis
查看redis启动状态
7.3 建立集群
- PS:当前版本为5.0.5可以使用redis-cli实现集群功能,redis3的时候需要借助ruby脚本
[root@10 bin]# ./redis-cli --cluster create 192.168.56.30:7001 192.168.56.30:7002 192.168.56.30:7003 192.168.56.30:7004 192.168.56.30:7005 192.168.56.30:7006 --cluster-replicas 1
### 集群搭建成功
>>> Performing hash slots allocation on 6 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 192.168.56.30:7005 to 192.168.56.30:7001
Adding replica 192.168.56.30:7006 to 192.168.56.30:7002
Adding replica 192.168.56.30:7004 to 192.168.56.30:7003
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: 27115af9b4c58e102777f3908c84e7820725a5a1 192.168.56.30:7001
slots:[0-5460] (5461 slots) master
M: 419bd67bf5b1d0ae2b50e5fba0b9d0ee65efacae 192.168.56.30:7002
slots:[5461-10922] (5462 slots) master
M: 2e636dbbf883bc4f5e0f475fcb7086b2f2adae36 192.168.56.30:7003
slots:[10923-16383] (5461 slots) master
S: 8be4877aa338e5c3655bf3b98b1bb8f6a00bd6bf 192.168.56.30:7004
replicates 419bd67bf5b1d0ae2b50e5fba0b9d0ee65efacae
S: 7b43d1dac5fb72845a018982930d7d932712eb58 192.168.56.30:7005
replicates 2e636dbbf883bc4f5e0f475fcb7086b2f2adae36
S: f85abf55ea9c2623d9bcb8ced99f046532495611 192.168.56.30:7006
replicates 27115af9b4c58e102777f3908c84e7820725a5a1
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
.....
>>> Performing Cluster Check (using node 192.168.56.30:7001)
M: 27115af9b4c58e102777f3908c84e7820725a5a1 192.168.56.30:7001
slots:[0-5460] (5461 slots) master
1 additional replica(s)
S: f85abf55ea9c2623d9bcb8ced99f046532495611 192.168.56.30:7006
slots: (0 slots) slave
replicates 27115af9b4c58e102777f3908c84e7820725a5a1
M: 419bd67bf5b1d0ae2b50e5fba0b9d0ee65efacae 192.168.56.30:7002
slots:[5461-10922] (5462 slots) master
1 additional replica(s)
M: 2e636dbbf883bc4f5e0f475fcb7086b2f2adae36 192.168.56.30:7003
slots:[10923-16383] (5461 slots) master
1 additional replica(s)
S: 8be4877aa338e5c3655bf3b98b1bb8f6a00bd6bf 192.168.56.30:7004
slots: (0 slots) slave
replicates 419bd67bf5b1d0ae2b50e5fba0b9d0ee65efacae
S: 7b43d1dac5fb72845a018982930d7d932712eb58 192.168.56.30:7005
slots: (0 slots) slave
replicates 2e636dbbf883bc4f5e0f475fcb7086b2f2adae36
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
## 可以看到当前目录生成的节点和RDB文件
[root@10 bin]# ls
dump.rdb nodes-7003.conf nodes-7006.conf redis-7003.conf redis-7006.conf redis-check-rdb redis-sentinel
nodes-7001.conf nodes-7004.conf redis-7001.conf redis-7004.conf redis-benchmark redis-cli redis-server
nodes-7002.conf nodes-7005.conf redis-7002.conf redis-7005.conf redis-check-aof redis.conf startup.sh
[root@10 bin]#
7.4 集群测试
- 测试时不要忘记
-c
这个参数
[root@10 master]# ./redis-cli -p 7001 -c
127.0.0.1:7001> set age 100
OK
[root@10 slave1]# ./redis-cli -p 7002 -c
127.0.0.1:7002> get age
-> Redirected to slot [741] located at 192.168.56.30:7001
"100"
[root@10 slave2]# ./redis-cli -p 7005 -c
127.0.0.1:7005> get age
-> Redirected to slot [741] located at 192.168.56.30:7001
"100"
7.5 编写关闭脚本
- 可以使用
ps aux|grep redis
命令查看是否关闭
[root@10 bin]# vim stop.sh
./redis-cli -p 7001 shutdown
./redis-cli -p 7002 shutdown
./redis-cli -p 7003 shutdown
./redis-cli -p 7004 shutdown
./redis-cli -p 7005 shutdown
./redis-cli -p 7006 shutdown
[root@10 bin]# chmod a+x stop.sh
[root@10 bin]# ./stop.sh
8. Redis常用API
8.1 Jedis
- Redis官方推荐的java连接开发工具
- 导入依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>3.2.0</version>
</dependency>
- 连接测试
public class TestJedis {
public static void main(String[] args) throws JSONException {
// 创建jedis对象,指定redis地址和端口
Jedis jedis = new Jedis("192.168.56.30",6379);
// jedis指令演示
System.out.println(jedis.ping());
jedis.flushDB();
JSONObject jsonObject = new JSONObject();
jsonObject.put("hello","world");
jsonObject.put("name","zhangsan");
//开启事务
Transaction multi = jedis.multi();
String result = jsonObject.toString();
try {
multi.set("user1",result);
multi.set("user2",result);
int i = 1/0;
// 提交事务
multi.exec();
} catch (Exception e){
//放弃事务
multi.discard();
e.printStackTrace();
}finally {
System.out.println(jedis.get("user1"));
System.out.println(jedis.get("user2"));
// 关闭连接
jedis.close();
}
//PONG
//java.lang.ArithmeticException: / by zero
// at com.xxx.demo1.TestJedis.main(TestJedis.java:28)
//null
//null 事务执行失败回滚,user1,user2为null
}
}
8.2 SpringBoot整合Redis
- SpringBoot 2.x后,jedis被替换为lettuce
- jedis:底层采用直连,多线程操作是不安全的,为避免线程安全问题,采用jedis pool(BIO)
- lettuce:采用netty,实例可以在多个线程中进行共享,不仅性能高,还可以减少线程数量(NIO)
# 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.7.0</version>
</dependency>
# redis简单配置
spring.redis.host=192.168.56.30
spring.redis.port=6379
- 测试
@SpringBootTest
class Demo3ApplicationTests {
@Autowired
private RedisTemplate redisTemplate;
/**
* redisTemplate 操作不同类型的数据,API基本与指令一致
* opsForValue操作字符串类似String
* opsForList
* opsForSet
* opsForHash
* opsForZSet
* opsForGeo等
* 除了基本的操作,还有一些常用的的方法都可以直接通过redisTemplate操作,如事务,基本的CRUD等
*/
@Test
void contextLoads() {
//获取连接对象,清空数据库
// RedisConnection connection = redisTemplate.getConnectionFactory().getConnection();
// connection.flushAll();
// connection.flushDb();
redisTemplate.opsForValue().set("myName1", "my name is mimi!");
System.out.println("myName1.value = " + redisTemplate.opsForValue().get("myName1"));
redisTemplate.opsForValue().set("myName2", "my name is 咪咪!");
System.out.println("myName2.value = " + redisTemplate.opsForValue().get("myName2"));
//执行结果
//myName1.value = my name is mimi!
//myName2.value = my name is 咪咪!
}
}
- redis 内的值显示乱码
127.0.0.1:6379> keys *
1) "\xac\xed\x00\x05t\x00\amyName2"
2) "\xac\xed\x00\x05t\x00\amyName1"
3) "\xac\xed\x00\x05t\x00\x06myName"
- 这是因为默认的序列化方式是JDK序列化,可以更改为JSON序列化
- 可以通过重写RedisTemplate配置类解决
@Configuration
public class RedisConfig {
//自定义RedisTemplate
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(factory);
// JSON序列化配置
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
// @Autowired
// @Qualifier("redisTemplate")
// private RedisTemplate redisTemplate;
// 可以通过Qualifier注解指定配置
8.3 RedisUtil
- 自己封装的工具,
@Component
public final class RedisUtil {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
// =============================common============================
/**
* 指定缓存失效时间
* @param key 键
* @param time 时间(秒)
*/
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 getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 判断key是否存在
* @param key 键
* @return true 存在 false不存在
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除缓存
* @param key 可以传一个值 或多个
*/
@SuppressWarnings("unchecked")
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete(CollectionUtils.arrayToList(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 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 递增
* @param key 键
* @param delta 要增加几(大于0)
*/
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)
*/
public long decr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, -delta);
}
// ================================Map=================================
/**
* HashGet
* @param key 键 不能为null
* @param item 项 不能为null
*/
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}
/**
* 获取hashKey对应的所有键值
* @param key 键
* @return 对应的多个键值
*/
public Map<Object, Object> hmget(String key) {
return redisTemplate.opsForHash().entries(key);
}
/**
* HashSet
* @param key 键
* @param map 对应多个键值
*/
public boolean hmset(String key, Map<String, Object> map) {
try {
redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* HashSet 并设置时间
* @param key 键
* @param map 对应多个键值
* @param time 时间(秒)
* @return true成功 false失败
*/
public boolean hmset(String key, Map<String, Object> map, long time) {
try {
redisTemplate.opsForHash().putAll(key, map);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张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 键
* @param item 项
* @param value 值
* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value, long time) {
try {
redisTemplate.opsForHash().put(key, item, value);
if (time > 0) {
expire(key, time);
}
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);
}
/**
* 判断hash表中是否有该项的值
*
* @param key 键 不能为null
* @param item 项 不能为null
* @return true 存在 false不存在
*/
public boolean hHasKey(String key, String item) {
return redisTemplate.opsForHash().hasKey(key, item);
}
/**
* hash递增 如果不存在,就会创建一个 并把新增后的值返回
*
* @param key 键
* @param item 项
* @param by 要增加几(大于0)
*/
public double hincr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, by);
}
/**
* hash递减
*
* @param key 键
* @param item 项
* @param by 要减少记(小于0)
*/
public double hdecr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, -by);
}
// ============================set=============================
/**
* 根据key获取Set中的所有值
* @param key 键
*/
public Set<Object> sGet(String key) {
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 根据value从一个set中查询,是否存在
*
* @param key 键
* @param value 值
* @return true 存在 false不存在
*/
public boolean sHasKey(String key, Object value) {
try {
return redisTemplate.opsForSet().isMember(key, value);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将数据放入set缓存
*
* @param key 键
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSet(String key, Object... values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 将set数据放入缓存
*
* @param key 键
* @param time 时间(秒)
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSetAndTime(String key, long time, Object... values) {
try {
Long count = redisTemplate.opsForSet().add(key, values);
if (time > 0)
expire(key, time);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 获取set缓存的长度
*
* @param key 键
*/
public long sGetSetSize(String key) {
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 移除值为value的
*
* @param key 键
* @param values 值 可以是多个
* @return 移除的个数
*/
public long setRemove(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代表所有值
*/
public List<Object> lGet(String key, long start, long end) {
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 获取list缓存的长度
*
* @param key 键
*/
public long lGetListSize(String key) {
try {
return redisTemplate.opsForList().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 通过索引 获取list中的值
*
* @param key 键
* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
*/
public Object lGetIndex(String key, long index) {
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
*/
public boolean lSet(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
* @param key 键
* @param value 值
* @param time 时间(秒)
*/
public boolean lSet(String key, Object value, long time) {
try {
redisTemplate.opsForList().rightPush(key, value);
if (time > 0)
expire(key, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, List<Object> value) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, List<Object> value, long time) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
if (time > 0)
expire(key, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据索引修改list中的某条数据
*
* @param key 键
* @param index 索引
* @param value 值
* @return
*/
public boolean lUpdateIndex(String key, long index, Object value) {
try {
redisTemplate.opsForList().set(key, index, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 移除N个值为value
*
* @param key 键
* @param count 移除多少个
* @param value 值
* @return 移除的个数
*/
public long lRemove(String key, long count, Object value) {
try {
Long remove = redisTemplate.opsForList().remove(key, count, value);
return remove;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
}
9. 其他
9.1 Redis发布订阅
- 感觉这玩意儿也不是专门做这个的,实际项目应该没人用,顶多面试问问,碰到好的面试题再回来更新。