Redis配置
1.虚拟内存vm-enabled 为 no
Redis的VM选项,即虚拟内存功能,VM是作为Redis存储超出物理内存数据的一种数据在内存与磁盘换入换出的一个持久化策略,但是其内存管理成本也非常的高,所以要关闭VM功能,将redis.conf文件中vm-enabled为no。
2.内存配置:maxmemory
redis.conf中的maxmemory选项是告诉Redis当使用了多少物理内存后就开始拒绝后续的写入请求,该参数能很好的保护好你的Redis不会因为使用了过多的物理内存而导致swap,最终严重影响性能甚至崩溃。不要让Redis所在机器物理内存使用超过实际内存总量的3/5。重写AOF文件和RDB文件的进程(即使不做持久化,复制到Slave的时候也要写RDB)会fork出一条新进程来,采用了操作系统的Copy-On-Write策略(如果父进程的内存没被修改,子进程与父进程共享Page。如果父进程的Page被修改, 会复制一份改动前的内容给新进程),留意Console打出来的报告,如"RDB: 1215 MB of memory used by copy-on-write"。在系统极度繁忙时,如果父进程的所有Page在子进程写RDB过程中都被修改过了,就需要两倍内存。
3.系统参数配置:vm.overcommit_memory = 1
启动Redis时,如果看到如下警告:
WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
为了调整内存分配策略,需要配置overcommit_memory
0:表示内核将检查是否有足够的可用内存供应用进程使用;如果有足够的可用内存,内存申请允许;否则,内存申请失败,并把错误返回给应用进程。
1:表示内核允许分配所有的物理内存,而不管当前的内存状态如何。
2:表示内核允许分配超过所有物理内存和交换空间总和的内存
默认为0,如果内存情况比较紧张的话,设为1。按照Redis启动时的提醒,设置 vm.overcommit_memory = 1 ,使得fork()一条10G的进程时,因为COW策略而不一定需要有10G的free memory.
3.输出缓冲区buffer大小限制
对于Redis的输出(也就是命令的返回值)来说,其大小经常是不可控的,可能是一个简单的命令,能够产生体积庞大的返回数据。另外也有可能因为执行命令太多,产生的返回数据的速率超过了往客户端发送的速率,这时也会产生消息堆积,从而造成输出缓冲区越来越大,占用过多内存,甚至导致系统崩溃。所以Redis设置了一些保护机制来避免这种情况的出现,这些机制作用于不同种类的客户端,有不同的输出缓冲区大小限制,限制方式有两种:
一种是大小限制,当某一个客户端的缓冲区超过某一大小时,直接关闭掉这个客户端连接;另一种是当某一个客户端的缓冲区持续一段时间占用空间过大时,也直接关闭掉客户端连接。对于不同客户端的策略如下:
1.对普通客户端来说,限制为0,也就是不限制,因为普通客户端通常采用阻塞式的消息应答模式,如:发送请求,等待返回,再发请求,再等待返回。这种模式通常不会导致输出缓冲区的堆积膨胀。
2.对于Pub/Sub 客户端来说,大小限制是32m,当输出缓冲区超过32m时,会关闭连接。持续性限制是,当客户端缓冲区大小持续60秒超过8m,也会导致连接关闭。
3.对于 Slave 客户端来说,大小限制是256m,持续性限制是当客户端缓冲区大小持续60秒超过64m时,关闭连接。
上面三种规则都是可配置的。可以通过 CONFIG SET 命令或者修改 redis.conf 文件来配置。对于pub/sub和slave客户端默认配置正常情况下不会有问题,但是当缓存数据量超过3g的时候如果触发主从同步,会带来灾难。详见后面缓冲区溢出异常场景描述。
5.持久化配置策略
高可用性关乎系统出错时到底会丢失多少数据,多久不能服务。为了保证高可用性,Redis提供了rdb和aof两种持久化方式,但是是否开启持久化,启用什么样的持久化策略应该根据业务需求来定。原则上:
1. Master最好不要做任何持久化工作,包括内存快照和AOF日志文件,特别是不要启用内存快照做持久化。Master写内存快照,save命令调度rdbSave函数,会阻塞主线程的工作,当快照比较大时对性能影响是非常大的,会间断性暂停服务,所以Master最好不要写内存快照。
2.如果数据比较关键,某个Slave开启AOF备份数据,策略为每秒同步一次。
3.当业务场景不需要数据持久化时,关闭所有的持久化方式可以获得最佳的性能以及最大的内存使用量。
4.如果需要使用持久化,根据是否可以容忍重启丢失部分数据在快照方式与语句追加方式之间选择其一。
5.Master AOF持久化,如果不重写AOF文件,这个持久化方式对性能的影响是最小的,但是AOF文件会不断增大,AOF文件过大会影响Master重启的恢复速度。另外,Master调用BGREWRITEAOF重写AOF文件,AOF在重写的时候会占大量的CPU和内存资源,导致服务load过高,出现短暂服务暂停现象。将no-appendfsync-on-rewrite的配置设为yes可以缓解这个问题,设置为yes表示rewrite期间对新写操作不fsync,暂时存在内存中,等rewrite完成后再写入。最好是不开启Master的AOF备份功能。
- 持久化RDB
1.RDB可在指定的时间间隔内生成数据集的时间点快照(point-in-time snapshot),可以配置复合的快照触发条件持久化
2.RDB写入时连内存一起Fork出一个新进程,遍历新进程内存中的数据,解决了Snapshot过程中又有新写入请求问题。
3.RDB会先写到临时文件,完了再Rename,这样外部程序对RDB文件的备份和传输过程是安全的。
优点:RDB的启动时间会更短:
1.RDB文件中每一条数据只有一条记录,不会像AOF日志那样可能有一条数据的多次操作记录。所以每条数据只需要写一次就行了。
2.RDB文件的存储格式和Redis数据在内存中的编码格式是一致的,无须再进行数据编码工作,所以CPU消耗要远小于AOF日志加载。
缺点:数据丢失,一旦数据库出现问题,从上次RDB文件生成到Redis停机这段时间的数据全部丢掉了。
- 持久化AOF文件
AOF在写入内存数据的同时将操作命令保存到日志文件(Redis协议纯文本),通过重新执行这些命令来还原数据集。
通过appendfsync选项来控制AOF文件何时调用fsync将其写到磁盘上:appendfsync no:appendfsync everysec:appednfsync always:
优点:对数据安全性要求极高的应用,无法容忍数据丢失的应用
缺点:在写入内存数据的同时将操作命令保存到日志文件,性能损耗大,维护成本非常高,恢复重建时间会非常长。
通用策略:用Replication机制来弥补aof、snapshot性能上的不足,达到了数据可持久化的目的。
1.Master上Snapshot和AOF都不做,来保证Master的读写性能
2.而Slave上则同时开启Snapshot和AOF来进行持久化,保证数据的安全性
- 数据恢复:当redis服务器挂掉时,重启时将按以下优先级恢复数据到内存:
1.如果只配置了AOF,重启时加载AOF文件恢复数据.
2.如果同时配置了RDB和AOF,启动时只加载AOF文件恢复数据.
3.如果只配置了RDB,启动时将加载dump文件恢复数据.
若可以忍受数据丢失,只启用RDB即可;若对数据很敏感,可以同时启用RDB和AOF;不建议只启用AOF,因为一旦如果AOF文件损坏或AOF解析引擎存在bug,整个数据集都无法重建。
持久化案例1:子进程持久化时,子进程的write和主进程的fsync冲突造成阻塞。在开启了AOF持久化的结点上,当子进程执行AOF重写或者RDB持久化时,出现了Redis查询卡顿甚至长时间阻塞的问题, 此时, Redis无法提供任何读写操作。
原因分析:
Redis 服务设置了 appendfsync everysec, 主进程每秒钟便会调用 fsync(), 要求内核将数据”确实”写到存储硬件里. 但由于服务器正在进行大量IO操作, 导致主进程 fsync()/操作被阻塞, 最终导致 Redis 主进程阻塞.
redis.conf中是这么说的:
When the AOF fsync policy is set to always or everysec, and a background saving process (a background save or AOF log background rewriting) is performing a lot of I/O against the disk, in some Linux configurations Redis may block too long on the fsync() call. Note that there is no fix for this currently, as even performing fsync in a different thread will block our synchronous write(2) call.
当执行AOF重写时会有大量IO,这在某些Linux配置下会造成主进程fsync阻塞;
解决方案:
设置 no-appendfsync-on-rewrite yes, 在子进程执行AOF重写时, 主进程不调用fsync()操作;注意, 即使进程不调用 fsync(), 系统内核也会根据自己的算法在适当的时机将数据写到硬盘(Linux 默认最长不超过
30 秒).
这个设置带来的问题是当出现故障时,最长可能丢失超过30秒的数据,而不再是1秒;
案例2:AOF重写完成后合并数据时造成的阻塞
在bgrewriteaof过程中,所有新来的写入请求依然会被写入旧的AOF文件,同时放到AOF buffer中,当rewrite完成后,会在主线程把这部分内容合并到临时文件中之后才rename成新的AOF文件,所以rewrite过程中会不断打印"Background AOF buffer size: 80 MB, Background AOF buffer size: 180 MB",要监控这部分的日志。这个合并的过程是阻塞的,如果产生了280MB的buffer,在100MB/s的传统硬盘上,Redis就要阻塞2.8秒;
解决方案:
将硬盘设置的足够大,将AOF重写的阈值调高,保证高峰期间不会触发重写操作;在闲时使用crontab 调用AOF重写命令。
6.主从配置策略
1.为了主从复制的速度和连接的稳定性,Slave和Master最好在同一个局域网内。
2.尽量避免在压力较大的主库上增加从库。
3.为了Master的稳定性,主从复制不要用图状结构,用单向链表结构更稳定,即主从关系 为:Master<--Slave1<--Slave2<--Slave3.......,这样的结构也方便解决单点故障问题,实现 Slave对Master的替换,也即,如果Master挂了,可以立马启用Slave1做Master,其他不变。
- Master-Slave复制
1.先执行一次全同步 — 请求master BgSave出自己的一个RDB Snapshot文件发给slave,slave接收完毕后,清除掉自己的旧数据,然后将RDB载入内存。
2.再进行增量同步— master作为一个普通的client连入slave,将所有写操作转发给slave,没有特殊的同步协议
2.8开始,支持持续复制处理方式代替采用全量同步。Master端为复制流维护一个内存缓冲区(in-memory backlog),记录最近发送的复制流命令;同时,Master和Slave之间都维护一个复制偏移量(replication offset)和当前Master服务器ID(Master run id)。当网络断开,Slave尝试重连时:
a. 如果MasterID相同(即仍是断网前的Master服务器),并且从断开时到当前时刻的历史命令依然在Master的内存缓冲区中存在,则Master会将缺失的这段时间的所有命令发送给Slave执行,然后复制工作就可以继续执行了;
b. 否则,依然需要全量复制操作;