Redis高级(持久化--redis主从架构--redis哨兵模式--redis分片集群)
Posted 依旧范特西_0l
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis高级(持久化--redis主从架构--redis哨兵模式--redis分片集群)相关的知识,希望对你有一定的参考价值。
目录
redis实现持久化的两种模式
RDB持久化
RDB全称Redis Database Backup file(Redis数据备份文件),也被称为数据快照,就是把redis内存中的所有数据写入到本地磁盘中, 当服务宕机重启后,可以读取本地磁盘文件来恢复数据,Redis数据备份文件被称为RDB文件,默认是保存在当前运行目录下(可通过配置文件更改相关保存信息)
执行时机:
1) 执行save命令
2) 执行bgsave命令
3) 正常停机时
4) 达到RDB自动保存的阈值时
save命令:
执行save命令,会触发一次RDB持久,是主进程在进行磁盘IO, 此时无法对外提供服务,会阻塞请求, 只适用于手动停机,数据迁移的场景
bgsave命令:
与save命令不同的是,bgsave是异步执行的,从主进程fork一个子进程进行持久化操作,在磁盘IO的时候,不会阻塞请求,主进程能够正常的提供服务,值得注意的是在主线程fork的时候是阻塞请求的状态
RDB自动保存阈值:
Redis内部有触发RDB的机制,可以在redis.conf文件中找到,格式如下:
save 900 1 # 900秒内,如果至少有1个key被修改,则执行bgsave save 300 10 # 300秒内, 如果至少有10个key被修改,则执行bgsave save 60 10000 # 60秒内,10000个key # 如果是save "" 则表示禁用RDB
其他配置:
# 是否压缩 ,建议不开启,压缩也会消耗cpu,磁盘的话不值钱 rdbcompression yes # RDB文件名称 dbfilename dump.rdb # 文件保存的路径目录 dir ./
RDB执行原理:
save:
操作系统会在redis中维护一个虚拟内存表" 页表 " ,页表和物理内存有映射关系,所以redis主进程在持久化的时候,只需要操作虚拟内存中的页表,操作系统会从页表中读取数据,实现磁盘IO
bgsave:
会从主进程fork出一个子进程,fork的过程是将主进程中的页表复制到子进程中,子进程拥有同样的映射关系后,就可以读取物理内存,生成RDB文件. 这样虽然fork效率高,而且不会影响主进程对外提供服务,但是异步的,就会出现脏读的现象出现,而fork也想到了这一点,采用了copy-on-write技术:
在子进程读的时候,会把内存数据标记成read-only状态,此时主线程只能进行读取,若要进行写操作,就要在内存中拷贝一份数据副本,对数据副本进行写操作,数据副本产生后,主线程页表就会映射数据副本,
在redis优化中,会提到在给redis分配内存的时候尽量有一般的内存留余,在极端情况下,有可能子线程读取很慢,而主进程的写操作很多,就会导致拷贝很多的数据副本,甚至会拷贝全部的数据副本,这样redis内存就翻倍了,但这种情况几乎不会出现,但理论上是会出现
RDB的缺点:
两次RDB操作之间如果服务宕机,就会丢失数据, 无法保证数据的强完整性
fork子进程,生成RDB文件,压缩RDB文件都比较耗时
AOF持久化
AOF全称为Append Only File(追加文件)。Redis处理的每一个写命令都会记录在AOF文件,可以看做是命令日志文件。当需要进行数据恢复的时候,重新执行AOF文件即可
AOF模式的开启:
该模式默认是不开启的,需要我们修改redis.conf配置文件来开启AOF:
# 是否开启AOF功能,默认是no appendonly yes # AOF文件的名称 appendfilename "appendonly.aof"
AOF的IO频率也可以通过配置文件进行更改:
# 表示每执行一次写命令,立即记录到AOF文件 appendfsync always # 写命令执行完先放入AOF缓冲区,然后表示每隔1秒将缓冲区数据写到AOF文件,是默认方案 appendfsync everysec # 写命令执行完先放入AOF缓冲区,由操作系统决定何时将缓冲区内容写回磁盘 appendfsync no
三种模式的对比:
AOF的文件命令重写:
在redis中,往往会有对同一个key有不同的操作,这些操作命令都会被存储在AOF文件中,所以AOF文件会比RDB文件大很多,同时这些对同一个key的多条命令,只有最后一条才有价值,所以我们可以通过执行bgrewriteaof命令对其进行体积优化()BGREWRITEAOF — Redis 命令参考BGREWRITEAOF — Redis 命令参考()BGREWRITEAOF — Redis 命令参考,
如图,AOF原本有三个命令,但是set num 123 和 set num 666都是对num的操作,第二次会覆盖第一次的值,因此第一个命令记录下来没有意义。 所以重写命令后,AOF文件内容就是:mset name jack num 666 ,
Redis也会在触发阈值时自动去重写AOF文件。阈值也可以在redis.conf中配置:
# AOF文件比上次文件 增长超过多少百分比则触发重写 auto-aof-rewrite-percentage 100 # AOF文件体积最小多大以上才触发重写 auto-aof-rewrite-min-size 64mb
RDB与AOF两种持久化模式对比:
在企业应用中,我们一般会根据数据的价值,效率等各方条件进行评估,再考虑使用哪种方式
redis主从架构
主从架构
单节点的redis对并发的承受能力是有限的,所以如果业务流量大,我们会对redis搭建主从架构,进行读写分离 redis主从搭建
redis集群的主从数据同步有两种方式,全量同步/增量同步
全量同步
执行时机:
在主从服务器第一次连接或者从服务器长时间未同步的时候会执行全量同步,会将master节点上的所有数据同步到slave节点上
在主从数据同步的时候,会有两个属性, Replication Id,offset用来判断slave是否是第一次连接,slave数据同步到哪里了
Replication Id:
简称replid,是数据集的标记,master与slave节点都有replid,但是id不一样, 只有在建立连接的时候,slave节点才会继承master分支的replid,所以在进行主从连接的时候, 可以通过id是否一致来判断是需要进行全量同步
offset:
偏移量,随着记录在repl_baklog中的数据增多而逐渐增大。slave完成同步时也会记录当前同步的offset。如果slave的offset小于master的offset,说明slave数据落后于master,需要更新。
repl_baklog:
在master节点执行持久化的时候,会记录所有持久化期间的命令到repl_baklog, 用来计算offset偏移量
因此slave做数据同步,必须向master声明自己的replication id 和offset,master才可以判断到底需要同步哪些数据。
流程图:
全量同步是将生成的RDB文件通过网络进行传输,传输成本较高,所以一般都是主从节点第一次连接的时候进行全量同步,全量同步后,就会陆续执行增量同步,来确保数据一致性
增量同步
执行时机:
全量同步是将生成的RDB文件通过网络进行传输,传输成本较高,所以一般都是主从节点第一次连接的时候进行全量同步,全量同步后,就会陆续执行增量同步,来确保数据一致性,
增量同步是通过offset值来判断slave节点同步到哪里了, 然后会将repl_baklog中的偏差记录发送给slave节点,slave节点执行记录使其同步
流程图:
如何通过offset值来确定同步位置的:
这就要说到全量同步时的repl_baklog文件了。这个文件是一个固定大小的数组,只不过数组是环形,也就是说角标到达数组末尾后,会再次从0开始读写,这样数组头部的数据就会被覆盖。repl_baklog中会记录Redis处理过的命令日志及,包括master当前的offset值,和slave已经拷贝到的offset值
此时,master的offset值与slave的offset值的差值,就是slave需要增量同步的数据
随着数据的不断持久化,master的offset值会不断增大,而slave也会不断的进行增量同步
直到数据被填满,,此时master就会覆盖绿色部分(也就是slave已经同步过的数据)
但如果salve在增量同步的过程中遇到了阻塞,就会导致从节点的进度赶不上主节点的进度,有可能会导致slave的offset被覆盖,如果此时slave状态恢复,就无法进行增量同步了,只能进行全量同步
redis主从集群 -- 数据同步优化
主从同步可以保证主从数据的一致性,非常重要。可以从以下几个方面来优化Redis主从就集群:
- 在master中配置repl-diskless-sync yes启用无磁盘复制,避免全量同步时的磁盘IO。
- Redis单节点上的内存占用不要太大,减少RDB导致的过多磁盘IO
- 适当提高repl_baklog的大小,发现slave宕机时尽快实现故障恢复,尽可能避免全量同步
- 限制一个master上的slave节点数量,如果实在是太多slave,则可以采用主-从-从链式结构,减少master压力
redis主从集群+哨兵框架
redis主从集群可以实现高并发读的功能,配合哨兵框架可实现高可用
架构图:
哨兵的作用如下:
监控:Sentinel 会不断检查您的master和slave是否按预期工作
自动故障恢复:如果master故障,Sentinel会将一个slave提升为master。当故障实例恢复后也以新的master为主
通知:Sentinel充当Redis客户端的服务发现来源,当集群发生故障转移时,会将最新信息推送给Redis的客户端
集群监控原理:
Sentinel基于心跳机制监测服务状态,每隔1秒向集群的每个实例发送ping命令:
主观下线:如果某sentinel节点发现某实例未在规定时间响应,则认为该实例主观下线。
客观下线:若超过指定数量(quorum)的sentinel都认为该实例主观下线,则该实例**客观下线**。quorum值最好超过Sentinel实例数量的一半。
自动故障恢复原理:
一旦发现master故障,sentinel需要在salve中选择一个作为新的master,选择依据是这样的:首先会判断slave节点与master节点断开时间长短,如果超过指定值(down-after-milliseconds * 10)则会排除该slave节点然后判断slave节点的slave-priority值(默认都为1),越小优先级越高,如果是0则永不参与选举,如果slave-prority一样,则判断slave节点的offset值,越大说明数据越新,优先级越高最后是判断slave节点的运行id大小,越小优先级越高。
选举成功后的执行流程:
sentinel给备选的slave1节点发送slaveof no one命令,让该节点成为master
sentinel给所有其它slave发送slaveof (IP地址) + 7002 命令,让这些slave成为新master的从节点,开始从新的master上同步数据。
最后,sentinel将故障节点标记为slave,当故障节点恢复后会自动成为新的master的slave节点
redis分片集群
分片集群
redis读写分离配合哨兵模式可以实现高可用,高并发读的要求,但不满足海量数据存储,高并发写的要求,所以我们可以搭建redis分片集群
架构图:
分片集群特征:
集群中有多个master,每个master保存不同数据
每个master都可以有多个slave节点
master之间拥有心跳检测机制,通过ping监测彼此健康状态
分片集群中存在散列插槽机制,所以客户端请求可以访问集群任意节点,最终都会被转发到正确节点
散列插槽(hash插槽)
Redis会把每一个master节点映射到0~16383共16384个插槽(hash slot)上,查看集群信息时就能看到:
数据中的key不是与master节点进行绑定的,而是与插槽进行绑定的,所以不用指定访问哪个节点,redis会根据key的插槽值找到key所在的位置,而插槽值是根据key的有效部分计算的, 盘点有效部分有两种情况
- key中包含"",且“”中至少包含1个字符,“”中的部分是有效部分 # key: anum 会根据a计算插槽值
- key中不包含“”,整个key都是有效部分 # key: num 会根据num计算插槽值计算方式是利用CRC16算法得到一个hash值,然后对16384取余,得到的结果就是slot值。
如何将一类数据分配到统一节点上:
综上得知,分片集群中的每个节点都是映射16384个插槽的一部分,数据进行存储的时候是计算key的slot值进行存储, 那同一类数据对的key值可能会计算出不同的slot值,就会被分配到不同的节点上,如果要实现对一类数据的群组化,我们就可以利用slot值计算的有效部分,在同一类数据key加上typeID,这样计算出的slot值就会一样了
集群伸缩
如何添加一个分片节点:
new_host参数: 新节点的ip地址
new_port参数: 新节点的端口号
existing_host参数: 任意一个已经存在的节点的ip
existing_port参数: 任意一个已经存在的节点的端口
existing_host参数和existing_port参数用于推送新节点上线信息给集群中的其他节点
转移插槽:
在新节点上线后,是没有插槽映射的,所以需要我们手动分配插槽
命令:
首先建立连接(这里ip和端口号可以填写任意旧节点的信息):
选择抽取多少个插槽:
填写新节点id,接收插槽:
以哪种方式抽取插槽:
- all:代表全部,也就是三个节点各转移一部分
- 具体的id:目标节点的id
- done:没有了我们这里填写具体的节点id
完成
故障转移
自动故障转移:
分片集群支持自动故障转移,当一台master故障后, 集群内的心跳机制就会发现,当超过一般的节点认为该节点下线后,就会自动选举一个salve当做新的master,而当故障节点重启后,会以salve的角色进行运行
手动故障转移:
利用cluster failover命令可以手动让集群中的某个master宕机,切换到执行cluster failover命令的这个slave节点,实现无感知的数据迁移。其流程如下
分布式缓存技术redis学习系列——redis高级应用(主从事务与锁持久化)
上文《详细讲解redis数据结构(内存模型)以及常用命令》介绍了redis的数据类型以及常用命令,本文我们来学习下redis的一些高级特性。
安全性设置
设置客户端操作秘密
redis安装好后,默认情况下登陆客户端和使用命令操作时不需要密码的。某些情况下,为了安全起见,我们可以设置在客户端连接后进行任何操作之前都要进行密码验证。修改redis.conf进行配置。
[root@localhost ~]# vi /usr/local/redis/etc/redis.conf
#######################SECURITY ##############################
......
# Warning: since Redis is pretty fast an outside user can try up to
# 150k passwords per second against a good box. This means that you should
# use a very strong password otherwise it will be very easy to break.
#
# requirepass foobared
requirepass redis129
# Command renaming.
如上,找到# requirepass foobared这一行,在下面添加“requirepass 密码”一行设置密码。设置好密码后,有两种方式授权客户端进行操作。
客户端授权方式
(1)登录时使用-a参数指定客户端密码,如下
[root@localhost ~]# /usr/local/redis/bin/redis-cli -h 192.168.2.129 -p 6379 -a redis129
192.168.2.129:6379> keys *
1) "myzset"
192.168.2.129:6379>
(2)登录客户端后使用auth命令进行授权,如下
[root@localhost ~]# /usr/local/redis/bin/redis-cli -h 192.168.2.129 -p 6379
192.168.2.129:6379> keys *
(error) NOAUTH Authentication required.
192.168.2.129:6379> auth redis129
OK
192.168.2.129:6379> keys *
1) "myzset"
192.168.2.129:6379>
主从复制
主从复制,即主服务器与从服务器之间数据备份的问题。Redis 支持简单且易用的主从复制(master-slave replication)功能, 该功能可以让从服务器(slave server)成为主服务器(master server)的精确复制品。
主从复制的特点
(1)一个主服务器可以有多个从服务器。
(2)不仅主服务器可以有从服务器, 从服务器也可以有自己的从服务器。
(3)Redis 支持异步复制和部分复制(这两个特性从Redis 2.8开始),主从复制过程不会阻塞主服务器和从服务器。
(4)主从复制功能可以提升系统的伸缩性和功能,如让多个从服务器处理只读命令,使用复制功能来让主服务器免于频繁的执行持久化操作。
主从复制的过程
下面我们用一个图来讲解redis主从复制的过程。
Redis主从复制过程示意图
从上面的示意图可以看出,主服务器与从服务器建立连接之后,Redis主从复制过程主要有下面几步:
(1)从服务器都将向主服务器发送一个 SYNC 命令。
(2)主服务器接到 SYNC 命令后开启一个后台子进程并开始执行 BGSAVE,并在保存操作执行期间, 将所有新执行的写入命令都保存到一个缓冲区里面。
(3)当 BGSAVE 执行完毕后, 主服务器将执行保存操作所得的 .rdb 文件发送给从服务器, 从服务器接收这个 .rdb 文件, 并将文件中的数据载入到内存中。
(4)主服务器会以 Redis 命令协议的格式, 将写命令缓冲区中积累的所有内容都发送给从服务器。
配置从服务器
redis配置一个从服务器非常简单, 只要在从服务器的配置文件redis.conf中增加主服务器的IP地址和端口号就可以,如果主服务器设置了客户端密码,还需要在从服务器中配置主服务器的密码,如下
##########################REPLICATION ###############################
# Master-Slave replication. Use slaveof to make a Redis instance a copy of
# another Redis server. A few things to understand ASAP about Redis replication.
#
......
# slaveof <masterip> <masterport>
slaveof 192.168.2.129 6379
# If the master is password protected (using the "requirepass" configuration
# directive below) it is possible to tell the slave to authenticate before
# starting the replication synchronization process, otherwise the master will
# refuse the slave request.
#
# masterauth <master-password>
masterauth redis129
事务与锁
Redis 的事务支持相对简单,MULTI 、 EXEC 、 DISCARD 和 WATCH 这四个命令是 Redis 事务的基础。
事务开启与取消
l MULTI 开启一个事务。当客户端发出了MULTI 命令时,客户端和服务端的连接就进入了一个事务上下文的状态。MULTI 执行之后, 客户端可以继续向服务器发送任意多条命令, 这些命令不会立即被执行, 而是被放到一个队列中, 当 EXEC 命令被调用时, 所有队列中的命令才会被执行。
l EXEC 顺序执行事务队列中的命令。
192.168.2.129:6379> multi
OK
192.168.2.129:6379> set name "zhangsan"
QUEUED
192.168.2.129:6379> set age 20
QUEUED
192.168.2.129:6379> exec
1) OK
2) OK
192.168.2.129:6379> keys *
1) "age"
2) "name"
192.168.2.129:6379>
l DISCARD 取消事务。当执行 DISCARD 命令时, 事务会被放弃, 事务队列会被清空, 并且客户端会从事务状态中退出。
192.168.2.129:6379> multi
OK
192.168.2.129:6379> set name2 "lisi"
QUEUED
192.168.2.129:6379> set age 22
QUEUED
192.168.2.129:6379> discard
OK
192.168.2.129:6379> exec
(error) ERR EXEC without MULTI
192.168.2.129:6379>
乐观锁
l WATCH 对key值进行锁操作。 在 WATCH 执行之后, EXEC 执行之前, 有其他客户端修改了 key 的值, 那么当前客户端的事务就会失败。如下:
Client1开启watch name并在事务中修改name,但是没有执行exec
192.168.2.129:6379> get name
"huangliu"
192.168.2.129:6379> watch name
OK
192.168.2.129:6379> multi
OK
192.168.2.129:6379> set name lisi
QUEUED
Client2 修改name
192.168.2.129:6379> get name
"huangliu"
192.168.2.129:6379> set name "wangwu"
OK
192.168.2.129:6379> get name
"wangwu"
192.168.2.129:6379>
Client1执行exec
192.168.2.129:6379> exec
(nil)
192.168.2.129:6379>
可见,由于被watch的name已经被Client2 修改,所以Client1的事务执行失败,程序需要做的, 就是不断重试这个操作, 直到没有发生碰撞(Crash)为止。对key进行加锁监视的机制类似Java多线程中的锁(synchronized中的监视器对象),被称作乐观锁。乐观是一种非常强大的锁机制,后面我们会进一步学习redis的分布式锁。
持久化机制
前面我们已经说过,既可以把redis理解为缓存技术,也可以理解为数据库,因为redis支持将内存中的数据周期性的写入磁盘或者把操作追加到记录文件中,这个过程称为redis的持久化。redis支持两种方式的持久化,一种是快照方式(snapshotting),也称RDB方式;两一种是追加文件方式(append-only file),也称AOF方式。RDB方式是redis默认的持久化方式。
RDB方式
RDB方式是将内存中的数据的快照以二进制的方式写入名字为 dump.rdb的文件中。我们对 Redis 进行设置, 让它根据设置周期性自动保存数据集。修改redis.conf文件,如下
######################### SNAPSHOTTING ################################
#
# Save the DB on disk:
......
# In the example below the behaviour will be to save:
# after 900 sec (15 min) if at least 1 key changed
# after 300 sec (5 min) if at least 10 keys changed
# after 60 sec if at least 10000 keys changed
#
# Note: you can disable saving completely by commenting out all "save" lines.
#
# It is also possible to remove all the previously configured save
# points by adding a save directive with a single empty string argument
# like in the following example:
#
# save ""
#900秒内如果有超过1个key被修改则发起保存快照
save 900 1
#300秒内如果有超过10个key被修改则发起保存快照
save 300 10
#60秒内如果有超过1000个key被修改则发起保存快照
save 60 10000
dump.rdb文件默认生成在%REDIS_HOME%etc目录下(如/usr/local/redis/etc/),可以修改redis.conf文件中的dir指定dump.rdb的保存路径
# The filename where to dump the DB
dbfilename dump.rdb
# The working directory.
#
# The DB will be written inside this directory, with the filename specified
# above using the \'dbfilename\' configuration directive.
#
# The Append Only File will also be created inside this directory.
#
# Note that you must specify a directory here, not a file name.
dir ./
AOF方式
RDB方式是周期性的持久化数据, 如果未到持久化时间点,Redis 因为某些原因而造成故障停机, 那么服务器将丢失最近写入、且仍未保存到快照中的那些数据。所以从redis 1.1开始引入了AOF方式,AOF 持久化记录服务器执行的所有写操作命令,并在服务器启动时,通过重新执行这些命令来还原数据集。 AOF 文件中的命令全部以 Redis 协议的格式来保存,新命令会被追加到文件的末尾。
AOF方式仍然有丢失数据的可能,因为收到写命令后可能并不会马上将写命令写入磁盘,因此我们可以修改redis.conf,配置redis调用write函数写入命令到文件中的时机。如下
#######################APPEND ONLY MODE #############################
......
# AOF and RDB persistence can be enabled at the same time without problems.
# If the AOF is enabled on startup Redis will load the AOF, that is the file
# with the better durability guarantees.
#
# Please check http://redis.io/topics/persistence for more information.
#启用AOF方式
appendonly yes
#每次有新命令追加到 AOF 文件时就执行一次 fsync :非常慢,也非常安全
appendfsync always
#每秒 fsync 一次:足够快(和使用 RDB 持久化差不多),并且在故障时只会丢失 1 秒钟的数据
appendfsync everysec
#从不 fsync :将数据交给操作系统来处理。更快,也更不安全的选择
appendfsync no
从上面三种AOF持久化时机来看,为了保证不丢失数据,appendfsync always是最安全的。
发布以及订阅消息
Redis的发布以及订阅有点类似于聊天,是一种消息通信模式。在这个模式中,发送者(发送信息的客户端)不是将信息直接发送给特定的接收者(接收信息的客户端), 而是将信息发送给频道(channel), 然后由频道将信息转发给所有对这个频道感兴趣的订阅者。SUBSCRIBE 、 UNSUBSCRIBE 和 PUBLISH 三个命令实现了消息的发布与订阅。如下
Client1发布频道mychannel与消息
192.168.2.129:6379> publish mychannel "message from channel1"
(integer) 1
192.168.2.129:6379>
Client2 订阅频道mychannel并接受Client1通过频道发过来的消息
192.168.2.129:6379> subscribe mychannel
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "mychannel"
3) (integer) 1
1) "message"
2) "mychannel"
3) "message from channel1"
至此,redis的客户端安全性设置、主从复制、事务与锁、持久化机制以及发布与订阅消息主要内容介绍完毕。下一篇我们将继续学习redis的集群。
参考文档
http://redis.io/documentation
http://redisdoc.com/
以上是关于Redis高级(持久化--redis主从架构--redis哨兵模式--redis分片集群)的主要内容,如果未能解决你的问题,请参考以下文章
分布式缓存技术redis学习系列——redis高级应用(主从事务与锁持久化)
Redis主从与哨兵架构详解 Redis主从架构 如何在同一台机器搭建主从架构 Redis主从工作原理 数据部分复制 Jedis使用 Redis的管道(Pipeline) Redis Lua脚本(代码