Redis-集群模式&哨兵模式介绍&持久化
Redis是什么
redis是一款开源的基于内存的高性能key-value数据库,常用于存储热数据的缓存数据库。
Redis的键值可以包括字符串(strings)类型,同时它还包括哈希(hashes)、列表(lists)、集合(sets)和 有序集合(sorted sets)等数据类型。 对于这些数据类型,你可以执行原子操作。例如:对字符串进行附加操作(append);递增哈希中的值;向列表中增加元素;计算集合的交集、并集与差集等。为了获得优异的性能,Redis采用了内存中(in-memory)数据集(dataset)的方式。
Redis支持数据的持久化,你可以每隔一段时间将数据集转存到磁盘上(snapshot),或者在日志尾部追加每一条操作命令(append only file,aof)。Redis同样支持主从复制(master-slave replication),并且具有非常快速的非阻塞首次同步( non-blocking first synchronization)、网络断开自动重连等功能。同时Redis还具有其它一些特性,其中包括简单的事物支持、发布订阅 ( pub/sub)、管道(pipeline)和虚拟内存(vm)等 。
Redis具有丰富的客户端,支持现阶段流行的大多数编程语言。
Redis的安装配置
Redis的安装
下载最新稳定版 redis( http://redis.io/download )
- tar zxvf redis-2.2.11 解压缩
- cd src 进入src目录
- make 编译Redis
- make test 可以测试一下(本步可省略)
- make install 安装,默认安装目录是 /usr/local/bin,生成如图5个二进制文件,可以将其拷到新建目录下,例如: /usr/local/redis/bin[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CRU9m1oA-1588994524727)(en-resource://database/580:1)]
- cp 源码/src/redis.conf /usr/local/redis/etc 配置文件复制
- cd /usr/local/redis
*./bin/redis-server ./etc/redis.conf 启动Redis服务
此时redis已经运行,但要获得好的性能,还需要对配置文件进行合理的配置
Redis的配置文件
1 |
|
- Redis默认不是以守护进程的方式运行,可以通过该配置项修改,使用yes启用守护进程
daemonize no - 当Redis以守护进程方式运行时,Redis默认会把pid写入/var/run/redis.pid文件,可以通过pidfile指定
pidfile /var/run/redis.pid - 指定Redis监听端口,默认端口为6379,作者在自己的一篇博文中解释了为什么选用6379作为默认端口,因为6379在手机按键上MERZ对应的号码,而MERZ取自意大利歌女Alessia Merz的名字
port 6379 - 绑定的主机地址
bind 127.0.0.1 - 当 客户端闲置多长时间后关闭连接,如果指定为0,表示关闭该功能
timeout 300 - 指定日志记录级别,Redis总共支持四个级别:debug、verbose、notice、warning,默认为verbose
loglevel verbose7. 日志记录方式,默认为标准输出,如果配置Redis为守护进程方式运行,而这里又配置为日志记录方式为标准输出,则日志将会发送给/dev/null
logfile stdout - 设置数据库的数量,默认数据库为0,可以使用SELECT
命令在连接上指定数据库id
databases 16 - 指定在多长时间内,有多少次更新操作,就将数据同步到数据文件,可以多个条件配合
save
Redis默认配置文件中提供了三个条件:
save 900 1
save 300 10
save 60 10000
分别表示900秒(15分钟)内有1个更改,300秒(5分钟)内有10个更改以及60秒内有10000个更改。 - 指定存储至本地数据库时是否压缩数据,默认为yes,Redis采用LZF压缩,如果为了节省CPU时间,可以关闭该选项,但会导致数据库文件变的巨大
rdbcompression yes11. 指定本地数据库文件名,默认值为dump.rdb
dbfilename dump.rdb - 指定本地数据库存放目录
dir ./ - 设置当本机为slav服务时,设置master服务的IP地址及端口,在Redis启动时,它会自动从master进行数据同步
slaveof - 当master服务设置了密码保护时,slav服务连接master的密码
masterauth - 设置Redis连接密码,如果配置了连接密码,客户端在连接Redis时需要通过AUTH
命令提供密码,默认关闭
requirepass foobared - 设置同一时间最大客户端连接数,默认无限制,Redis可以同时打开的客户端连接数为Redis进程可以打开的最大文件描述符数,如果设置 maxclients 0,表示不作限制。当客户端连接数到达限制时,Redis会关闭新的连接并向客户端返回max number of clients reached错误信息
maxclients 12817. 指定Redis最大内存限制,Redis在启动时会把数据加载到内存中,达到最大内存后,Redis会先尝试清除已到期或即将到期的Key,当此方法处理 后,仍然到达最大内存设置,将无法再进行写入操作,但仍然可以进行读取操作。Redis新的vm机制,会把Key存放内存,Value会存放在swap区
maxmemory - 指定是否在每次更新操作后进行日志记录,Redis在默认情况下是异步的把数据写入磁盘,如果不开启,可能会在断电时导致一段时间内的数据丢失。因为 redis本身同步数据文件是按上面save条件来同步的,所以有的数据会在一段时间内只存在于内存中。默认为no
appendonly no - 指定更新日志文件名,默认为appendonly.aof
appendfilename appendonly.aof - 指定更新日志条件,共有3个可选值: no:表示等操作系统进行数据缓存同步到磁盘(快) always:表示每次更新操作后手动调用fsync()将数据写到磁盘(慢,安全) everysec:表示每秒同步一次(折衷,默认值)
appendfsync everysec21. 指定是否启用虚拟内存机制,默认值为no,简单的介绍一下,VM机制将数据分页存放,由Redis将访问量较少的页即冷数据swap到磁盘上,访问多的页面由磁盘自动换出到内存中(在后面的文章我会仔细分析Redis的VM机制)
vm-enabled no - 虚拟内存文件路径,默认值为/tmp/redis.swap,不可多个Redis实例共享
vm-swap-file /tmp/redis.swap - 将所有大于vm-max-memory的数据存入虚拟内存,无论vm-max-memory设置多小,所有索引数据都是内存存储的(Redis的索引数据 就是keys),也就是说,当vm-max-memory设置为0的时候,其实是所有value都存在于磁盘。默认值为0
vm-max-memory 0 - Redis swap文件分成了很多的page,一个对象可以保存在多个page上面,但一个page上不能被多个对象共享,vm-page-size是要根据存储的 数据大小来设定的,作者建议如果存储很多小对象,page大小最好设置为32或者64bytes;如果存储很大大对象,则可以使用更大的page,如果不 确定,就使用默认值
vm-page-size 32 - 设置swap文件中的page数量,由于页表(一种表示页面空闲或使用的bitmap)是在放在内存中的,,在磁盘上每8个pages将消耗1byte的内存。
vm-pages 13421772826. 设置访问swap文件的线程数,最好不要超过机器的核数,如果设置为0,那么所有对swap文件的操作都是串行的,可能会造成比较长时间的延迟。默认值为4
vm-max-threads 4 - 设置在向客户端应答时,是否把较小的包合并为一个包发送,默认为开启
glueoutputbuf yes - 指定在超过一定的数量或者最大的元素超过某一临界值时,采用一种特殊的哈希算法
hash-max-zipmap-entries 64
hash-max-zipmap-value 512 - 指定是否激活重置哈希,默认为开启
activerehashing yes - 指定包含其它的配置文件,可以在同一主机上多个Redis实例之间使用同一份配置文件,而同时各个实例又拥有自己的特定配置文件
include /path/to/local.conf
Redis的集群
主从模式
主从的配置
*主从中主的配置文件不需要变化,仅更改从的配置文件即可
1 |
|
主从同步的方法
增量同步
redis 同步的是指令流,主节点会将那些对自己的状态产生修改性影响的指令记录在本地的内存 buffer 中,然后异步将 buffer 中的指令同步到从节点,从节点一边执行同步的指令流来达到和主节点一样的状态,一边向主节点反馈自己同步到哪里了 (偏移量,这是redis-2.8之后才有的特性)。从节点同步数据的时候不会影响主节点的正常工作,也不会影响自己对外提供读服务的功能,从节点会用旧的数据来提供服务,当同步完成后,需要删除旧数据集,加载新数据,这个时候才会暂停对外服务。
因为内存的 buffer 是有限的,所以 redis 主节点不能将所有的指令都记录在内存 buffer 中。redis 的复制内存 buffer 是一个定长的环形数组,如果数组内容满
了,就会从头开始覆盖前面的内容。
快照同步
如果节点间网络通信不好,那么当从节点同步的速度不如主节点接收新写请求的速度时,buffer 中会丢失一部分指令,从节点中的数据将与主节点中的数据不一致,此时将会触发快照同步。
快照同步是一个非常耗费资源的操作,它首先需要在主节点上进行一次 bgsave 将当前内存的数据全部快照到RDB文件中,然后再将快照文件的内容全部传送到从节点。从节点将RDB文件接受完毕后,立即执行一次全量加载,加载之前先要将当前内存的数据清空。加载完毕后通知主节点继续进行增量同步。
在整个快照同步进行的过程中,主节点的复制 buffer 还在不停的往前移动,如果快照同步的时间过长或者复制 buffer 太小,都会导致同步期间的增量指令在复制 buffer 中被覆盖,这样就会导致快照同步完成后无法进行增量复制,然后会再次发起快照同步,如此极有可能会陷入快照同步的死循环。所以需要配置一个合适的复制 buffer 大小参数,避免快照复制的死循环。
哨兵模式
哨兵模式是一种特殊的模式,首先Redis提供了哨兵的命令,哨兵是一个独立的进程,作为进程,它会独立运行。其原理是哨兵通过发送命令,等待Redis服务器响应,从而监控运行的多个Redis实例。
数据的获取 客户端连接哨兵,哨兵反馈redis的连接信息
这里的哨兵有两个作用
- 发送命令,让Redis服务器返回监控其运行状态,包括主服务器和从服务器。
- 兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机。
然而一个哨兵进程对Redis服务器进行监控,可能会出现问题,为此,我们可以使用多个哨兵进行监控。各个哨兵之间还会进行监控,这样就形成了多哨兵模式。
用文字描述一下故障切换(failover)的过程。假设主服务器宕机,哨兵1先检测到这个结果,系统并不会马上进行failover过程,仅仅是哨兵1主观的认为主服务器不可用,这个现象成为主观下线。当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行failover操作。切换成功后,就会通过发布订阅模式,让各个哨兵把自己监控的从服务器实现切换主机,这个过程称为客观下线。这样对于客户端而言,一切都是透明的。
哨兵模式的配置文件
1 |
|
每个redis都要配置一个哨兵
每个哨兵的配置都是一样的。在Redis安装目录下有一个sentinel.conf文件,copy一份进行修改
1 |
|
Cluster集群
概况
今天我们来简单介绍下,Redis的官方多机部署方案,Redis Cluster。一组Redis Cluster是由多个Redis实例组成,官方推荐我们使用6实例,其中3个为主节点,3个为从结点。一旦有主节点发生故障的时候,Redis Cluster可以选举出对应的从结点成为新的主节点,继续对外服务,从而保证服务的高可用性。那么对于客户端来说,知道知道对应的key是要路由到哪一个节点呢?原来,Redis Cluster 把所有的数据划分为16384个不同的槽位,可以根据机器的性能把不同的槽位分配给不同的Redis实例,对于Redis实例来说,他们只会存储部门的Redis数据,当然,槽的数据是可以迁移的,不同的实例之间,可以通过一定的协议,进行数据迁移。
数据获取
客户端是如何访问Redis Cluster里面的数据呢?首先客户端需要保存一份Redis Cluster槽相关的信息,也就是路由表,然后对即将访问的key进行哈希计算,计算出对应的槽位,然后向对应的Redis实例发起查询请求。如果访问的Redis实例中,的确保存着对应槽的数据信息,就会进行返回,否则向客户端返回一个Moved指令,让客户端到正确的地址进行获取。
数据迁移
在分布式系统中,衡量一个系统好坏的一项重要指标,系统的扩展性。什么是系统的扩展性呢?就是今天你又10万个用户,需要4台机器进行服务,如果明天的用户数量增加到20万了,是不是只要简单的增加4台机器就行,同时又不需要进行复杂的数据迁移。Redis Cluster便是如此,当你新增一些实例的时候,只需要将一部分槽位迁移到新的实例即可。在迁移的过程中,客户端会先去旧的实例上去查询数据,因为迁移正在发生,如果对应的数据还在本机上,那么直接返回,否则返回让客户端重定向到新的实例。客户端先向新的机器发起ask指令,新实例返回成功后,再一次查询最终的结果。
Redis的持久化
RDB持久化
RDB持久化即通过创建快照(压缩的二进制文件)的方式进行持久化,保存某个时间点的全量数据。RDB持久化是Redis默认的持久化方式。RDB持久化的触发包括手动触发与自动触发两种方式。
手动触发
*save,命令行执行save命令,将以同步的方式创建rdb文件保存快照,会阻塞服务器的主进程,生产环境中不要用
*bgsave, 在命令行执行bgsave命令,将通过fork一个子进程以异步的方式创建rdb文件保存快照,除了fork时有阻塞,子进程在创建rdb文件时,主进程可继续处理请求
自动触发
*在redis.conf中配置 save m n 定时触发,如 save 900 1表示在900s内至少存在一次更新就触发
*主从复制时,如果从节点执行全量复制操作,主节点自动执行bgsave生成RDB文件并发送给从节点
*执行debug reload命令重新加载Redis时
*执行shutdown且没有开启AOF持久化
redis.conf中RDB持久化配置#
1 |
|
AOF持久化
AOF(Append-Only-File)持久化即记录所有变更数据库状态的指令,以append的形式追加保存到AOF文件中。在服务器下次启动时,就可以通过载入和执行AOF文件中保存的命令,来还原服务器关闭前的数据库状态。
redis.conf中AOF持久化配置如下
1 |
|
AOF持久化的实现包括3个步骤:命令追加:
- 将命令追加到AOF缓冲区文件写入:
- 缓冲区内容写到AOF文件文件保存:
- AOF文件保存到磁盘
其中后两步的频率通过appendfsync来配置,appendfsync的选项包括
always, 每执行一个命令就保存一次,安全性最高,最多只丢失一个命令的数据,但是性能也最低(频繁的磁盘IO)
everysec,每一秒保存一次,推荐使用,在安全性与性能之间折中,最多丢失一秒的数据
no, 依赖操作系统来执行(一般大概30s一次的样子),安全性最低,性能最高,丢失操作系统最后一次对AOF文件触发SAVE操作之后的数据
AOF通过保存命令来持久化,随着时间的推移,AOF文件会越来越大,Redis通过AOF文件重写来解决AOF文件不断增大的问题(可以减少文件的磁盘占有量,加快数据恢复的速度),原理如下:
- 调用fork,创建一个子进程子进程
- 读取当前数据库的状态来“重写”一个新的AOF文件(这里虽然叫“重写”,但实际并没有对旧文件进行任何读取,而是根据数据库的当前状态来形成指令)
- 主进程持续将新的变动同时写到AOF重写缓冲区与原来的AOF缓冲区中
- 主进程获取到子进程重写AOF完成的信号,调用信号处理函数将AOF重写缓冲区内容写入新的AOF文件中,并对新文件进行重命名,原子地覆盖原有AOF文件,完成新旧文件的替换
AOF的重写也分为手动触发与自动触发
- 手动触发: 直接调用bgrewriteaof命令
- 自动触发: 根据auto-aof-rewrite-min-size和auto-aof-rewrite-percentage参数确定自动触发时机。其中auto-aof-rewrite-min-size表示运行AOF重写时文件最小体积,默认为64MB。auto-aof-rewrite-percentage表示当前AOF文件大小(aof_current_size)和上一次重写后AOF文件大小(aof_base_size)的比值。自动触发时机为 aof_current_size > auto-aof-rewrite-min-size &&(aof_current_size - aof_base_size)/aof_base_size> = auto-aof-rewrite-percentage
数据库的恢复
服务器启动时,如果没有开启AOF持久化功能,则会自动载入RDB文件,期间会阻塞主进程。如果开启了AOF持久化功能,服务器则会优先使用AOF文件来还原数据库状态,因为AOF文件的更新频率通常比RDB文件的更新频率高,保存的数据更完整。
redis数据库恢复的处理流程如下,[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s3N3cOKM-1588994524747)(en-resource://database/587:1)]
在数据恢复方面,RDB的启动时间会更短,原因有两个:
RDB 文件中每一条数据只有一条记录,不会像AOF日志那样可能有一条数据的多次操作记录。所以每条数据只需要写一次就行了,文件相对较小。
RDB 文件的存储格式和Redis数据在内存中的编码格式是一致的,不需要再进行数据编码工作,所以在CPU消耗上要远小于AOF日志的加载。
但是在进行RDB持久化时,fork出来进行dump操作的子进程会占用与父进程一样的内存,采用的copy-on-write机制,对性能的影响和内存的消耗都是比较大的。比如16G内存,Redis已经使用了10G,这时save的话会再生成10G,变成20G,大于系统的16G。这时候会发生交换,要是虚拟内存不够则会崩溃,导致数据丢失。所以在用redis的时候一定对系统内存做好容量规划。
RDB、AOF混合持久化
Redis从4.0版开始支持RDB与AOF的混合持久化方案。首先由RDB定期完成内存快照的备份,然后再由AOF完成两次RDB之间的数据备份,由这两部分共同构成持久化文件。该方案的优点是充分利用了RDB加载快、备份文件小及AOF尽可能不丢数据的特性。缺点是兼容性差,一旦开启了混合持久化,在4.0之前的版本都不识别该持久化文件,同时由于前部分是RDB格式,阅读性较低。
开启混合持久化
1 |
|
数据恢复加载过程就是先按照RDB进行加载,然后把AOF命令追加写入。
持久化方案的建议
- 如果Redis只是用来做缓存服务器,比如数据库查询数据后缓存,那可以不用考虑持久化,因为缓存服务失效还能再从数据库获取恢复。
- 如果你要想提供很高的数据保障性,那么建议你同时使用两种持久化方式。如果你可以接受灾难带来的几分钟的数据丢失,那么可以仅使用RDB。
- 通常的设计思路是利用主从复制机制来弥补持久化时性能上的影响。即Master上RDB、AOF都不做,保证Master的读写性能,而Slave上则同时开启RDB和AOF(或4.0以上版本的混合持久化方式)来进行持久化,保证数据的安全性
虚拟内存
edis没有使用os提供的虚拟内存机制而是自己实现了自己的虚拟内存机制 ,但是思路和目的都是相同的。就是暂时把不经常访问的数据从内存交换到磁盘中,从而腾出内存空间用于其他需要访问的数据。尤其是对于redis这样的内存数据库,内存总是不够用的。除了可以将数据分割到多个redis server外。另外的能够提高数据库容量的办法就是使用vm把那些不经常访问的数据交换的磁盘上。如果我们的存储的数据总是有少部分数据被经常访问,大 部分数据很少被访问,对于网站来说确实总是只有少量用户经常活跃。当少量数据被经常访问时,使用vm不但能提高单台redis server数据库的容量,而且也不会对性能造成太多影响。
1 |
|
Redis的常用命令
- 登录 redis-cli -p 5566 -a password
- 检查key是否存在 EXISTS key
- 搜索某关键字 KSYS *4
返回一个Key所影响的vsl的类型 TYPE key
String
设置一个键的值 SET key value
获取一个建的值 GET key
删除键对 DEL key
同时获取多个 mget key1 key2
Hash
设置一个hash HMSET key valueKey value –<key,<valueKey,value>>
获取hash所有 key&value HGETALL key
获取hash所有 key HKEYS key
获取hash所有 keu的vslue HVALS key
获取hash内键值对的长度 HLEN key
给一个hash的某个键值对赋值 HSET key valueKey value
当hash中valueKey不存在时赋值 HSETNX key valueKey value
List
给list赋值 LPUSH listName value
按照索引取值 LINDEX listName 1