Redis认识
注:本文档仅代表个人笔记,如有错误请见谅
Redis认识操作以及理解
1、认识
Redis(全称:Remote Dictionary Server 远程字典服务)是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、key-value数据库,并提供多种语言的API,从2010年3月15日起,Redis的开发工作由VMware主持,从2013年5月开始,Redis的开发由Povotal赞助
Redis是完全开源免费的,是一个高性能的key-value内存数据库
1.1、关系型与非关系型数据库
关系型数据库(RDBMS)和非关系型数据库(NoSQL)是两种不同的数据库类型,它们在数据存储、数据模型、数据一致性、扩展性等方面存在一些区别。
- 数据模型:
- 关系型数据库使用表格(表)来组织数据,每个表包含多个行和列,通过定义表之间的关系(外键)来建立数据之间的连接。
- 非关系型数据库使用各种数据模型,如键值对、文档、列族、图形等,可以更自由地组织和存储数据。
- 数据一致性:
- 关系型数据库强调数据的一致性,遵循ACID(原子性、一致性、隔离性和持久性)事务特性,确保数据的完整性和可靠性。
- 非关系型数据库通常以牺牲一致性为代价,追求高可用性和分布式扩展性,采用BASE(基本可用、软状态、最终一致性)原则。
- 扩展性:
- 关系型数据库通常采用垂直扩展(增加硬件资源)的方式来提高性能和容量,但在大规模数据处理和高并发场景下,成本较高。
- 非关系型数据库更适合横向扩展(分布式集群)的方式,可以通过添加更多的节点来提高性能和容量,并且更适合处理大规模数据和高并发访问。
- 灵活性:
- 关系型数据库对于数据结构和模式有严格的要求,需要预先定义表结构和字段类型,难以适应动态变化和非结构化数据。
- 非关系型数据库更加灵活,可以存储半结构化和非结构化数据,无需事先定义模式,适用于快速迭代和灵活的数据模型。
- 常见的关系型数据库:MySQL、Oracle、SQLServer,PostgreSQL,ClickHouse等
- 常见的非关系型数据库:Redis(键值数据库,用于缓存/持久储存)、MongoDB(文档存储数据库)、Memcached(键值数据库用于缓存)
NoSQL用于超大规模数据的存储,收集万亿比特的数据。这些类型的数据存储没有固定的形式,没有多余操作
就可以横向扩展。
1.2、Reids的特点
- Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用
- Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储
- Redis支持数据的备份,即master-slave模式的数据备份。
1.3、redis的优缺点
优点:
- 高性能:Redis 是一个高性能的 KV 存储系统,它的数据全部存储在内存中,数据访问速度极快,可以支持高并发读写操作。
- 多种数据结构:Redis 的数据结构非常丰富,包括字符串、哈希表、列表、集合和有序集合等多种数据结构,广泛应用于缓存、队列、计数器、排行榜等场景。
- 事务支持:Redis 支持事务操作,可以将多个命令打包成一个事务,同时执行,保证事务的原子性、一致性和隔离性。
- 持久化支持:Redis 支持两种持久化方式(RDB 和 AOF),可以将内存中的数据持久化到磁盘中,保证数据不会因进程退出或机器宕机而丢失。
- 主从复制:Redis 支持主从复制,可以设置一个或多个从节点,主节点负责写操作,从节点负责读操作,这样可以提高读取性能,同时还能提高数据的可用性和可靠性。
- 集群支持:Redis 支持集群方式部署,可以将数据分散到多个节点上,提高系统的容量和性能,同时还能提供高可用性和负载均衡。
缺点:
- 内存限制:Redis 的数据存储全部在内存中,因此它的数据存储容量受限于物理内存的大小。如果存储的数据量超过了内存的限制,就需要使用 Redis 集群,这会增加部署和维护的复杂度。
- 持久化性能:Redis 支持两种持久化方式:RDB(定期快照)和 AOF(追加式日志),但是对于写入操作来说,它们都会对性能产生一定的影响。尤其是 AOF,它会不断追加日志,当 AOF 文件过大时,Redis 重启会花费大量时间进行重建。
- 数据结构限制:Redis 的数据结构种类不多,主要包括字符串、哈希表、列表、集合和有序集合。虽然这些数据结构能够满足大多数需求,但是有时候需要更加丰富的数据结构,比如图、树等。
- 部署复杂度:Redis 部署有多种方式,包括单机、主从、哨兵、集群等多种架构。这些架构各有优缺点,选择适合的架构需要考虑多个因素,如数据量、访问量、安全性、可用性等。部署难度和复杂度较大,需要有一定的技术经验支持。
2、Redis部署
(转载)Linux-安装Redis(详细教程) - ddgo's - 博客园 (cnblogs.com)
3、Redis的常用命令
-
启动
- redis-server [--port 6379] #默认端口是6379
- redis-server [xx/xx/redis.conf] #参数多可以使用配置文件启动
-
连接reids
- ./redis-cli [-h 127.0.0.1 -p 6379]
-
停止reids
-
redis-cli shutdown
-
kill redis-pid
两个命令作用相同
-
-
发送命令
-
redis-cli 带参数运行比如
redis-cli shutdown
-
redis-cli 不带参数运行比如
./redis-cli
127.0.0.1:6379
>
shutdownnot connected
-
-
测试连通性
- ping
key操作命令
-
获取所有键
-
key
*
*
表示通配符,表示任意字符,会遍历所有键显示所有的键列表,时间复杂度O(n),在生产环境不建议使用。
-
-
获取键总数
- dbsize
获取键总数时不会遍历所有的键,直接获取内部变量,时间复杂度O(1)。
-
查询键是否存在
-
exists key #单个
-
exists [key ...] #多个
查询查询多个,返回存在的个数。
-
-
删除键
- del key #单个
- del [key...] #多个
可以删除多个,返回删除成功的个数。
-
查询键类型
- type key
-
移动键
- move key db #key是键名,db是要移到那个库中
-
查询key的生命周期(秒)
- 秒语法 ttl key
- 毫秒语法 pttl key
-
设置过期时间
- 秒语法:expire key seconds
- 毫秒语法:pexpire key milliseconds
-
设置永不过期
-
persist key
127.0.0.1:6379[2]> persist javastack (integer) 1 1表示设置成功
-
-
更改键名称
- rename key newkey
4、redis的数据类型
Redis支持五种数据类型:String(字符串),Hash(哈希),List(列表),Set(集合)及Zset(sorted set:有序集合)。
4.1string字符串简介
字符串类型是redis最基础的数据结构,首先键是字符串类型,而且其他几种结构都是在字符串类型基础上构建的,所以字符串类型为其他四种数据结构尊定了基础。
字符串类型实际上可以是简单的字符串、复杂的字符串(xml、json)、数字(整数、浮点数)、二进制(图片、音频、视频)。
4.1.1字符串应用场景:
共享session:
用户登录后,用户刷新一次可能会重复登录,这个时候我们就将用户session集中管理,在这种模式下每次获取和更新用户信息都可以从redis中获取。
限速:
当我们在登录时,用来限制为我手机接到验证码的时间,防止我们的短信接口不被频繁访问,会限制用户每分钟获取验证码的频率。
4.1.2字符串命令
设置键的值为什么
- set 键 值
set key value [EX seconds] [PX milliseconds] [NX|XX] #将字符串值value关联到key
EX:设置键的过期时间为 second 秒
PX:设置键的过期时间为 millisecond 毫秒
NX:只在键不存在时,才对键进行设置操作。
XX:只在键已经存在时,才对键进行设置操作。
例:
set key1 hello EX 1000
OK
get key1 "hello"
ttl key1
(integer) 992
注:
因为 SET 命令可以通过参数来实现和 SETNX 、 SETEX 和 PSETEX 三个命令的效果:
SETNX:若给定的 key 已经存在,则 SETNX 不做任何动作
SETEX:如果 key 已经存在, SETEX 命令将覆写旧值。
PSETEX:这个命令和 SETEX 命令相似,但它以毫秒为单位设置 key 的生存时间,而不是像 SETEX 命令那样,以秒为单位。
-
get 键
返回key所关联的字符串值,当 key 不存在时,返回 nil ,否则,返回 key 的值。如果 key 不是字符串类型,那么返回一个错误。
-
mset key value [key value …]
同时设置一个或多个键值对
-
mset key value [key value …]
返回所有(一个或多个)给定 key 的值。
如果给定的 key 里面,有某个 key 不存在,那么这个 key 返回特殊值 nil 。因此,该命令永不失败。
-
append key value
如果 key 已经存在并且是一个字符串, APPEND 命令将 value 追加到 key 原来的值的末尾。
如果 key 不存在, APPEND 就简单地将给定 key 设为 value ,就像执行 SET key value 一样。
-
decr key
将 key 中储存的数字值减一。
如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 DECR 操作。
如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。
127.0.0.1:6379> set k2 10
OK
127.0.0.1:6379> decr k2 (integer)
9
127.0.0.1:6379> get k2 "9"
127.0.0.1:6379> set k3 zhangsan
OK
127.0.0.1:6379> decr k3
(error) ERR value is not an integer or out of range
127.0.0.1:6379> decr k4
(integer) -1
127.0.0.1:6379> get k4 "-1"
127.0.0.1:6379> keys *
"k4"
"k2"
"k1"
"k3"
-
decrby key decrement
将 key 所储存的值减去减量 decrement 。
如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 DECRBY 操作。
如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。
127.0.0.1:6379> set k1 100 OK 127.0.0.1:6379> decrby k1 20 (integer) 80 127.0.0.1:6379> get k1 "80" 127.0.0.1:6379> decrby k2 20 (integer) -20 127.0.0.1:6379> get k2 "-20" 127.0.0.1:6379> set k3 zhangsan OK 127.0.0.1:6379> decrby k3 10 (error) ERR value is not an integer or out of range
-
incr key
将 key 中储存的数字值增一。
如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。
如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。
-
incrby key increment
将 key 所储存的值加上增量 increment 。
如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCRBY 命令。
如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。
-
incrbyfloat key increment
为 key 中所储存的值加上浮点数增量 increment 。
如果 key 不存在,那么 INCRBYFLOAT 会先将 key 的值设为 0 ,再执行加法操作。
127.0.0.1:6379> set m 10.05
OK
127.0.0.1:6379> incrbyfloat m 0.1
"10.15"
- getrange key start end
返回 key 中字符串值的子字符串,字符串的截取范围由 start 和 end 两个偏移量决定(包括 start 和 end 在内)。
负数偏移量表示从字符串最后开始计数, -1 表示最后一个字符, -2 表示倒数第二个,以此类推。
127.0.0.1:6379> set str 'hello world!'
OK
127.0.0.1:6379> getrange str 0 4
"hello"
127.0.0.1:6379> getrange str -4 -1
"rld!"
127.0.0.1:6379> getrange str -1 -5
""
127.0.0.1:6379> getrange str 0 -1
"hello world!"
127.0.0.1:6379> getrange str 0 20
"hello world!"
注
substr与getrange一样
getset key value
将给定 key 的值设为 value ,并返回 key 的旧值(old value)。
返回给定 key 的旧值。当 key 没有旧值时,也即是, key 不存在时,返回 nil 。
-
strlen key
返回 key 所储存的字符串值的长度。
当 key 储存的不是字符串值时,返回一个错误。
127.0.0.1:6379> set s 'hello world!' OK 127.0.0.1:6379> strlen s (integer) 12
4.2哈希类型概述
在redis中哈希类型是指键本身又是一种键值对结构:
value格式:{{key1,value1},…{keyn,valuen}}
4.2.1哈希类型应用场景
适合存储对象,并且可以像数据库中update一样,去修改其中的一个属性中的一个值。比如:用户(姓名、性别、年龄等),文章(标题、发布时间、作者、内容),那么我们就可以对用户里面的性别,年龄等属性做到一对一的修改、存储、读取。
4.2.2、哈希命令
-
hset key field value
将哈希表 key 中的域 field 的值设为 value 。
如果 key 不存在,一个新的哈希表被创建并进行 HSET 操作。
如果域 field 已经存在于哈希表中,旧值将被覆盖。
如果 field 是哈希表中的一个新建域,并且值设置成功,返回 1 。
如果哈希表中域 field 已经存在且旧值已被新值覆盖,返回 0 。
-
hget key field
返回哈希表 key 中给定域 field 的值。
给定域的值。
当给定域不存在或是给定 key 不存在时,返回 nil 。
-
hdel key field [field …]
删除哈希表 key 中的一个或多个指定域,不存在的域将被忽略。
注:
HDEL 每次只能删除单个域,如果你需要在一个原子时间内删除多个域,请将命令包含在 MULTI / EXEC 块内(事务)。
-
hexists key field
查看哈希表 key 中,给定域 field 是否存在。
如果哈希表含有给定域,返回 1 。
如果哈希表不含有给定域,或 key 不存在,返回 0 。
-
hgetall key
返回哈希表 key 中,所有的域和值。
以列表形式返回哈希表的域和域的值。
若 key不存在,返回空列表。
-
hincrby key field increment
为哈希表 key 中的域 field 的值加上增量 increment 。
增量也可以为负数,相当于对给定域进行减法操作。
-
hincrbyfloat key field increment
为哈希表 key 中的域 field 加上浮点数增量 increment 。
-
hkeys key
返回哈希表 key 中的所有域。
一个包含哈希表中所有域的表。
当 key 不存在时,返回一个空表。
-
hlen key
返回哈希表 key 中域的数量。
当 key 不存在时,返回 0 。
-
hmset key field value [field value …]
同时将多个 field-value (域-值)对设置到哈希表 key 中。
此命令会覆盖哈希表中已存在的域。
-
hmget key field [field …]
返回哈希表 key 中,一个或多个给定域的值。
如果给定的域不存在于哈希表,那么返回一个 nil 值。
-
hsetnx key field value
将哈希表 key 中的域 field 的值设置为 value ,当且仅当域 field 不存在。
若域 field 已经存在,该操作无效。
-
hvals key
返回哈希表 key 中所有域的值。
4.3、列表类型概述
列表类型是用来储存多个有序的字符串,列表类型中字符串称之为元素,一个列表最多可以储存2的32次方-1个元素。我们可以对列表两端插入和弹出操作,还可以通过索引下标获取指定范围的元素列表、获取指定索引下标元素等。
优点:
列表类型中的元素可以重复。
列表类型中的元素是有序的。
列表类型中的元素可以通过索引下标获取某个或某个范围内的元素列表。
4.3.1、使用场景
栈:先进后出
队列:先进先出
消息队列: 主要表现在生产者与消费者之间。
最新列表:在一个社交网站上,显示用户最近发表的帖子。如果这个社交网站拥有大量的用户和帖子,每次查询最新的帖子,都需要遍历整个帖子列表,并按照时间排序,会导致查询非常缓慢。相反,可以使用Redis的LPUSH命令将每个用户发表的帖子按照时间顺序塞进一个List中,再从该List中获取用户最近发表的几篇帖子。这样可以避免多次遍历整个帖子列表和排序,提高了查询性能。
lpush+lpop=Stack(栈)
lpush+rpop=Queue(队列)
lpush+ltrim=Capped Collection(有限集合)
lpush+brpop=Message Queue(消息队列)
Stack(栈):
左入栈:LPUSH key value [value ...]
左出栈:LPOP key
Queue(队列):
左入队:LPUSH key value [value ...]
右出队:RPOP key
Capped Collection(有限集合):
左入集合:LPUSH key value [value ...]
限制集合大小:LTRIM key start stop
Message Queue(消息队列):
左入队列:LPUSH key value [value ...]
右出队列(阻塞):BRPOP key timeout
4.3.2列表命令
- lpush key value [value …]
lpush k a b c #他的值为:c b a
#相当于
lpush k a
lpush k b
lpush k c
如果 key 不存在,一个空列表会被创建并执行 LPUSH 操作。
当 key 存在但不是列表类型时,返回一个错误。
127.0.0.1:6379> lpush k1 a b c
(integer) 3
127.0.0.1:6379> lpush k1 a
(integer) 4
127.0.0.1:6379> lrange k1 0 -1
1) "a"
2) "c"
3) "b"
4) "a"
注:
lpush可以重复,写在前面的在底部,后进来的在前面
- rpush key value [value …]
将一个或多个值 value 插入到列表 key 的表尾(最右边)。
如果有多个 value 值,那么各个 value 值按从左到右的顺序依次插入到表尾。
如:
rpush k a b c #他的值为:a b c
#相当于
rpush k a
rpush k b
rpush k c
如果 key 不存在,一个空列表会被创建并执行 RPUSH 操作。
当 key 存在但不是列表类型时,返回一个错误。
127.0.0.1:6379> rpush k2 a b c
(integer) 3
127.0.0.1:6379> rpush k2 a
(integer) 4
127.0.0.1:6379> lrange k2 0 -1
1) "a"
2) "b"
3) "c"
4) "a"
-
llen key
返回列表 key 的长度。
如果 key 不存在,则 key 被解释为一个空列表,返回 0 .
如果 key 不是列表类型,返回一个错误。
127.0.0.1:6379> llen k3 (integer) 0 127.0.0.1:6379> lpush k3 "hello world" "what you name" (integer) 2 127.0.0.1:6379> llen k3 (integer) 2
-
lrange key start stop
返回列表 key 中指定区间内的元素,区间以偏移量 start 和 stop 指定。
下标(index)参数 start 和 stop 都以 0 为底,也就是说,以 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。
你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。
127.0.0.1:6379> lrange k3 1 2 1) "hello world" 127.0.0.1:6379> lrange k3 0 2 1) "what you name" 2) "hello world" 127.0.0.1:6379> lrange k2 0 2 1) "a" 2) "b" 3) "c" 127.0.0.1:6379> lrange k2 0 -1 1) "a" 2) "b" 3) "c" 4) "a"
-
ltrim key start stop
让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。
如:
ltrim k1 0 2 #表示只保留列表 list 的前三个元素,其余元素全部删除。
下标(index)参数 start 和 stop 都以 0 为底,也就是说,以 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。
你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。
当 key 不是列表类型时,返回一个错误。
127.0.0.1:6379> lpush k4 a b c d e f g (integer) 7 127.0.0.1:6379> lrange k4 0 -1 1) "g" 2) "f" 3) "e" 4) "d" 5) "c" 6) "b" 7) "a" 127.0.0.1:6379> ltrim k4 1 -1 OK 127.0.0.1:6379> lrange k4 0 -1 1) "f" 2) "e" 3) "d" 4) "c" 5) "b" 6) "a" 127.0.0.1:6379> ltrim k4 1000 2000 OK 127.0.0.1:6379> lrange k4 0 -1 (empty list or set) 127.0.0.1:6379> rpush k5 a b c d e f g (integer) 7 127.0.0.1:6379> ltrim k5 1000 2000 OK 127.0.0.1:6379> lrange k5 0 -1 (empty list or set)
-
lset key index value
将列表 key 下标为 index 的元素的值设置为 value 。
当 index 参数超出范围,或对一个空列表( key 不存在)进行 LSET 时,返回一个错误。
127.0.0.1:6379> exists k1 (integer) 0 127.0.0.1:6379> lset k1 0 item1 (error) ERR no such key 127.0.0.1:6379> lpush k1 item1 item2 (integer) 2 127.0.0.1:6379> lset k1 0 item0 OK 127.0.0.1:6379> lrange k1 0 -1 1) "item0" 2) "item1" 127.0.0.1:6379> rpush k2 item1 item2 (integer) 2 127.0.0.1:6379> lset k2 0 item0 OK 127.0.0.1:6379> lrange k2 0 -1 1) "item0" 2) "item2" 127.0.0.1:6379> lset k1 5 item5 (error) ERR index out of range 127.0.0.1:6379> lset k2 5 item5 (error) ERR index out of range
-
lrem key count value
根据参数 count 的值,移除列表中与参数 value 相等的元素。
count 的值可以是以下几种:
count > 0: 从表头开始向表尾搜索,移除与value相等的元素,数量为count 。
count < 0: 从表尾开始向表头搜索,移除与value相等的元素,数量为count的绝对值。
count = 0 : 移除表中所有与 value 相等的值。
127.0.0.1:6379> lpush k3 a b d f a d f b e c (integer) 10 127.0.0.1:6379> lrange k3 0 -1 1) "c" 2) "e" 3) "b" 4) "f" 5) "d" 6) "a" 7) "f" 8) "d" 9) "b" 10) "a" 127.0.0.1:6379> lrem k3 1 a (integer) 1 127.0.0.1:6379> lrange k3 0 -1 1) "c" 2) "e" 3) "b" 4) "f" 5) "d" 6) "f" 7) "d" 8) "b" 9) "a" 127.0.0.1:6379> lrem k3 1 f (integer) 1 127.0.0.1:6379> lrange k3 0 -1 1) "c" 2) "e" 3) "b" 4) "d" 5) "f" 6) "d" 7) "b" 8) "a" 127.0.0.1:6379> lrem k3 2 d (integer) 2 127.0.0.1:6379> lrange k3 0 -1 1) "c" 2) "e" 3) "b" 4) "f" 5) "b" 6) "a" 127.0.0.1:6379> lrem k3 -1 a (integer) 1 127.0.0.1:6379> lrange k3 0 -1 1) "c" 2) "e" 3) "b" 4) "f" 5) "b" 127.0.0.1:6379> lrem k3 0 b (integer) 2 127.0.0.1:6379> lrange k3 0 -1 1) "c" 2) "e" 3) "f"
-
lpop key
移除并返回列表 key 的头元素。
127.0.0.1:6379> lpush a 212 fdsf fds fd fsd fds (integer) 6 127.0.0.1:6379> lrange a 0 -1 1) "fds" 2) "fsd" 3) "fd" 4) "fds" 5) "fdsf" 6) "212" 127.0.0.1:6379> lpop a "fds" 127.0.0.1:6379> lrange a 0 -1 1) "fsd" 2) "fd" 3) "fds" 4) "fdsf" 5) "212" 127.0.0.1:6379> rpush b fdsf fds fd s fd f d (integer) 7 127.0.0.1:6379> lrange b 0 -1 1) "fdsf" 2) "fds" 3) "fd" 4) "s" 5) "fd" 6) "f" 7) "d" 127.0.0.1:6379> lpop b "fdsf" 127.0.0.1:6379> lrange b 0 -1 1) "fds" 2) "fd" 3) "s" 4) "fd" 5) "f" 6) "d"
-
rpop key
移除并返回列表 key 的尾元素。
127.0.0.1:6379> lrange a 0 -1 1) "fsd" 2) "fd" 3) "fds" 4) "fdsf" 5) "212" 127.0.0.1:6379> rpop a "212" 127.0.0.1:6379> lrange a 0 -1 1) "fsd" 2) "fd" 3) "fds" 4) "fdsf" 127.0.0.1:6379> lrange b 0 -1 1) "fds" 2) "fd" 3) "s" 4) "fd" 5) "f" 6) "d" 127.0.0.1:6379> rpop b "d" 127.0.0.1:6379> lrange b 0 -1 1) "fds" 2) "fd" 3) "s" 4) "fd" 5) "f" 127.0.0.1:6379>
-
blpop key [key …] timeout
当给定多个 key 参数时,按参数 key 的先后顺序依次检查各个列表,弹出第一个非空列表的头元素。
当key元素存在,非阻塞行为
127.0.0.1:6379> lpush a hello (integer) 1 127.0.0.1:6379> lpush b world (integer) 1 127.0.0.1:6379> blpop a b 0 1) "a" 2) "hello" 127.0.0.1:6379> blpop c a b 0 1) "b" 2) "world"
当key元素不存在,阻塞行为
127.0.0.1:6379> blpop a b c 0
-
brpop key [key …] timeout
当给定多个 key 参数时,按参数 key 的先后顺序依次检查各个列表,弹出第一个非空列表的尾部元素。
127.0.0.1:6379> llen a (integer) 0 127.0.0.1:6379> rpush a java (integer) 1 127.0.0.1:6379> rpush a python (integer) 2 127.0.0.1:6379> brpop a 30 1) "a" 2) "python"
4.4、集合
集合类型也是用来保存多个字符串的元素,集合类型中不允许有重复的元素,集合类型中元素是无序的,不能通过索引下标获取元素。在redis除了学习集合的增删改查,同时还可以对集合类型进行取交集、并集、差集。
4.4.1使用场景
- 数组去重:当我们需要去重一个数组时,可以将数组中的元素加入Redis中的一个集合中,由于集合元素的唯一性,最终从集合中取出的就是原数组中的唯一元素。
- 用户标签:我们可以将用户的兴趣标签和行为标签保存在一个集合里,利用集合的交集、并集和差集等操作,轻松地实现多个用户对应的标签匹配。
- 访问量计数:我们可以利用集合进行访问量的去重计数,将每个IP地址的访问记录存入一个以日期和网站URL为联合主键的Redis集合中,然后统计当前集合的元素数量即可得到今天该网站的访问量。
- 简单推荐系统:我们可以将一个用户的浏览记录保存在一个集合中,将每个页面浏览过的用户ID保存在以页面ID为键的Redis集合中,然后通过计算不同页面集合之间的相交数来推荐给用户感兴趣的其它页面
4.4.2、集合命令
-
sadd key member [member …]
将一个或多个 member 元素加入到集合 key 当中,已经存在于集合的 member 元素将被忽略。
假如 key 不存在,则创建一个只包含 member 元素作成员的集合。
当 key 不是集合类型时,返回一个错误。
127.0.0.1:6379> sadd s_a 123 234 345 (integer) 3 127.0.0.1:6379> smembers s_a 1) "123" 2) "234" 3) "345" 127.0.0.1:6379> sadd s_a 123 987 (integer) 1 127.0.0.1:6379> smembers s_a 1) "123" 2) "234" 3) "345" 4) "987"
-
smembers key
返回集合 key 中的所有成员。
不存在的 key 被视为空集合。
127.0.0.1:6379> sadd s_d dfs fds fds (integer) 2 127.0.0.1:6379> smembers s_d 1) "dfs" 2) "fds"
-
srem key member [member …]
移除集合 key 中的一个或多个 member 元素,不存在的 member 元素会被忽略。
当 key 不是集合类型,返回一个错误。
127.0.0.1:6379> smembers s_a 1) "123" 2) "234" 3) "345" 4) "987" 127.0.0.1:6379> srem s_a 234 (integer) 1 127.0.0.1:6379> smembers s_a 1) "123" 2) "345" 3) "987" 127.0.0.1:6379> srem s_a 134 (integer) 0 127.0.0.1:6379> srem s_a 123 987 (integer) 2 127.0.0.1:6379> smembers s_a 1) "345"
-
spop key
移除并返回集合中的一个随机元素。
127.0.0.1:6379> sadd s_b abc bcd fd sfds fdfd (integer) 5 127.0.0.1:6379> smembers s_b 1) "sfds" 2) "bcd" 3) "abc" 4) "fd" 5) "fdfd" 127.0.0.1:6379> spop s_b "fd" 127.0.0.1:6379> smembers s_b 1) "sfds" 2) "abc" 3) "bcd" 4) "fdfd" 127.0.0.1:6379> spop s_b "sfds" 127.0.0.1:6379> smembers s_b 1) "abc" 2) "bcd" 3) "fdfd
-
srandmember key [count]
如果命令执行时,只提供了 key 参数,那么返回集合中的一个随机元素,不删除。
如果 count 为正数,且小于集合基数,那么命令返回一个包含 count 个元素的数组,数组中的元素各不相同。如果 count 大于等于集合基数,那么返回整个集合。
如果 count 为负数,那么命令返回一个数组,数组中的元素可能会重复出现多次,而数组的长度为 count 的绝对值。
127.0.0.1:6379> sadd s_c afd fds fd s fds dfs dfs fd sfds (integer) 6 127.0.0.1:6379> srandmember s_c "s" 127.0.0.1:6379> srandmember s_c "sfds" 127.0.0.1:6379> smembers s_c 1) "sfds" 2) "s" 3) "fd" 4) "dfs" 5) "afd" 6) "fds" 127.0.0.1:6379> srandmember s_c 3 1) "sfds" 2) "s" 3) "afd" 127.0.0.1:6379> srandmember s_c 3 1) "sfds" 2) "s" 3) "fd" 127.0.0.1:6379> srandmember s_c -3 1) "dfs" 2) "s" 3) "s" 127.0.0.1:6379> srandmember s_c -3 1) "fd" 2) "afd" 3) "s" 127.0.0.1:6379> srandmember s_c 10 1) "fd" 2) "sfds" 3) "s" 4) "dfs" 5) "afd" 6) "fds" 127.0.0.1:6379> srandmember s_c -10 1) "fd" 2) "dfs" 3) "s" 4) "s" 5) "fds" 6) "s" 7) "fds" 8) "sfds" 9) "fds" 10) "fd" 127.0.0.1:6379> smembers s_c 1) "sfds" 2) "s" 3) "fd" 4) "dfs" 5) "afd" 6) "fds"
-
smove source destination member
将 member 元素从 source 集合移动到 destination 集合。
127.0.0.1:6379> smembers s_a 1) "345" 127.0.0.1:6379> smembers s_b 1) "abc" 2) "bcd" 3) "fdfd" 127.0.0.1:6379> smove s_b s_a abc (integer) 1 127.0.0.1:6379> smembers s_a 1) "abc" 2) "345" 127.0.0.1:6379> smembers s_b 1) "bcd" 2) "fdfd"
-
scard key
返回集合 key 的基数(集合中元素的数量)。
127.0.0.1:6379> smembers s_c 1) "sfds" 2) "s" 3) "fd" 4) "dfs" 5) "afd" 6) "fds" 127.0.0.1:6379> scard s_c (integer) 6 127.0.0.1:6379> del s_c (integer) 1 127.0.0.1:6379> scard s_c (integer) 0
-
sismember key member
判断 member 元素是否集合 key 的成员
127.0.0.1:6379> smembers s_b 1) "bcd" 2) "fdfd" 127.0.0.1:6379> sismember s_b bcd (integer) 1 127.0.0.1:6379> sismember s_b bcf (integer) 0
-
sinter key [key …]
返回一个集合的全部成员,该集合是所有给定集合的交集。
不存在的 key 被视为空集。
当给定集合当中有一个空集时,结果也为空集(根据集合运算定律)。
127.0.0.1:6379> smembers s_a 1) "abc" 2) "345" 127.0.0.1:6379> smembers s_b 1) "bcd" 2) "fdfd" 127.0.0.1:6379> sinter s_a s_b (empty list or set) 127.0.0.1:6379> sadd s_a bcd (integer) 1 127.0.0.1:6379> sinter s_a s_b 1) "bcd"
-
sinterstore destination key [key …]
这个命令类似于 [SINTER key key …] 命令,但它将结果保存到 destination 集合,而不是简单地返回结果集。
如果 destination 集合已经存在,则将其覆盖。
destination 可以是 key 本身。
127.0.0.1:6379> smembers s_a 1) "bcd" 2) "abc" 3) "345" 127.0.0.1:6379> smembers s_b 1) "bcd" 2) "fdfd" 127.0.0.1:6379> sinterstore s_c s_a s_b (integer) 1 127.0.0.1:6379> smembers s_c 1) "bcd" 127.0.0.1:6379> sinterstore s_a s_a s_b (integer) 1 127.0.0.1:6379> smembers s_a 1) "bcd"
-
sunion key [key …]
返回一个集合的全部成员,该集合是所有给定集合的并集。
不存在的 key被视为空集。
127.0.0.1:6379> smembers s_a 1) "bcd" 2) "fd" 127.0.0.1:6379> smembers s_b 1) "bcd" 2) "fdfd" 127.0.0.1:6379> sunion s_a s_b 1) "bcd" 2) "fdfd" 3) "fd"
-
sunionstore destination key [key …]
这个命令类似于 [SUNION key key …] 命令,但它将结果保存到 destination 集合,而不是简单地返回结果集。
如果 destination 已经存在,则将其覆盖。
destination 可以是 key 本身。
127.0.0.1:6379> smembers s_a 1) "bcd" 2) "fd" 127.0.0.1:6379> smembers s_b 1) "bcd" 2) "fdfd" 127.0.0.1:6379> sunionstore s_c s_a s_b (integer) 3 127.0.0.1:6379> smembers s_c 1) "bcd" 2) "fdfd" 3) "fd" 127.0.0.1:6379> sunionstore s_a s_a s_b (integer) 3 127.0.0.1:6379> smembers s_a 1) "bcd" 2) "fdfd" 3) "fd"
-
sdiff key [key …]
返回一个集合的全部成员,该集合是所有给定集合之间的差集。
不存在的 key 被视为空集。
127.0.0.1:6379> smembers s_a 1) "bcd" 2) "fdfd" 3) "fd" 127.0.0.1:6379> smembers s_b 1) "bcd" 2) "fdfd" 127.0.0.1:6379> sdiff s_a s_b 1) "fd"
-
sdiffstore destination key [key …]
这个命令的作用和 [SDIFF key key …] 类似,但它将结果保存到 destination 集合,而不是简单地返回结果集。
如果 destination 集合已经存在,则将其覆盖。
destination 可以是 key 本身。
127.0.0.1:6379> smembers s_a 1) "bcd" 2) "fdfd" 3) "fd" 127.0.0.1:6379> smembers s_b 1) "bcd" 2) "fdfd" 127.0.0.1:6379> sdiffstore s_c s_a s_b (integer) 1 127.0.0.1:6379> smembers s_c 1) "fd" 127.0.0.1:6379> sdiffstore s_a s_a s_b (integer) 1 127.0.0.1:6379> smembers s_a 1) "fd"
4.5有序集合
有序集合保留了集合中不能有重复成员的特性,与集合不同的是有序集合中的元素是可以进行排序,他的排序方式是给每个元素设置一个分数,作为排序的依据。
有序集合中的元素不可以重复,但是score可以重复,就和一个人的身份证号不能重复一样,但姓名是可以重复的。
4.5.1使用场景:
- 排行榜:使用分数作为分数以存储每个用户的成绩,将用户的得分作为分数,在有序集合中进行排序。
- 带权重的消息队列:可以使用分数表示消息的优先级,高优先级的消息排在低优先级的消息之前,这样可以优先处理重要的消息。
- 社交网络的关注者列表:使用有序集合存储用户的关注者列表,每个关注者的时间戳作为分数,可以按照时间顺序查看关注者列表。
- 粉丝列表:可以使用有序集合存储某个用户的粉丝列表,将每个粉丝的关注时间戳作为分数,可以按照时间顺序查看粉丝列表。
- 范围查找:有序集合支持按分数范围(例如:从20到100分)来查找元素,可以用来实现范围查找的功能。
4.5.2命令
-
zadd key score member [[score member] …]
将一个或多个 member 元素及其 score 值加入到有序集 key 当中。
如果某个 member 已经是有序集的成员,那么更新这个 member 的 score 值,并通过重新插入这个 member 元素,来保证该 member 在正确的位置上。
score 值可以是整数值或双精度浮点数。
如果 key 不存在,则创建一个空的有序集并执行 ZADD 操作。
当 key 存在但不是有序集类型时,返回一个错误。
127.0.0.1:6379> zadd z_a 9 abc 10 def (integer) 2 127.0.0.1:6379> zadd z_a 8 fdfd (integer) 1 127.0.0.1:6379> zrange z_a 0 -1 1) "fdfd" 2) "abc" 3) "def" 127.0.0.1:6379> zrange z_a 0 -1 withscores 1) "fdfd" 2) "8" 3) "abc" 4) "9" 5) "def" 6) "10" 127.0.0.1:6379> zadd z_a 7 abc (integer) 0 127.0.0.1:6379> zrange z_a 0 -1 withscores 1) "abc" 2) "7" 3) "fdfd" 4) "8" 5) "def" 6) "10" 127.0.0.1:6379> zadd z_a 4 afdsc (integer) 1 127.0.0.1:6379> zrange z_a 0 -1 withscores 1) "afdsc" 2) "4" 3) "abc" 4) "7" 5) "fdfd" 6) "8" 7) "def" 8) "10" 127.0.0.1:6379> zadd z_a 9 afddsc (integer) 1 127.0.0.1:6379> zrange z_a 0 -1 withscores 1) "afdsc" 2) "4" 3) "abc" 4) "7" 5) "fdfd" 6) "8" 7) "afddsc" 8) "9" 9) "def" 10) "10"
-
zrange key start stop [withscores]
返回有序集 key 中,指定区间内的成员。
其中成员的位置按 score 值递增(从小到大)来排序。
具有相同 score 值的成员按字典序(lexicographical order )来排列。
如果你需要成员按 score 值递减(从大到小)来排列,请使用 [ZREVRANGE key start stop WITHSCORES] 命令。
下标参数 start 和 stop 都以 0 为底,也就是说,以 0 表示有序集第一个成员,以 1 表示有序集第二个成员,以此类推。 你也可以使用负数下标,以 -1 表示最后一个成员, -2 表示倒数第二个成员,以此类推。
超出范围的下标并不会引起错误。 比如说,当 start 的值比有序集的最大下标还要大,或是 start > stop 时, ZRANGE 命令只是简单地返回一个空列表。 另一方面,假如 stop 参数的值比有序集的最大下标还要大,那么 Redis 将 stop 当作最大下标来处理。
可以通过使用 WITHSCORES 选项,来让成员和它的 score 值一并返回,返回列表以 value1,score1, …, valueN,scoreN 的格式表示。 客户端库可能会返回一些更复杂的数据类型,比如数组、元组等。
127.0.0.1:6379> zrange z_a 0 -1 withscores 1) "afdsc" 2) "4" 3) "fdfd" 4) "8" 5) "afddsc" 6) "9" 127.0.0.1:6379> zrange z_a 1 2 withscores 1) "fdfd" 2) "8" 3) "afddsc" 4) "9" 127.0.0.1:6379> zrange z_a 0 200 withscores 1) "afdsc" 2) "4" 3) "fdfd" 4) "8" 5) "afddsc" 6) "9" 127.0.0.1:6379> zrange z_a 200 300 withscores (empty list or set)
-
zrevrange key start stop [withscores]
返回有序集 key 中,指定区间内的成员。
其中成员的位置按 score 值递减(从大到小)来排列。 具有相同 score 值的成员按字典序的逆序(reverse lexicographical order)排列。
127.0.0.1:6379> zrange z_a 0 -1 withscores 1) "aaa" 2) "2" 3) "afdsc" 4) "4" 5) "fdfd" 6) "4" 7) "afddsc" 8) "9" 127.0.0.1:6379> zrevrange z_a 0 -1 withscores 1) "afddsc" 2) "9" 3) "fdfd" 4) "4" 5) "afdsc" 6) "4" 7) "aaa" 8) "2"
-
zscore key member
返回有序集 key 中,成员 member 的 score 值。
如果 member 元素不是有序集 key 的成员,或 key 不存在,返回 nil 。
127.0.0.1:6379> zrange z_a 0 -1 withscores 1) "afdsc" 2) "4" 3) "fdfd" 4) "8" 5) "afddsc" 6) "9" 127.0.0.1:6379> zscore z_a fdfd "8"
-
zrem key member [member …]
移除有序集 key 中的一个或多个成员,不存在的成员将被忽略。
当 key 存在但不是有序集类型时,返回一个错误。
127.0.0.1:6379> zrange z_a 0 -1 withscores 1) "afdsc" 2) "4" 3) "abc" 4) "7" 5) "fdfd" 6) "8" 7) "afddsc" 8) "9" 9) "def" 10) "10" 127.0.0.1:6379> zrem z_a 4 (integer) 0 127.0.0.1:6379> zrem z_a abc def (integer) 2 127.0.0.1:6379> zrange z_a 0 -1 withscores 1) "afdsc" 2) "4" 3) "fdfd" 4) "8" 5) "afddsc" 6) "9"
-
zincrby key increment member
为有序集 key 的成员 member 的 score 值加上增量 increment 。
可以通过传递一个负数值 increment ,让 score 减去相应的值,比如 ZINCRBY key -5 member ,就是让 member 的 score 值减去 5 。
当 key 不存在,或 member 不是 key 的成员时, ZINCRBY key increment member 等同于 ZADD key increment member 。
当 key 不是有序集类型时,返回一个错误。
score 值可以是整数值或双精度浮点数。
127.0.0.1:6379> zrange z_a 0 -1 withscores 1) "afdsc" 2) "4" 3) "fdfd" 4) "8" 5) "afddsc" 6) "9" 127.0.0.1:6379> zscore afdsc (error) ERR wrong number of arguments for 'zscore' command 127.0.0.1:6379> zscore z_a fdfd "8" 127.0.0.1:6379> zincrby z_a 2 fdfd "10" 127.0.0.1:6379> zrange z_a 0 -1 withscores 1) "afdsc" 2) "4" 3) "afddsc" 4) "9" 5) "fdfd" 6) "10" 127.0.0.1:6379> zincrby z_a -6 fdfd "4" 127.0.0.1:6379> zrange z_a 0 -1 withscores 1) "afdsc" 2) "4" 3) "fdfd" 4) "4" 5) "afddsc" 6) "9"
-
zrank key member
返回有序集 key 中成员 member 的排名。其中有序集成员按 score 值递增(从小到大)顺序排列。
排名以 0 为底,也就是说, score 值最小的成员排名为 0 。
127.0.0.1:6379> zrange z_a 0 -1 withscores 1) "afdsc" 2) "4" 3) "fdfd" 4) "4" 5) "afddsc" 6) "9" 127.0.0.1:6379> zrank z_a fdfd (integer) 1 127.0.0.1:6379> zrank z_a afdsc (integer) 0 127.0.0.1:6379> zrank z_a afddsc (integer) 2 127.0.0.1:6379> zadd z_a 2 aaa (integer) 1 127.0.0.1:6379> zrange z_a 0 -1 withscores 1) "aaa" 2) "2" 3) "afdsc" 4) "4" 5) "fdfd" 6) "4" 7) "afddsc" 8) "9" 127.0.0.1:6379> zrank z_a fdfd (integer) 2
-
zrevrank key member
返回有序集 key 中成员 member 的排名。其中有序集成员按 score 值递减(从大到小)排序。
排名以 0 为底,也就是说, score 值最大的成员排名为 0 。
127.0.0.1:6379> zrange z_a 0 -1 withscores 1) "aaa" 2) "2" 3) "afdsc" 4) "4" 5) "fdfd" 6) "4" 7) "afddsc" 8) "9" 127.0.0.1:6379> zrevrank z_a fdfd (integer) 1 127.0.0.1:6379> zrevrank z_a afddsc (integer) 0 127.0.0.1:6379> zrevrank z_a aaa (integer) 3
-
zrangebyscore key min max [withscores] [limit offset count]
返回有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。有序集成员按 score 值递增(从小到大)次序排列。
具有相同 score 值的成员按字典序(lexicographical order)来排列(该属性是有序集提供的,不需要额外的计算)。
可选的 LIMIT 参数指定返回结果的数量及区间(就像SQL中的 SELECT LIMIT offset, count ),注意当 offset 很大时,定位 offset 的操作可能需要遍历整个有序集,此过程最坏复杂度为 O(N) 时间。
可选的 WITHSCORES 参数决定结果集是单单返回有序集的成员,还是将有序集成员及其 score 值一起返回。
127.0.0.1:6379> zadd salary 5000 zhangsan 2500 lisi 12000 wangwu (integer) 3 127.0.0.1:6379> zrangebyscore salary -inf +inf 1) "lisi" 2) "zhangsan" 3) "wangwu" 127.0.0.1:6379> zrangebyscore salary -inf +inf withscores 1) "lisi" 2) "2500" 3) "zhangsan" 4) "5000" 5) "wangwu" 6) "12000" 127.0.0.1:6379> zrangebyscore salary -inf +inf withscores limit 0 2 1) "lisi" 2) "2500" 3) "zhangsan" 4) "5000" 127.0.0.1:6379> zrangebyscore salary -inf +inf withscores limit 2 2 1) "wangwu" 2) "12000" 127.0.0.1:6379> zrangebyscore salary -inf +inf withscores limit 1 2 1) "zhangsan" 2) "5000" 3) "wangwu" 4) "12000" 127.0.0.1:6379> zrangebyscore salary 2500 6000 1) "lisi" 2) "zhangsan" 127.0.0.1:6379> zrangebyscore salary (2500 6000 1) "zhangsan" 127.0.0.1:6379> zrangebyscore salary (2500 (5000 (empty list or set) 127.0.0.1:6379> zrangebyscore salary (2500 5000 1) "zhangsan"
-
zcount key min max
返回有序集 key 中, score 值在 min 和 max 之间(默认包括 score 值等于 min 或 max )的成员的数量。
127.0.0.1:6379> zadd salary 5000 zhangsan 2500 lisi 12000 wangwu (integer) 3 127.0.0.1:6379> zcount salary -inf +inf (integer) 3 127.0.0.1:6379> zcount salary 2500 5000 (integer) 2 127.0.0.1:6379> zcount salary 2500 (5000 (integer) 1
-
zcard key
返回有序集 key的基数。
127.0.0.1:6379> zcard z_a (integer) 4 127.0.0.1:6379> zcard salary (integer) 3
5、python操作redis
安装
单机和哨兵,使用的是redis模块,所以下载redis即可,使用pip或者pip3命令来安装。
pip3 install redis
集群需要用到rediscluster模块,采用以下命令安装redis-py-cluster即可,最好指定版本
pip3 install redis-py-cluster=1.3.0
连接
reids单机连接:
import redis
conn = redis.StrictRedis(connection_pool=redis.ConnectionPool(
host="127.0.0.1", port="6379", password=""
))
redis哨兵连接:
from redis.sentinel import Sentinel
sentinel_list = [
("地址1", "6379"),
("地址2", "6379"),
("地址3", "6379")
]
mySentinel = Sentinel(sentinel_list)
master = mySentinel.master_for("mymaster", db=0)
slave = mySentinel.slave_for("mymaster", db=0)
# 使用master进行写的操作,使用slave进行读的操作
master.hset("key", "filed", "value")
slave.hget("key", "filed")
redis集群连接:
from rediscluster import StrictRedisCluster
cluster_nodes = [{"host": "ip1", "port": "6001"},
{"host": "ip2", "port": "6002"},
{"host": "ip3", "port": "6003"},
{"host": "ip4", "port": "6004"},
{"host": "ip5", "port": "6005"},
{"host": "ip6", "port": "6006"}]
password = "123456"
conn = StrictRedisCluster(
startup_nodes=cluster_nodes,
decode_response=True,
password=password,
max_connection=300
)
startup_nodes:Redis 集群节点列表,可以使用集群中的任何节点信息,建议至少包含 3 个节点的信息,以保证集群的高可用性。
decode_response:设置是否对 Redis 返回结果进行解码,默认为 True,如果设置为 True,则对 Redis 的返回结果进行解码,否则直接返回原始的字节数据。
password:Redis 集群的认证密码,在进行集群连接时需要进行认证才能获取访问权限。
max_connection:Redis 集群最大的连接数,默认为 300,该参数决定了连接池的大小,建议根据实际业务情况进行调整。
字符串操作
conn.set(key, value) #增加
conn.get(key) #查询
conn.delete(key) #删除
集合操作
conn.sadd(key, value1, value2...) #增加
conn.smembers(key) #查询
conn.delete(key) #删除
列表操作
conn.lpush(key, value1, value2,...) #增加
conn.lrange(key, start, end) # 比如列出所有conn.lrange("key_name", 0, -1) #查询
conn.delete(key) #删除
哈希操作(字典)
conn.hset(key, field, value) #增加
conn.hget(key, filed) #查询
conn.hgetall(key) #查询
conn.delete(key) #删除
有序集合操作
# 添加单个元素
conn.zadd('myset', {'member1': 1})
# 添加多个元素
conn.zadd('myset', {'member2': 2, 'member3': 3})
count = conn.zcard('myset')
print(count) # 输出有序集合中的成员数量
members = conn.zrange('myset', 0, -1)
print(members) # 输出有序集合中的所有成员
score = conn.zscore('myset', 'member1')
print(score) # 输出'member1'的分数
conn.zrem('myset', 'member1')
6、Redis发布订阅模式
Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。Redis 客户端可以订阅任意数量的频道。
6.1、应用场景
在Redis中,你可以设定对某一个key值进行消息发布及消息订阅,当一个key值上进行了消息发布后,所有订阅它的客户端都会收到相应的消息。这一功能最明显的用法就是用作实时消息系统,比如普通的即时聊天,群聊等功能。
- 今日头条订阅号、微信订阅公众号、新浪微博关注、邮件订阅系统
- 即时通信系统(QQ、微信)
- 群聊部落系统(微信群)
在这个模式中,客户端可以订阅一个或多个频道,并接收发布在这些频道上的消息。一个客户端也可以向一个或多个频道发布消息。当有新的消息被发布在频道上时,所有已订阅该频道的客户端都将收到这条消息。
以下是发布订阅模式的具体细节:
订阅频道 使用SUBSCRIBE命令,用于订阅一个或多个频道:
SUBSCRIBE channel1 channel2 channel3
当客户端成功订阅频道后,它会进入阻塞状态,等待频道中有消息发布。
发布消息 使用PUBLISH命令,向一个或多个频道发布消息:
PUBLISH channel message
当有新的消息发布到频道上时,所有已订阅该频道的客户端都会收到这条消息。
取消订阅 使用UNSUBSCRIBE命令,用于取消对一个或多个频道的订阅:
UNSUBSCRIBE channel1 channel2 channel3
如果取消订阅后,客户端没有订阅任何频道,它会停止接收消息并退出阻塞状态。
下面是一个简单的示例,假设有两个客户端A和B,A订阅频道c1和c2,B订阅频道c2和c3。A发布消息到频道c1,B发布消息到频道c3,最终A和B都能收到对应消息。
# 客户端A
127.0.0.1:6379> SUBSCRIBE c1 c2
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "c1"
3) (integer) 1
1) "subscribe"
2) "c2"
3) (integer) 2
# 客户端B
127.0.0.1:6379> SUBSCRIBE c2 c3
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "c2"
3) (integer) 1
1) "subscribe"
2) "c3"
3) (integer) 2
# 客户端A发布到频道c1
127.0.0.1:6379> PUBLISH c1 "hello world"
(integer) 1
# 客户端B发布到频道c3
127.0.0.1:6379> PUBLISH c3 "hi there"
(integer) 1
# A收到频道c1上的消息
1) "message"
2) "c1"
3) "hello world"
# B收到频道c3上的消息
1) "message"
2) "c3"
3) "hi there"
复制代码
这样就实现了Redis的发布订阅模式。
7、Redis事物
Redis提供了类似于SQL数据库的事务,可以保证多个命令的原子性执行,也可以保证在同一客户端中执行的多个命令都能成功执行,或者都不会执行。在Redis事务中,命令的执行并不是即时执行而是先进队列,只有当提交时,Redis才会一次性把所有命令执行。
Redis事务允许一组命令在单一步骤中执行。事务有两个属性,说明如下:
- 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
- Redis事务是原子的。原子意味着要么所有的命令都执行,要么都不执行;
一个事务从开始到执行会经历以下三个阶段:
- 开始事务
- 命令入队
- 执行事务
7.1以下是Redis事务的具体细节:
开启事务
使用MULTI命令,用于开始一个事务:
MULTI
在事务开始之后,客户端可以执行多个命令,并将它们添加到队列中,但这些命令并没有真正地被执行。
将命令加入队列
执行任意Redis命令,该命令会被添加到当前客户端队列中,例如:
set key1 value1
get key2
incr key3
上面的三个命令都会被加入到队列中等待执行。
执行事务
使用EXEC命令,用于提交事务:
EXEC
一旦提交事务,Redis会按照客户端队列中的命令顺序执行所有的命令,如果队列中任意一条命令执行失败,整个事务就会被回滚。
取消事务
使用DISCARD命令,用于取消一个事务:
DISCARD
当客户端使用DISCARD命令时,所有已经在队列中的命令都会被清空,并且事务会被取消。
比如这个转账的例子
127.0.0.1:6379> set xiaolong 60000
OK
127.0.0.1:6379> set gouda 200
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> INCRBY xiaolong -10000
QUEUED
127.0.0.1:6379> INCRBY gouda 10000
QUEUED
127.0.0.1:6379> EXEC
1) (integer) 50000
2) (integer) 10200
8、Redis配置文件
在启动Redis服务器时,我们需要为其指定一个配置文件,缺省情况下配置文件在Redis的源码目录下,文件名为redis.conf。
redis配置文件使用#######################
被分成了几大块区域,
主要有:
- 通用(general)
- 快照(snapshotting)
- 复制(replication)
- 安全(security)
- 限制(limits)
- 追加模式(append only mode)
- LUA脚本(lua scripting)
- REDIS集群(REDIS CLUSTER)
- 慢日志(slow log)
- 事件通知(event notification)
- ADVANCED CONFIG
为了对Redis的系统实现有一个直接的认识,我们首先来看一下Redis的配置文件中定义了哪些主要参数以及这些参数的作用。
- daemonize no
默认情况下,redis不是在后台运行的。如果需要在后台运行,把该项的值更改为yes;
- pidfile /var/run/redis_6379.pid
当Redis在后台运行的时候,Redis默认会把pid文件放在/var/run/redis.pid, 你可以配置到其他地址。当运行多个redis服务时,需要指定不同的pid文件和端口;
- port 6379
指定redis运行的端口,默认是6379;
- bind 127.0.0.1
指定redis只接收来自于该IP地址的请求,如果不进行设置,那么将处理所有请求。在生产环境中最好设置该项;
- loglevel notice
指定日志记录级别,
其中Redis总共支持四个级别: debug 、verbose 、notice 、warning, 默认为 notice 。
- debug表示记录很多信息,用于开发和测试。
- verbose表示记录有用的信息, 但不像debug会记录那么多。
- notice表示普通的verbose,常用于生产环境。
- warning 表示只有非常重要或者严重的信息会记录到日志;
- logfile ""
配置log文件地址,默认值为stdout。若后台模式会输出到/dev/null("黑洞");(可以自定义配置:/var/log/redis/redis.log)
- databases 16
可用数据库数,默认值为16,默认数据库为0,数据库范围在0 15之间切换,彼此隔离;
- save
保存数据到磁盘,格式为save <seconds> <changes>
,指出在多长时间内,有多少次更新操作, 就将数据同步到数据文件rdb。相当于条件触发抓取快照,这个可以多个条件配合。
保存数据到磁盘:
- save 900 1 #900秒(15分钟)内至少有1个key被改变
- save 300 10 #300秒(5分钟)内至少有10个key被改变
- save 60 10000 #60秒内至少有10000个key被改变
- rdbcompression yes
存储至本地数据库时(持久化到rdb文件)是否压缩数据,默认为yes;
如果为了节省CPU时间,可以关闭该选项,但会导致数据库文件变的巨大
- dbfilename dump.rdb
本地持久化数据库文件名,默认值为dump.rdb;
- dir ./
工作目录,数据库镜像备份的文件放置的路径。
- slaveof
主从复制,设置该数据库为其他数据库的从数据库。设置当本机为slave服务时,设置master服务的IP地址及端口。 在Redis启动时,它会自动从master进行数据同步;
- masterauth
当master服务设置了密码保护时(用requirepass制定的密码)slave服务连接master的密码;
- slave-serve-stale-data yes
当一个slave失去和master的连接,或者同步正在进行中,slave的行为有两种可能:
-
如果 slave-serve-stale-data 设置为 "yes" (默认值),slave会继续响应客户端请求,可能是正常数据,也可能是还没获得值的空数据。
-
如果 slave-serve-stale-data 设置为 "no",slave会回复"正在从master同步(SYNC with master in progress)"来处理各种请求,除了 INFO 和 SLAVEOF 命令。
- repl-ping-slave-period 10
从库会按照一个时间间隔向主库发送PING,可以通过repl-ping-slave-period设置这个时间间隔,默认是10秒;
- repl-timeout 60
设置主库批量数据传输时间或者ping回复时间间隔,默认值是60秒,一定要确保repl-timeout大于repl-ping-slave-period ;不然会经常检测到超时。
master检测到slave上次发送的时间超过repl-timeout,即认为slave离线,清除该slave信息。
slave检测到上次和master交互的时间超过repl-timeout,则认为master离线
- requirepass foobared
设置客户端连接后进行任何其他指定前需要使用的密码。因为redis速度相当快,所以在一台比较好的服务器下, 一个外部的用户可以在一秒钟进行150K次的密码尝试,这意味着你需要指定非常强大的密码来防止暴力破解;
- renamecommand CONFIG ""
命令重命名,在一个共享环境下可以重命名相对危险的命令,比如把CONFIG重名为一个不容易猜测的字符: rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52。 如果想删除一个命令,直接把它重命名为一个空字符""即可:rename-command CONFIG "";
- maxclients 128
设置最多同时连接客户端数量。
默认没有限制,这个关系到Redis进程能够打开的文件描述符数量。
特殊值"0"表示没有限制。
一旦达到这个限制,Redis会关闭所有新连接并发送错误"达到最大用户数上限(max number of clients reached)"
- maxmemory
指定Redis最大内存限制。Redis在启动时会把数据加载到内存中,达到最大内存后,Redis会先尝试清除已到期或即将到期的Key, Redis同时也会移除空的list对象。当此方法处理后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作。
注意:Redis新的vm机制,会把Key存放内存,Value会存放在swap区;
- maxmemory-policy volatile-lru
当内存达到最大值的时候Redis会选择删除哪些数据呢?有五种方式可供选择: - volatile-lru 代表利用LRU算法移除设置过期时间的key(LRU:最近使用 LeastRecentlyUsed)
- allkeys-lru 代表利用LRU算法移除任何key
- volatile-random 代表移除设置过过期时间的随机key
- allkeys_random 代表移除一个随机的key,
- volatile-ttl 代表移除即将过期的key(minor TTL)
- noeviction 代表不移除任何key,只是返回一个写错误。
注意:对于上面的策略,如果没有合适的key可以移除,写的时候Redis会返回一个错误;
- appendonly no
是否开启aof功能
默认情况下,redis会在后台异步的把数据库镜像备份到磁盘,但是该备份是非常耗时的,而且备份也不能很频繁。 如果发生诸如拉闸限电、拔插头等状况,那么将造成比较大范围的数据丢失,所以redis提供了另外一种更加高效的数据库备份及灾难恢复方式。
开启appendonly模式之后,redis会把所接收到的每一次写操作请求都追加到appendonly.aof文件中。
当redis重新启动时,会从该文件恢复出之前的状态
- appendfilename appendonly.aof
AOF文件名称,默认为"appendonly.aof";
- appendfsync everysec
Redis支持三种同步AOF文件的策略: - no 代表不进行同步,系统去操作,
- always 代表每次有写操作都进行同步,
- everysec 代表对写操作进行累积,每秒同步一次
默认是"everysec",按照速度和安全折中这是最好的。
- slowlog-log-slower-than 10000
记录超过特定执行时间的命令。执行时间不包括I/O计算,比如连接客户端,返回结果等,只是命令执行时间。
可以通过两个参数设置slow log:一个是告诉Redis执行超过多少时间被记录的参数slowlog-log-slower-than(微妙), 另一个是slow log 的长度。当一个新命令被记录的时候最早的命令将被从队列中移除,
下面的时间以微秒(百万分之一秒,1000 * 1000)单位, 因此1000000代表一分钟。
1秒=1000毫秒
1秒=1000000微秒
注意制定一个负数将关闭慢日志,而设置为0将强制每个命令都会记录;
- slowlog-max-len 128
慢操作日志"保留的最大条数
"记录"将会被队列化,如果超过了此长度,旧记录将会被移除。可以通过SLOWLOG <subcommand> args
查看慢记录的信息(SLOWLOG get 10,SLOWLOG reset),通过"SLOWLOG get num"指令可以查看最近num条慢速记录,其中包括"记录"操作的时间/指令/K-V等信息。
Select 命令
Redis Select 命令用于切换到指定的数据库,数据库索引号 index 用数字值指定,以 0 作为起始索引值。
python@ubuntu:/dev$ redis-cli
127.0.0.1:6379> select 1
OK
127.0.0.1:6379[1]> set name 'itcast.cn'
OK
127.0.0.1:6379[1]> select 2
OK
127.0.0.1:6379[2]> set web 'itcast.com'
OK
127.0.0.1:6379[2]> get web
"itcast.com"
127.0.0.1:6379[2]> select 1
OK
127.0.0.1:6379[1]> get name
"itcast.cn"
127.0.0.1:6379[1]>
Shutdown 命令
Redis Shutdown 命令执行以下操作:
-
停止所有客户端
-
如果有至少一个保存点在等待,执行 SAVE 命令
-
如果 AOF 选项被打开,更新 AOF 文件
-
关闭 redis 服务器(server)
redis 127.0.0.1:6379> PING PONG redis 127.0.0.1:6379> SHUTDOWN $ redis
Redis 认证
redis-cli
AUTH "password"
或者
redis-cli -h 127.0.0.1 -p 6379 -a myPassword
Redis Showlog
Redis Showlog 是 Redis 用来记录查询执行时间的日志系统。
查询执行时间指的是不包括像客户端响应(talking)、发送回复等 IO 操作,而单单是执行一个查询命令所耗费的时间。
另外,slow log 保存在内存里面,读写速度非常快,因此你可以放心地使用它,不必担心因为开启 slow log 而损害 Redis 的速度。
语法redis Showlog 命令基本语法如下:
redis 127.0.0.1:6379> SLOWLOG subcommand [argument]
查看日志信息:
redis 127.0.0.1:6379> slowlog get 2
1) 1) (integer) 14
2) (integer) 1309448221
3) (integer) 15
4) 1) "ping"
2) 1) (integer) 13
2) (integer) 1309448128
3) (integer) 30
4) 1) "slowlog"
2) "get"
3) "100"
每一个Showlog都是由四个字段组成的慢日志标识符记录的命令进行处理的Unix时间戳执行所需的时间,以微秒命令、参数。
查看当前日志的数量:
redis 127.0.0.1:6379> SLOWLOG LEN
(integer) 14
使用命令 SLOWLOG RESET 可以清空 slow log 。
redis 127.0.0.1:6379> SLOWLOG LEN
(integer) 14
redis 127.0.0.1:6379> SLOWLOG RESET
OK
redis 127.0.0.1:6379> SLOWLOG LEN
(integer) 0
9、Reids持久化
Redis支持两种数据持久化方式:RDB方式和AOF方式。前者会根据配置的规则定时将内存中的数据持久化到硬盘上,后者则是在每次执行写命令之后将命令记录下来。两种持久化方式可以单独使用,但是通常会将两者结合使用。
9.1、持久化介绍
持久化功能有效地避免因进程退出造成的数据丢失问题,当下次重启时利用之前持久化的文件即可实现数据恢复。
9.2、持久化方式
Redis支持RDB和AOF两种持久化机制:
RDB(快照方式): RDB就是Snapshot快照存储,是默认的持久化方式。可理解为半持久化模式,
即按照一定的策略周期性的将数据保存到磁盘。对应产生的数据文件为dump.rdb,快照的周期通过配置文件中的save参数来定义 ,默认的文件名为dump.rdb。
Redis的RDB文件不会坏掉,因为其写操作是在一个新进程中进行的。当生成一个新的RDB文件时,Redis生成的子进程会先将数据写到一个临时文件中,然后通过原子性rename系统调用将临时文件重命名为RDB文件。
这样在任何时候出现故障,Redis的RDB文件都总是可用的。
同时,Redis的RDB文件也是Redis主从同步内部实现中的一环。
第一次Slave向Master同步的实现是:Slave向Master发出同步请求,Master先dump出rdb文件,然后将rdb文件全量传输给slave,然后Master把缓存的命令转发给Slave,初次同步完成。第二次以及以后的同步实现是:Master将变量的快照直接实时依次发送给各个Slave。但不管什么原因导致Slave和Master断开重连都会重复以上两个步骤的过程。
Redis的主从复制是建立在内存快照的持久化基础上的,只要有Slave就一定会有内存快照发生。可以很明显的看到,RDB有它的不足,就是一旦数据库出现问题,那么我们的RDB文件中保存的数据并不是全新的。
从上次RDB文件生成到Redis停机这段时间的数据全部丢掉了。
AOF(日志追加): AOF方式是将执行过的写指令记录下来,在数据恢复时按照从前到后的顺序再将指令执行一遍。这种方式 redis 会将每一个收到的写命令都通过 write 函数追加到文件中(默认appendonly.aof)。
AOF(Append-Only File)比RDB方式有更好的持久化性
AOF(Append-Only File)比RDB方式有更好的持久化性。
- 在使用AOF持久化方式时,Redis会将每一个收到的写命令都通过Write函数追加到文件中,类似于MySQL的binlog。
- 当Redis重启是会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。
在Redis重启时会逐个执行AOF文件中的命令来将硬盘中的数据载入到内存中,所以说,载入的速度相较RDB会慢一些
- 默认情况下,Redis没有开启AOF方式的持久化,可以在redis.conf中通过appendonly参数开启:
AOF的完全持久化方式同时也带来了另一个问题,持久化文件会变得越来越大。
比如: 我们调用INCR test 命令100次,文件中就必须保存全部的100条命令,但其实99条都是多余的。
因为要恢复数据库的状态其实文件中保存一条SET test 100就够了。
为了压缩AOF的持久化文件,Redis提供了bgrewriteaof命令。收到此命令后Redis将使用与快照类似的方式将内存中的数据以命令的方式保存到临时文件中,最后替换原来的文件,以此来实现控制AOF文件的增长。
配置redis自动重写AOF文件的参数
no-appendfsync-on-rewrite yes #在AOF重写时,不进行命令追加操作,而只是将其放在缓冲区里,避免与命令的追加造成DISK IO
上的冲突。
auto-aof-rewrite-percentage 100 #当前AOF文件大小是上次日志重写得到AOF文件大小的二倍时,自动启动新的日志重写过程。
auto-aof-rewrite-min-size 64mb #当前AOF文件启动新的日志重写过程的最小值,避免刚刚启动Reids时由于文件尺寸较小导致频繁的重写。
9.1、RDB和AOF的优缺点
RDB优点:RDB是一个紧凑的单一文件,方便传送,适用于灾难恢复,与AOF相比在恢复大的数据集的时候,RDB方式会更快一些
RDB缺点:reids意外宕机,可能会丢失几分钟的数据(取决于配置的save时间点)RDB方式需要保存珍整个数据集,是一个比较繁重的工作,通常需要设置5分钟或者更久做一次完整的保存。
针对RDB不适合实时持久化的问题,Redis提供了AOF持久化方式来解决。
AOF的优点:AOF只是追加日志文件,因此对服务器性能影响较小,速度比RDB要快,消耗的内存较少。
AOF的缺点:AOF方式生成的日志文件太大,即使通过AOF重写,文件体积仍然很大。
恢复数据的速度比RDB慢。
9.2、RDB
触发RDB持久化的过程分为手动触发和自动触发
9.2.1、手动触发
手动触发持久化的操作有二个:save和bgsave。它们主要区别体现在:是否阻塞 Redis 主线程的执行。
- save:在客户端中执行 save 命令,就会触发 Redis 的持久化,但同时也是使 Redis 处于阻塞状态,直到 RDB 持久化完成,才会响应其他客户端发来的命令,所以在生产环境一定要慎用。
执行过程:
查看/usr/local/redis/dump.rdb文件保存时间
[root@zutuanxue redis]# ls dump.rdb -l
-rw-r--r--. 1 root root 92 2月 28 03:37 dump.rdb
[root@zutuanxue redis]# ./src/redis-cli
127.0.0.1:6379> save
OK
127.0.0.1:6379> exit
[root@zutuanxue redis]# ls dump.rdb -l
-rw-r--r--. 1 root root 92 2月 28 11:08 dump.rdb
当执行完 save 命令之后,持久化文件 dump.rdb 的修改时间就变了,这就表示 save 成功的触发了 RDB 持久化
-
bgsave命令:bgsave(background save)既后台保存的意思, 它和 save 命令最大的区别就是 bgsave 会 fork() 一个子进程来执行持久化,整个过程中只有在 fork() 子进程时有短暂的阻塞,当子进程被创建之后,Redis 的主进程就可以响应其他客户端的请求了,相对于整个流程都阻塞的 save 命令来说,显然 bgsave 命令更适合我们使用。
执行过程:
127.0.0.1:6379> bgsave
Background saving started
127.0.0.1:6379> exit
[root@zutuanxue redis]# ls dump.rdb -l
-rw-r--r--. 1 root root 92 2月 28 11:29 dump.rdb
9.2.2、自动触发
自动触发持久化,本质是 Redis 通过判断,如果满足设置的触发条件,自动执行一次 bgsave 命令。
RDB 自动持久化主要来源于以下几种情况:
save a b 在秒内,如果有b个键发生改变,自动触发持久化
配置文件(/usr/local/redis/redis.conf) 默认配置为:
save 900 1 当 900s 内如果有 1次 Redis 键值发生改变,就会触发持久化;
save 300 10 当 300s 内如果有 10次 Redis 键值发生改变,就会触发持久化;
save 60 10000 当 60s 内如果有 10000次 Redis 键值发生改变,就会触发持久化;
设置多个save命令是,满足一个条件就都会触发持久化
-
flushall
清空 Redis 数据库,在生产环境下不可以使用(尽量),当 Redis 执行了 flushall 命令之后,则会触发自动持久化,把 RDB 文件清空。
9.2.3、RDB持久化配置
配置文件:
vim /usr/local/redis/redis.conf
#RDB持久化自动触发条件
save 900 1
save 300 10
save 60 10000
#bgsave持久化失败,是否停止持久化数据到磁盘,yes 表示停止持久化,no 表示忽略错误继续写文件
stop-writes-on-bgsave-error yes
#rdb文件是否压缩
rdbcompression yes
#写入文件和读取文件时是否开启rdb文件检查,检查是否有无损坏,如果在启动是检查发现损坏,则停止启动。
rdbchecksum yes
#rdb持久化后存放的文件名
dbfilename dump.rdb
#rdb持久化后文件的存放路径
dir ./
文件压缩要是开启的话:Redis 会采用 LZF 算法进行压缩。如果不想消耗 CPU 性能来进行文件压缩的话,可以设置为关闭此功能,这样的缺点是需要更多的磁盘空间来保存文件。
配置设置
config set xx
[root@zutuanxue redis]# mkdir data
[root@zutuanxue redis]# ./src/redis-cli
127.0.0.1:6379> config set dir "/usr/local/redis/data"
OK
127.0.0.1:6379> config get dir
1) "dir"
2) "/usr/local/redis/data"
配置查询
config get xxx
127.0.0.1:6379> config get dir
1) "dir"
2) "/usr/local/redis"
127.0.0.1:6379> config get dbfilename
1) "dbfilename"
2) "dump.rdb"
127.0.0.1:6379> config get stop-writes-on-bgsave-error
1) "stop-writes-on-bgsave-error"
2) "yes"
使用命令修改的方式,马上生效,在 Redis 重启之后就会丢失。手动修改 Redis 配置文件,想要立即生效需要重启 Redis 服务器,会一直有效。
-
禁用持久化
127.0.0.1:6379> config set save "" OK
RDB文件恢复
当 Redis 服务器启动时,Redis 就会自动加载 RDB 文件恢复持久化数据。
9.3、AOF持久化
AOF方式在使用Redis存储非临时数据时,一般都需要打开AOF持久化来降低进程终止导致的数据丢失,AOF可以将Redis执行的每一条写命令追加到硬盘文件中,这一过程显然会降低Redis的性能,但是大部分情况下这个影响是可以接受的,另外,使用较快的硬盘能提高AOF的性能。
命令写入(append)、文件同步(sync)、文件重写(rewrite)、重启加载(load)是AOF(Append-Only File)持久化机制的工作流程。在AOF持久化模式下,Redis会将每个接收到的写命令追加到AOF文件的末尾,然后通过文件同步操作将数据从内存刷写到磁盘。当AOF文件变得过大时,Redis会触发AOF文件重写操作,将其中的写命令以更紧凑的方式重新写入新的AOF文件中。而在Redis重启时,它会加载AOF文件中的命令来恢复数据。
9.3.1、AOF特点
默认文件名是 appendonly.aof。保存的位置由配置中 dir 来配置目录。
AOF 每次都会保存写命令,数据实时性更高。
AOF 需要使用“重写机制”来优化,每次记录写命令,文件会很大的问题。
AOF 根据不同的“缓冲区同步策略”将我们缓冲区中写入的命令,同步到磁盘。
缓冲区同步策略
设置appendfsync 控制,一共3种:
always:客户端的每一个写操作都保存到aof文件当,这种策略很安全,但是每个写都会有IO操作,所以也很慢。
everysec:每秒写入一次aof文件,因此,最多可能会丢失1s的数据。 推荐使用这种方式。
no: 交由操作系统来处理什么时候写入aof文件。更快,但也是最不安全的选择,不推荐使用。
AOF持久化恢复
AOF
持久化恢复的执行流程如下:
Redis
服务启动时会检查是否开启了AOF
持久化功能;- 如果开启了
AOF
持久化功能,Redis
会在启动时加载AOF
文件; - 接下来
Redis
开始重放AOF
文件中的操作,将其中记录的每一条指令逐条执行,将数据加载到内存数据库中。在重放过程中,Redis
会检查每个指令的正确性,确保重放后数据和AOF
文件记录的数据一致; - 重放完成后,
Redis
完成AOF
持久化恢复过程。
需要注意的是,在重放过程中,Redis
会在内存数据库中执行操作,因此可能会影响到线上业务。如果 AOF
文件非常大,重放的时间可能会比较长,甚至会阻塞线上业务,在这种情况下,可以选择使用异步重放命令或者定时任务来进行持久化恢复。
9.3.2在重启redis服务时,RDB与AOF如何执行
在Redis服务启动时,如果同时开启了RDB和AOF两个持久化策略,那么Redis会根据以下规则执行:
-
如果AOF文件存在,Redis会优先加载AOF文件,并将其中的操作重放到内存数据库中,以恢复脏数据。
-
如果AOF文件不存在,但RDB文件存在,Redis会加载RDB文件,并将其中的键值对加载到内存数据库中。
-
如果既没有AOF文件,也没有RDB文件,则Redis会以空数据库开始运行。
需要注意的是,如果在使用AOF持久化时,Redis在上一次正常停机之前未能完全将数据写入AOF文件,那么可能会导致数据的丢失。在这种情况下,可以考虑使用RDB持久化来补救,因为RDB文件中保存有最近一次成功持久化数据的快照。不过这也是需要谨慎处理的操作,因为RDB文件中的数据可能并不是最新的,如果在最后一次持久化之后发生了数据变更,则这部分数据将无法恢复。因此,建议在设置持久化策略时,根据业务情况进行选择,以确保数据的完整性和安全性。
9.3.3、开启AOF持久化
修改配置文件
/usr/local/redis/redis.conf
appendonly yes #表示开启AOF持久化,默认是no表示关闭
appendfilename "appendonly.aof" #AOF持久化文件名
appendfsync everysec #缓冲同步策略,默认值
no-appendfsync-on-rewrite no #是否重写,默认不重写
测试AOF
开启之后,Redis每执行一条写命令就会将该命令写入硬盘中的AOF文件。AOF文件保存路径和RDB文件路径是一致的,都是通过dir参数配置 , 默 认文 件 名 是 : appendonly.aof
[root@zutuanxue redis]# ./src/redis-server ./redis.conf
[root@zutuanxue redis]# ./src/redis-cli
127.0.0.1:6379> set x 123
OK
127.0.0.1:6379> set y 345
OK
127.0.0.1:6379> exit
[root@zutuanxue redis]# cat appendonly.aof
*2 #表示有二个参数,注意命令本身也是参数的一部份
$6 #第一个参数,长度为6(字节数)
SELECT #第一个参数,总是命令本身
$1 #第二个参数,长度为1
0 #第二个参数
*3
$3
set
$1
x
$3
123
*3
$3
set
$1
y
$3
345
再次测试
[root@zutuanxue redis]# ./src/redis-cli
127.0.0.1:6379> set z 23
OK
127.0.0.1:6379> get z
"23"
127.0.0.1:6379> exit
[root@zutuanxue redis]# cat appendonly.aof
/*前面有以前的写命令,aof文件只会存储写命令*/
*2
$6
SELECT
$1
0
*3
$3
set
$1
z
$2
23
9.3.4、AOF重写
-
为什么要重写?
随着aof文件越来越大,需要定期对aof文件进行重写,达到压缩的目的。
-
重写AOF改变
进程内已经超时的数据不再写入文件。
旧的aof有无效命令(如:set k1 hello ex 10000),新的aof文件只保留最终数据的写入命令。
多条写命令可以合并为一个(如:lpush list a、lpush list b、lpush list c可以转化为:lpush list a b c)。但也不能将整个lpush生成的元素全部写在一起,所以对于list、set、hash、zset等类型操作,以64个元素为界拆分为多条。来防止客户端缓冲区溢出。
AOF重写降低了文件占用空间,更小的aof 文件启动redis时,加载更快。
-
重写方式
AOF重写过程可以手动触发和自动触发:
手动触发:直接调用bgrewriteaof命令。
自动触发:根据auto-aof-rewrite-min-size和auto-aof-rewrite-percentage参数确定自动触发时机
9.3.4.1手动触发
127.0.0.1:6379> bgrewriteaof
Background append only file rewriting started
[root@zutuanxue redis]# ./src/redis-cli
127.0.0.1:6379> keys *
1) "k2"
2) "ff"
3) "h"
4) "k3"
5) "k1"
6) "fds"
7) "hfds"
8) "z"
9) "k"
127.0.0.1:6379> set a 1231
OK
127.0.0.1:6379> set b 12321
OK
127.0.0.1:6379> exit
[root@zutuanxue redis]# cat appendonly.aof
9.3.4.2自动触发
修改配置文件
/usr/local/redis/redis.conf
auto-aof-rewrite-percentage 100 #AOF文件增长率(当前AOF文件大小超过上一次重写的AOF文件大小的百分之多少才会重写)
auto-aof-rewrite-min-size 64mb #表示运行AOF重写时文件最小体积,默认为64MB。
aof_current_size: aof当前尺寸(单位:字节)
aof_base_size: aof上次启动和重写的尺寸(单位:字节)
9.3.4.3自动触发时机
- 当前正在使用的AOF文件大小超过了auto-aof-rewrite-min-size参数所设置的最小尺寸。
- AOF文件增长量(即当前AOF文件大小与上次重写之后的大小之差)超过了auto-aof-rewrite-percentage参数所设置的百分比(默认值是100%)。
如果以上两个条件都满足,Redis就会自动触发AOF重写,并生成新的AOF文件。AOF重写过程是无阻塞的,即在AOF重写过程中,Redis可以继续处理新的命令请求,并将新的命令请求追加到旧的AOF文件中。
自动触发AOF重写的时机仅是一个参考值,实际上,我们可以通过手动触发AOF重写,来控制Redis的性能和可靠性。
查看aof_current_size和aof_base_size的值
127.0.0.1:6379> info persistence
# Persistence
loading:0
rdb_changes_since_last_save:1
rdb_bgsave_in_progress:0
rdb_last_save_time:1583254417
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:0
rdb_current_bgsave_time_sec:-1
rdb_last_cow_size:188416
aof_enabled:1
aof_rewrite_in_progress:0
aof_rewrite_scheduled:0
aof_last_rewrite_time_sec:0
aof_current_rewrite_time_sec:-1
aof_last_bgrewrite_status:ok
aof_last_write_status:ok
aof_last_cow_size:2330624
aof_current_size:295
aof_base_size:211
aof_pending_rewrite:0
aof_buffer_length:0
aof_rewrite_buffer_length:0
aof_pending_bio_fsync:0
aof_delayed_fsync:0
295>64M &&(295-211)/211>=100
只有当这二个条件同时成立,我们才会去触发我们的重写AOF
9.3.5AOF文件恢复
在写入AOF日志文件时,如果Redis服务器宕机,则AOF日志文件文件会出格式错误,在重启Redis服务器时,Redis服务器会拒绝载入这个AOF文件,可以通过以下步骤修复AOF并恢复数据。
使用redis-check-aof命令修复AOF文件,该命令格式如下:
$ redis-check-aof -fix file.aof
重启Redis服务器,加载已经修复的AOF文件,恢复数据。
10、Redis主从复制
Redis有两种不同的持久化方式,Redis服务器通过持久化,把Redis内存中持久化到硬盘当中,当Redis宕机时,我们重启Redis服务器时,可以由RDB文件或AOF文件恢复内存中的数据。
但是发成故障的时候想要快速恢复数据就会用到主从复制
10.1、单机问题
-
机器宕机:如果发生机器宕机(主板烧坏、硬盘损坏、内存损坏等),短时间内我们也无法修复,我们就会考虑将redis迁移到另外一台机器上,并且还要考虑数据同步问题。
-
容器瓶颈:一台机器内存是16G,redis使用12个G内存,除了redis还要使用其他的应用,这个时候,我们可能需要32个G内存才能使用,但是redis的应用对内存的增加也会提交,我们不可能一直去提高单机的内存
-
QPS瓶颈:redis官方数据显示redis可以处理达到10w的QPS,如果业务需要50w、100w的QPS就不行了
关于容量瓶颈、QPS瓶颈和机器宕机,这就是redis分布式和高可用需要解决的问题
10.2、主从复制概述
Master可以拥有多个slave;多个slave可以连接同一个Master外,还可以连接到其他的slave;主从复制不会阻塞Master,在主从复制时,Master可以处理client请求。
主从复制有两种形式:
- 一主一从:Master节点,slave节点,即主节点和从节点。主节点是数据写入,从节点可以通过复制操作将主节点的数据同步过来,并且随着主节点数据不断写入,从节点数据也会做同步的更新。
- 一主多从:一个master可以有多个slave,也就相当于有了多份的数据副本。这样可以做一个更加高可用的选择,例如一个master和一个slave挂掉了,还能有其他的slave数据备份。
10.2.1、主从复制的作用
数据备份:将master的数据,备份到slave
读写分离:用master来写入数据,用slave完成备份后,来完成只读的功能
注:当master宕机后,不会去选择slave作为master。需要手动将一台slave使用slaveof on one提升为master,要想自动实现提升,我们就需要使用哨兵。
10.3、单机主从
-
复制redis.conf
在/usr/local/redis目下,将redis.conf复制成三份分别取名为:redis-8000.conf、redis-8001.conf、redis-8002.conf三个配置文件
cp redis.conf redis-8000.conf cp redis.conf redis-8001.conf cp redis.conf redis-8002.conf
-
配置主机
以redis-8000为主机
#bind 127.0.0.1 #将bind注释掉 或 bind 0.0.0.0 port 8000 #改变其服务端口 daemonize yes #修改服务为后台运行 pidfile /var/run/redis_8000.pid #指定不同的pid文件,注意三份配置文件不同。 logfile "/usr/local/redis/log/redis_8000.log" #指定log日志路径,自己配,要求不同。 dir ./data/redis_8000 #这个指定rdb文件和aof文件的路径配置,要求改成不同。 masterauth ibethfy #都配上吧,从服务到主服务的认证密码。 requirepass ibethfy #三份文件都配置,客户端访问需要密码验证。
-
配置从机
以redis-8001,redis-8002位从机
#bind 127.0.0.1 #将bind注释掉 或 bind 0.0.0.0 port 8001 #改变其服务端口 daemonize yes #修改服务为后台运行 pidfile /var/run/redis_8001.pid #指定不同的pid文件,注意三份配置文件不同。 logfile "/usr/local/redis/log/redis_8001.log" #指定log日志路径,自己配,要求不同。 dir ./data/redis_8001 #这个指定rdb文件和aof文件的路径配置,要求改成不同。 replicaof 127.0.0.1 8000 #主服务这句话注释,从服务配置的两台需要开启。配置主服务的ip的port。 masterauth ibethfy #都配上吧,从服务到主服务的认证密码。 requirepass ibethfy #三份文件都配置,客户端访问需要密码验证。
#bind 127.0.0.1 #将bind注释掉 或 bind 0.0.0.0 port 8002 #改变其服务端口 daemonize yes #修改服务为后台运行 pidfile /var/run/redis_8002.pid #指定不同的pid文件,注意三份配置文件不同。 logfile "/usr/local/redis/log/redis_8002.log" #指定log日志路径,自己配,要求不同。 dir ./data/redis_8002 #这个指定rdb文件和aof文件的路径配置,要求改成不同。 replicaof 127.0.0.1 8000 #主服务这句话注释,从服务配置的两台需要开启。配置主服务的ip的port。 masterauth ibethfy #从服务到主服务的认证密码。 requirepass ibethfy #三份文件都配置,客户端访问需要密码验证。
注:replicaof 127.0.0.1 8000 在以前的版本中用的是slaveof 127.0.0.1 8000,这个配置现在也可以使用
-
启动服务
./src/redis-server ./redis-8000.conf ./src/redis-server ./redis-8001.conf ./src/redis-server ./redis-8002.conf #确认是否启动 ps -ef|grep redis root 14464 1 0 11:58 ? 00:00:00 ./src/redis-server 0.0.0.0:8000 root 14477 1 0 11:58 ? 00:00:00 ./src/redis-server 0.0.0.0:8001 root 14484 1 0 11:58 ? 00:00:00 ./src/redis-server 0.0.0.0:8002 root 14491 13960 0 11:58 pts/1 00:00:00 grep --color=auto redis #开启三个客户端 ./src/redis-cli -h 127.0.0.1 -p 8000 ./src/redis-cli -h 127.0.0.1 -p 8001 ./src/redis-cli -h 127.0.0.1 -p 8002 #查看主从信息 info replication 127.0.0.1:8000> info replication # Replication role:master connected_slaves:2 slave0:ip=127.0.0.1,port=8001,state=online,offset=392,lag=1 slave1:ip=127.0.0.1,port=8002,state=online,offset=392,lag=1 master_replid:9e9ab9f313fae877e7330507fd7b4ce99bc98122 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:392 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:392 127.0.0.1:8001> info replication # Replication role:slave master_host:127.0.0.1 master_port:8000 master_link_status:up master_last_io_seconds_ago:6 master_sync_in_progress:0 slave_repl_offset:434 slave_priority:100 slave_read_only:1 connected_slaves:0 master_replid:9e9ab9f313fae877e7330507fd7b4ce99bc98122 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:434 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:434 127.0.0.1:8002> info replication # Replication role:slave master_host:127.0.0.1 master_port:8000 master_link_status:up master_last_io_seconds_ago:4 master_sync_in_progress:0 slave_repl_offset:420 slave_priority:100 slave_read_only:1 connected_slaves:0 master_replid:9e9ab9f313fae877e7330507fd7b4ce99bc98122 master_replid2:0000000000000000000000000000000000000000 master_repl_offset:420 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:420
-
读写分离
主机写入信息
./src/redis-cli -h 127.0.0.1 -p 8000 127.0.0.1:8000> set str helloworld #从机查看信息 ./src/redis-cli -h 127.0.0.1 -p 8001 127.0.0.1:8001> get str "helloworld" 127.0.0.1:8001> set h 123 (error) READONLY You can't write against a read only replica. ./src/redis-cli -h 127.0.0.1 -p 8002 127.0.0.1:8002> get str "helloworld" 127.0.0.1:8002> set h 123 (error) READONLY You can't write against a read only replica.
10.4、多机主从
-
开启三台虚拟主机123、124、125,分别安装redis
关闭主机防火墙
firewall-cmd --state #查看防火墙状态 service firewalld stop #关闭防火墙
-
配置主机和从机
123为主机,修改配置文件redis.conf
主机配置 #bind 127.0.0.1 #将bind注释掉 或 bind 0.0.0.0 port 6379 #改变其服务端口 daemonize yes #修改服务为后台运行 pidfile /var/run/redis_6379.pid #指定不同的pid文件,注意三份配置文件不同。 logfile "/usr/local/redis/log/redis.log" #指定log日志路径,自己配,要求不同。 dir ./ #这个指定rdb文件和aof文件的路径配置,要求改成不同。 masterauth ibethfy #都配上吧,从服务到主服务的认证密码。 requirepass ibethfy #三份文件都配置,客户端访问需要密码验证。 从机配置 replicaof 192.168.1.123 6379 #主服务这句话注释,从服务配置的两台需要开启。配置主服务的ip的port。
-
启动服务
分别启动三台主机的redis服务器端服务
./src/redis-server ./redis.conf ps -ef|grep redis
分别启动三台主机客户端服务
./src/redis-cli
查看主从信息 info replication
127.0.0.1:6379> info replication
-
读写分离
123为Master,124、125为Slave
Master为写
127.0.0.1:6379> set str helloworld
Slave为读
127.0.0.1:6379> get str "helloworld" 127.0.0.1:6379> set ss fdfd (error) READONLY You can't write against a read only replica.
10.5、手动设置主从
当123虚拟机死机后,手动设置我们的主从,124为主,125为从
124执行slaveof no one
执行命令 SLAVEOF NO ONE 将使得这个从属服务器关闭复制功能,并从从属服务器转变回主服务器,原来同步所得的数据集不会被丢弃。
125执行slaveof host port
执行 SLAVEOF host port 将使当前服务器停止对旧主服务器的同步,丢弃旧数据集,转而开始对新主服务器进行同步。
11、redis哨兵模式
哨兵是一个分布式系统,你可以在一个架构中运行多个哨兵进程,这些进程使用流言协议来接收关于Master主服务器是否下线的信息,并使用投票协议来决定是否执行自动故障迁移,以及选择哪个Slave作为新的Master。
哨兵模式是一种用于保证reids高可用性的解决方案,哨兵模式的主要功能包括监控和自动故障转移。哨兵节点会定期检查主节点和从节点的健康状态,如果发现主节点不可用,它会选举出一个新的主节点,并通知其他从节点切换到新的主节点。这个过程是自动的,不需要人工干预,从而提高了系统的可用性。
然而,需要注意的是,哨兵模式并不能完全消除 Redis 的单点故障。在发生主节点故障时,虽然哨兵会自动进行故障转移,但在这个过程中会有短暂的服务不可用时间。此外,哨兵模式也无法解决网络分区等复杂故障情况为了进一步提高 Redis 的可用性,还可以考虑使用 Redis 集群模式。Redis 集群将数据分布在多个节点上,每个节点都可以处理部分数据,从而实现水平扩展和负载均衡。这样可以提高系统的整体性能和可用性。
11.1 哨兵模式分为单哨兵模式和多哨兵模式
- 单哨兵模式:单个哨兵只需要监控主redis,就可能得到从redis
- 多个哨兵:多个哨兵不仅同时监控主从redis,而且哨兵之间互为监控,多个哨兵可以防止单点故障
哨兵功能:
-
集群监控:负责监控主从集群中的Master和Slave进程是否正常工作。
-
故障转移(failover):如果Master宕机,会自动从Slave中选举出新的Master,进行主从自动切换。
-
配置中心:如果发生了故障转移,Sentinel负责通知客户端新的Master的地址。
-
消息通知:如果某个redis节点有故障,那么Sentsinel会发送报警消息给系统管理员。
11.2、单个哨兵
修改配置文件
/usr/local/redis/sentinel.conf
vim sentinel.conf
#修改配置
sentinel monitor mymaster 192.168.1.123 6379 1
启动:
./src/redis-sentinel ./sentinel.conf
注:mymaster:监控主数据的名称,命名时可以使用大小写字母和“.-_”符号
192.168.1.123 6379:主机的IP和端口号
11.3、多个哨兵
在一主多从的环境中,123为主机,124、125为从机
121中设置三个哨兵sentinel-1.conf、sentinel-2.conf、sentinel-3.conf
将哨兵文件sentinel.conf,复制三份sentinel-1.conf、sentinel-2.conf、sentinel-3.conf
[root@zutuanxue redis]# cp sentinel.conf sentinel-1.conf
[root@zutuanxue redis]# cp sentinel.conf sentinel-2.conf
[root@zutuanxue redis]# cp sentinel.conf sentinel-3.conf
修改配置文件/usr/local/redis/sentinel-1.conf
vim sentine-l.conf
#修改配置
protected-mode no
port 27001
daemonize yes
pidfile "/var/run/redis-sentinel-27001.pid"
logfile "/usr/local/redis/log/27001.log"
sentinel monitor m1 192.168.1.123 6379 1
#sentinel myid c299ec06a9dde77dcbc086082e9d7e30a29615b7
修改配置文件/usr/local/redis/sentinel-2.conf
vim sentine-2.conf
#修改配置
protected-mode no
port 27002
daemonize yes
pidfile "/var/run/redis-sentinel-27002.pid"
logfile "/usr/local/redis/log/27002.log"
sentinel monitor m2 192.168.1.123 6379 1
#sentinel myid c299ec06a9dde77dcbc086082e9d7e30a29615b7
修改配置文件/usr/local/redis/sentinel-3.conf
vim sentine-3.conf
#修改配置
protected-mode no
port 27003
daemonize yes
pidfile "/var/run/redis-sentinel-27003.pid"
logfile "/usr/local/redis/log/27003.log"
sentinel monitor m3 192.168.1.123 6379 1
#sentinel myid c299ec06a9dde77dcbc086082e9d7e30a29615b7
修改完全部启动就ok
12、redis集群
哨兵模式和 Redis 集群模式都是为了提高 Redis 的可用性和扩展性,但它们有不同的应用场景和特点。
哨兵模式适用于单个 Redis 实例的高可用性需求。它通过引入哨兵节点来监控和管理 Redis 主从节点,实现主节点故障时的自动故障转移。哨兵模式相对简单,易于配置和维护,适合于小规模的 Redis 部署。但是哨兵模式存在一些限制,比如故障转移过程中可能会有短暂的服务不可用时间,且无法实现数据的水平扩展。
Redis 集群模式则适用于需要横向扩展 Redis 性能和容量的场景。它将数据分片存储在多个节点上,每个节点只负责处理部分数据。这样可以提高系统的整体性能和容量,并且实现了负载均衡。Redis 集群模式相对复杂,需要对数据进行分片和重新分布,同时还需要客户端支持集群模式。但是它具有更好的可扩展性和高可用性,能够应对大规模的数据存储和访问需求。
所以,在选择 Redis 高可用方案时,可以根据实际需求来选择合适的方案。如果只是需要简单的高可用性保证,哨兵模式可能足够了。如果需要横向扩展和负载均衡,以及更高的可用性和容量,那么 Redis 集群模式可能更适合。
12.1、集群模式介绍
Redis 的哨兵和主从模式基本已经可以实现高可用和读写分离 ,但是在这种模式下每台 Redis 服务器都存储相同的数据,浪费内存空间,所以在redis上加入了 Cluster 集群模式,实现了 Redis 的分布式存储,也就是说Redis 节点上存储不同的内容。
多个redis节点网络互联,数据共享
所有的节点都是一主一从(也可以是一主多从),其中从节点不提供服务,仅作为备用
不支持同时处理多个key(如MSET/MGET),因为redis需要把key均匀分布在各个节点上,并发量很高的情况下同时创建key-value会降低性能并导致不可预测的行为
支持在线增加、删除节点
客户端可以连接任何一个主节点进行读写
12.1.1、集群工作方式
-
数据存取工作方式
在 Redis 的每一个节点上,都有这么两个东西,一个是插槽(slot),一个是cluster。
插槽的取值范围是:0-16383。cluster,可以理解为是一个集群管理的插件。
当我们的存取 Key的时候,Redis 会根据算法得出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,通过这个值,去找到对应的插槽所对应的节点,然后直接自动跳转到这个对应的节点上进行存取操作。
-
集群工作方式
为了保证高可用,redis集群模式引入了主从模式,一个主节点对应一个或者多个从节点,当主节点宕机的时候,就会启用从节点。
那么如何发现主节点宕机?当其它主节点ping一个主节点C时,如果半数以上的主节点与C通信超时,那么认为主节点C宕机了。如果主节点C和它的从节点C1都宕机了,那么该集群就无法再提供服务了。
12.2、集群搭建
集群搭建:至少要三个master
第一步:创建一个文件夹redis-cluster,然后在其下面分别创建6个文件夹如下:
mkdir redis-cluster
cd redis-cluster/
mkdir 7001
mkdir 7002
mkdir 7003
mkdir 7004
mkdir 7005
mkdir 7006
第二步:把之前的redis.conf配置文件分别copy到700*
下
cp redis.conf ./redis-cluster/7001/redis.conf
cp redis.conf ./redis-cluster/7002/redis.conf
cp redis.conf ./redis-cluster/7003/redis.conf
cp redis.conf ./redis-cluster/7004/redis.conf
cp redis.conf ./redis-cluster/7005/redis.conf
cp redis.conf ./redis-cluster/7006/redis.conf
第三步:由于redis集群需要使用ruby命令,所以我们需要安装ruby
yum install ruby
yum install rubygems
gem install redis #(安装redis和ruby的接口)
第四部、修改集群配置
vim ./redis-cluster/7001/redis.conf
#需要修改的配置
daemonize yes
port 700* #(分别对每个机器的端口号进行设置)
bind 192.168.1.171 #(必须要绑定当前机器的ip,不然会无限悲剧下去哇..深坑勿入!!!)
dir /usr/local/redis-cluster/700*/ #(指定数据文件存放位置,必须要指定不同的目录位置,不然会丢失数据,深坑勿入!!!)
appendonly yes
cluster-enabled yes #(启动集群模式,开始玩耍)
cluster-config-file nodes700*.conf#(这里700x最好和port对应上)
cluster-node-timeout 5000 #redis节点宕机被发现的时间
注:每个文件要修改端口号,bind的ip,数据存放的dir,并且nodes文件都需要进行修改
第五步:启动
./src/redis-server ./redis-cluster/7001/redis.conf
./src/redis-server ./redis-cluster/7002/redis.conf
./src/redis-server ./redis-cluster/7003/redis.conf
./src/redis-server ./redis-cluster/7004/redis.conf
./src/redis-server ./redis-cluster/7005/redis.conf
./src/redis-server ./redis-cluster/7006/redis.conf
第六步:创建集群
执行redis-cli--cluster create 命令
./src/redis-cli --cluster create 192.168.1.121:7001 192.168.1.121:7002 192.168.1.121:7003 192.168.1.121:7004 192.168.1.121:7005 192.168.1.121:7006 --cluster-replicas 1
create
表示创建一个redis集群。
–cluster-replicas 1
表示为集群中的每一个主节点指定一个从节点,即一比一的复制。
查看服务状态
ps -ef|grep redis
进入一个节点
./src/redis-cli -c -h 192.168.1.121 -p 7001 192.168.1.121:7001> info cluster # Cluster cluster_enabled:1 #节点是否为cluster模式 。1是0否
测试操做
/src/redis-cli -c -h 192.168.1.121 -p 7005
192.168.1.121:7005> set b fdfsfsd
-> Redirected to slot [3300] located at 192.168.1.121:7001
OK
192.168.1.121:7001> get b
"fdfsfsd"
192.168.1.121:7001> set c fdsfdfdsfds
-> Redirected to slot [7365] located at 192.168.1.121:7002
OK
192.168.1.121:7002> get c
"fdsfdfdsfds"
192.168.1.121:7002> set x fdsfdsfsdf
-> Redirected to slot [16287] located at 192.168.1.121:7003
OK
192.168.1.121:7003> keys *
1) "x"
2) "a"
192.168.1.121:7003> get b
-> Redirected to slot [3300] located at 192.168.1.121:7001
"fdfsfsd"
192.168.1.121:7001> keys *
1) "b"
192.168.1.121:7001> set d fdsfdsfsd
-> Redirected to slot [11298] located at 192.168.1.121:7003
OK
192.168.1.121:7003> get d
"fdsfdsfsd"
192.168.1.121:7003> set x zhangsan
OK
192.168.1.121:7003> get x
"zhangsan"
12.3、集群操做
12.3.1主从切换
查看节点信息
./src/redis-cli -c -h 192.168.1.121 -p 7001
192.168.1.121:7001> cluster nodes #查看集群中的节点
停掉7003,在查看节点信息
ps -ef|grep redis
root 31370 1 0 21:04 ? 00:00:05 ./src/redis-server 192.168.1.121:7001 [cluster]
root 31375 1 0 21:04 ? 00:00:05 ./src/redis-server 192.168.1.121:7002 [cluster]
root 31380 1 0 21:04 ? 00:00:05 ./src/redis-server 192.168.1.121:7003 [cluster]
root 31385 1 0 21:05 ? 00:00:05 ./src/redis-server 192.168.1.121:7004 [cluster]
root 31394 1 0 21:05 ? 00:00:05 ./src/redis-server 192.168.1.121:7005 [cluster]
root 31399 1 0 21:05 ? 00:00:05 ./src/redis-server 192.168.1.121:7006 [cluster]
root 32361 2769 0 22:39 pts/0 00:00:00 grep --color=auto redis
kill -s 9 31380
./src/redis-cli -c -h 192.168.1.121 -p 7005
192.168.1.121:7005> cluster nodes
启动7003,查看节点信息
./src/redis-server ./redis-cluster/7003/redis.conf
./src/redis-cli -c -h 192.168.1.121 -p 7005
192.168.1.121:7005> cluster nodes
12.3.1从节点操作
准备一个新的Redis,7007
cd redis-cluster/
mkdir 7007
cp ./7006/redis.conf ./7007/redis.conf
cd ..
vim ./redis-cluster/7007/redis.conf
./src/redis-server ./redis-cluster/7007/redis.conf
增加从节点
./src/redis-cli --cluster add-node 192.168.1.121:7007 192.168.1.121:7004 --cluster-slave
查看节点信息
./src/redis-cli -c -h 192.168.1.121 -p 7005
192.168.1.121:7005> cluster nodes
删除从节点
./src/redis-cli --cluster del-node 192.168.1.121:7007 99d2ceb080ef0d701546dea0901d4784a201fc06
>>> Removing node 99d2ceb080ef0d701546dea0901d4784a201fc06 from cluster 192.168.1.121:7007
>>> Sending CLUSTER FORGET messages to the cluster...
>>> SHUTDOWN the node.
查看节点信息:
./src/redis-cli -c -h 192.168.1.121 -p 7005
192.168.1.121:7005> cluster nodes
12.3.2主节点操作
将7007设置为7006的主节点
保证7007服务是启动的
./src/redis-cli --cluster add-node 192.168.1.121:7007 192.168.1.121:7006
查看节点信息,未分配槽位不能存储数据
./src/redis-cli -c -h 192.168.1.121 -p 7005
192.168.1.121:7005> cluster nodes
重新分配槽位
./src/redis-cli --cluster reshard 192.168.1.121:7007
查看节点信息
./src/redis-cli -c -h 192.168.1.121 -p 7005
192.168.1.121:7005> cluster nodes
给主节点加从节点
准备一个新的Redis,7008
cd redis-cluster/
mkdir 7008
cp ./7007/redis.conf ./7008/redis.conf
vim ./7008/redis.conf
cd ..
./src/redis-server ./redis-cluster/7008/redis.conf
为7007添加从节点7008
./src/redis-cli --cluster add-node 192.168.1.121:7008 192.168.1.121:7007 --cluster-slave
查看节点信息
./src/redis-cli -c -h 192.168.1.121 -p 7005
192.168.1.121:7005> cluster nodes
主节点操作
-
删除主节点
./src/redis-cli --cluster del-node 192.168.1.121:7008 8e52c94dafa72df26b1eddf94363a4780bed9339 >>> Removing node 8e52c94dafa72df26b1eddf94363a4780bed9339 from cluster 192.168.1.121:7008 >>> Sending CLUSTER FORGET messages to the cluster... >>> SHUTDOWN the node.
-
移动数据将7007移动到7001节点
./src/redis-cli --cluster reshard 192.168.1.121:7007
-
查看节点信息
./src/redis-cli -c -h 192.168.1.121 -p 7005 192.168.1.121:7005> cluster nodes
-
删除主节点
./src/redis-cli --cluster del-node 192.168.1.121:7007 d7a3e48cd142dce6566023fce21e31669e9fa3d5 >>> Removing node d7a3e48cd142dce6566023fce21e31669e9fa3d5 from cluster 192.168.1.121:7007 >>> Sending CLUSTER FORGET messages to the cluster... >>> SHUTDOWN the node.
-
查看节点信息
./src/redis-cli -c -h 192.168.1.121 -p 7005 192.168.1.121:7005> cluster nodes
13、redis Pipeline
13.1、Pipeline介绍
Redis本身是基于Request/Response协议的,正常情况下,客户端发送一个命令,等待Redis应答,Redis在接收到命令,处理后应答。在这种情况下,如果同时需要执行大量的命令,那就是等待上一条命令应答后再执行,这中间不仅仅多了RTT(Round Time Trip),而且还频繁的调用系统IO,发送网络请求。
Pipline(管道)是redis提供的一种批量执行命令的机制,它允许客户端在一次网络中返回发送多个命令,并一次性接收这些命令的响应结果,从而提高了 Redis 的性能和效率。
概述:在传统的 Redis 请求中,每个命令都需要通过网络单独发送给 Redis 服务器,并等待服务器的响应。这会造成一定的网络延迟和通信开销。而使用 Pipeline,可以将多个命令打包发送给 Redis 服务器,减少了网络往返次数,从而提高了性能。
13.2、Pipeline 原理
要支持Pipeline,其实既要服务端的支持,也要客户端支持。对于服务端来说,所需要的是能够处理一个客户端通过同一个TCP连接发来的多个命令,可以理解为,这里将多个命令切分,和处理单个命令一样
具体实现上,Pipeline 主要依赖于以下两个机制:
-
批量发送命令:客户端将多个命令按顺序打包成一个请求,然后一次性发送给 Redis 服务器。这样可以减少网络通信开销和延迟。
-
批量接收响应:Redis 服务器在接收到请求后,按照请求中命令的顺序依次处理,并将每个命令的响应结果按照顺序返回给客户端。客户端在接收到响应后,按照发送的顺序依次处理每个命令的响应结果。
通过这种方式,Pipeline 实现了将多个命令合并为一个请求发送,以及一次性接收多个命令的响应结果。这样就避免了每个命令单独发送和接收响应的网络开销,从而提高了 Redis 的性能。
需要注意的是,Pipeline 并不会改变 Redis 服务器对命令的执行顺序。Redis 服务器仍然会按照接收到的命令的顺序依次执行,并将响应结果按照相同的顺序返回给客户端。Pipeline 只是在网络通信层面上进行了优化,减少了网络往返次数,提高了性能。
另外,需要注意 Pipeline 的使用场景。它适用于需要批量执行多个命令或者批量操作的场景,可以显著提升性能。但对于单个简单的命令,使用 Pipeline 可能并不会带来明显的性能提升,甚至可能增加复杂性。
概述:Pipeline 的原理是通过批量发送命令和批量接收响应的方式,减少网络往返次数,从而提高 Redis 的性能和效率。
注:需要控制Pipeline的大小,否则会消耗Redis的内存
13.3、Pipeline的步骤
使用 Pipeline 的步骤如下:
-
建立连接:客户端与 Redis 服务器建立连接。
-
发送命令:客户端将多个命令打包发送给 Redis 服务器,而不是逐个发送。
-
接收响应:Redis 服务器按照发送的顺序依次处理命令,并将每个命令的响应结果按照顺序返回给客户端。
-
处理响应:客户端接收到响应后,按照发送的顺序依次处理每个命令的响应结果。
Pipeline 可以加速命令的执行,但它也带来了一些潜在的风险。由于 Pipeline 中的每个命令都会被立即发送到 Redis 服务器,如果其中某个命令出现错误,后续命令可能会受到影响。因此,在使用 Pipeline 时要确保每个命令都是正确的,并且适当处理错误情况。
13.4、Pipeline和事物的区别
- 原子性:事务是原子性的,它将所有操作作为一个单元执行,如果任何一个操作失败,则所有操作都将回滚到之前的状态。Pipeline没有原子性保障,任何一个操作执行失败,需要手动处理异常。
- 阻塞性:事务在执行期间会阻塞其他客户端的请求,直到事务完成。而Pipeline不会阻塞其他请求,因为它是异步执行的,它只负责将多个操作打包成一个请求,然后一次性发送到Redis。
- 可撤销性:事务可以通过执行回滚操作来撤销已经执行的操作。而Pipeline不能进行回滚。
13.5、python Pipeline操作
import redis
# 创建 Redis 客户端连接
r = redis.Redis(host='localhost', port=6379)
# 创建 Pipeline 对象
pipe = r.pipeline()
# 批量执行命令
pipe.set('key1', 'value1')
pipe.get('key1')
pipe.hset('hash', 'field', 'value')
pipe.hget('hash', 'field')
pipe.lpush('list', 'item')
pipe.lrange('list', 0, -1)
# 执行命令并获取响应结果
result = pipe.execute()
# 处理响应结果
for i in range(0, len(result), 2):
print(result[i], result[i+1])
13.6、Pipeline的适用场景
- 批量操作:当需要执行多个 Redis 命令时,使用 Pipeline 可以将这些命令打包在一起发送给 Redis 服务器,减少网络往返次数,提高性能。例如批量设置多个键值对、批量获取多个键的值等。
- 高吞吐量需求:Pipeline 可以减少每个命令的网络通信开销和延迟,从而提高 Redis 的吞吐量。特别是在需要频繁地与 Redis 交互、进行大量读写操作的场景下,使用 Pipeline 可以显著提升性能。
- 数据迁移:当需要将数据从一个 Redis 实例迁移到另一个实例时,可以使用 Pipeline 进行快速的数据迁移。通过将多个命令打包在一起发送,可以高效地将数据从源实例导出,并一次性导入到目标实例中。
- 复杂事务操作:当需要执行复杂的事务操作时,使用 Pipeline 可以将多个命令打包在一起,并使用 MULTI 和 EXEC 命令进行事务的原子性操作。这样可以保证事务中的所有命令要么全部成功执行,要么全部失败,从而维护数据的一致性。
14、redis雪崩、穿透、击穿
14.1雪崩
概念:Redis雪崩是指在高并发场景下,由于Redis缓存层的大规模失效或者访问集中导致的服务故障现象
当缓存层失效时,大量的请求会直接打到数据库上,由于数据库无法承受如此大的并发压力,导致数据库响应变慢甚至宕机,进而影响整个系统的正常运行。这种情况下,系统性能和可用性都会受到严重的影响,甚至可能导致系统崩溃。
Redis雪崩通常有以下几个主要原因:
-
缓存失效:当缓存中的数据过期或者被意外删除时,如果大量请求同时访问到该数据,会导致请求直接打到数据库上,增加了数据库的负载压力。
-
集中过期时间:如果大量的缓存数据设置了相同的过期时间,当这些数据同时失效时,会导致大量的请求打到数据库上,引起雪崩效应。
-
数据库压力:当数据库本身存在性能问题、负载过高或者发生网络故障等情况时,会导致数据库响应变慢或者不可用,从而引发Redis雪崩。
为了避免Redis雪崩,可以采取以下几个措施(解决办法):
- 设置合适的缓存过期时间:合理设置缓存数据的过期时间,避免大量数据同时失效。
- 缓存数据异步更新:通过异步方式更新缓存数据,避免大量请求同时访问到过期的数据。
- 分布式锁:在缓存数据失效时,使用分布式锁来保证只有一个请求去加载数据,其他请求等待并复用已加载好的数据。
- 多级缓存:使用多级缓存架构,如本地缓存和分布式缓存相结合,提高系统的可用性和性能。
- 限流与熔断:通过限制访问量或者使用熔断机制,避免大量请求同时访问缓存层和数据库。
- 数据预热:对于即将来临的大量请求,我们可以提前走一遍系统,将数据提前缓存在Redis中,并设置不同的过期时间。
- 保证redis服务高可用:Redis的哨兵模式和集群模式,为防止Redis集群单节点故障,可以通过这两种模式实现高可用。
综上所述,针对Redis雪崩问题,需要综合考虑缓存策略、系统架构和故障预防等方面的因素,并采取相应的措施来降低风险并提高系统的稳定性和可用性。
熔断机制介绍:
熔断机制是一种用于保护分布式系统的一种设计模式。它的主要目的是在面对服务故障或异常情况时,通过快速失败来避免连锁故障,从而提高系统的稳定性和可用性。
熔断机制通常应用于微服务架构中,其中各个微服务之间存在依赖关系。当某个微服务出现故障或者延迟较高时,传统的做法是持续尝试访问该服务,这可能导致请求积压、资源耗尽以及整个系统的崩溃。而熔断机制则通过在出现故障时快速拒绝请求,避免资源浪费和连锁故障。
熔断机制的工作原理如下:
-
监控:系统会监控对某个服务的请求成功率、响应时间等指标。当这些指标超过预设的阈值时,表示该服务可能出现故障或者延迟。
-
熔断器状态:根据监控指标,熔断器会处于不同的状态,包括关闭状态(正常请求通过)、打开状态(拒绝所有请求)和半开状态(部分请求通过)。
-
打开状态:当熔断器进入打开状态时,所有的请求都会被快速失败,不再请求该服务。这样可以避免请求积压和资源耗尽。
-
半开状态:在一段时间后,熔断器会自动进入半开状态,允许部分请求通过。如果这些请求成功,则认为服务已经恢复正常,熔断器会重新进入关闭状态。如果请求仍然失败,则熔断器会重新进入打开状态。
通过熔断机制,可以将故障隔离,提高系统的稳定性和可用性。同时,熔断机制还可以提供实时监控和告警功能,帮助开发人员及时发现和处理故障,并降低对依赖服务的依赖性。
总结:熔断机制是一种通过快速失败来保护系统的设计模式,能够有效防止连锁故障,提高系统的稳定性和可用性。
14.2、redis穿透
概述:缓存穿透:缓存和数据库中都没有的数据,可用户还是源源不断的发起请求,导致每次请求都会到数据库,从而压垮数据库。
流程:比如客户查询一个根本不存在的东西,首先从Redis中查不到,然后会去数据库中查询,数据库中也查询不到,那么就不会将数据放入到缓存中,后面如果还有类似源源不断的请求,最后都会压到数据库来处理,从而给数据库造成巨大的压力。
解决办法:
- 业务层校验
用户发过来的请求,根据请求参数进行校验,对于明显错误的参数,直接拦截返回。
比如,请求参数为主键自增id,那么对于请求小于0的id参数,明显不符合,可以直接返回错误请求。
- 不存在数据设置短过期时间
对于某个查询为空的数据,可以将这个空结果进行Redis缓存,但是设置很短的过期时间,比如30s,可以根据实际业务设定。注意一定不要影响正常业务。
- 布隆过滤器
关于布隆过滤器,后面会详细介绍。布隆过滤器是一种数据结构,利用极小的内存,可以判断大量的数据“一定不存在或者可能存在”。
对于缓存击穿,我们可以将查询的数据条件都哈希到一个足够大的布隆过滤器中,用户发送的请求会先被布隆过滤器拦截,一定不存在的数据就直接拦截返回了,从而避免下一步对数据库的压力。
14.3、redis击穿
概述:Redis击穿是指在高并发场景下,针对某个特定的缓存 key,由于大量请求同时访问该 key,而该 key 正好失效或者不存在,导致请求直接打到后端数据库上,增加了数据库的负载压力。
具体来说,当一个缓存 key 失效或者被删除时,如果此时有大量请求同时访问该 key,这些请求会发现缓存中没有数据,于是会直接去查询数据库或者其他数据源。由于数据库无法承受如此大的并发请求,数据库的响应时间会变慢甚至宕机,从而导致整个系统的性能下降甚至崩溃。
Redis击穿通常发生在以下情况下:
-
缓存失效:当缓存中的数据过期或者被意外删除时,大量请求同时访问到该数据,导致请求直接打到后端数据库。
-
热点数据:某些特定的热点数据,如广告位、热门商品等,可能会引起大量请求同时访问,一旦缓存失效,就会导致击穿问题。
为了避免Redis击穿,可以采取以下几个措施:
- 互斥锁(Mutex):在缓存失效时,使用互斥锁来保证只有一个请求去加载数据,其他请求等待并复用已加载好的数据。,这是比较常用的办法
- 降级策略:当缓存失效时,可以采取一些降级策略,如返回默认值或者预先计算好的结果,避免直接打到数据库。
- 设置热点数据用不过期:对于某个需要频繁获取的信息,缓存在Redis中,并设置其永不过期。当然这种方式比较粗暴,对于某些业务场景是不适合的。
- 定时更新:比如这个热点数据的过期时间是1h,那么每到59minutes时,通过定时任务去更新这个热点key,并重新设置其过期时间。
为了避免Redis击穿问题,需要综合考虑缓存策略、数据预加载、互斥锁等方面的因素,并采取相应的措施来降低风险并提高系统的稳定性和可用性。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY