Redis优化

架构选择
sentinel
Redis实例的监控管理、通知和实例失效备援服务,是 Redis 集群的管理工具。在一般的分布式中心节点数据库中, Redis-sentinel 的作用是中心节点的工作,监控各个其他节点的工作情况并且进行故障恢复,来提高集群的高可用性。
 
redis cluster
cluster 3.0自带的集群,特点在于他的分布式算法不是一致性 hash ,而是 hash 槽的概念,以及自身支持节点设置从节点。具体看官方文档介绍
twemproxy
它类似于一个代理方式,使用方法和普通redis无任何区别,设置好它下属的多个 redis 实例后,使用时在本需要连接 redis 的地方改为连接 twemproxy ,它会以一个代理的身份接收请求并使用一致性 hash 算法,将请求转接到具体 redis ,将结果再返回 twemproxy 。使用方式简便 ( 相对 redis 只需修改连接端口 ) ,对旧项目扩展的首选问题: twemproxy 自身单端口实例的压力,使用一致性 hash 后,对 redis 节点数量改变时候的 计算值的改变,数据无法自动移动到新的节点。
codis
基本和twemproxy一致的效果,但它支持在 节点数量改变情况下,旧节点数据可恢复到新 hash 节点
 
 
 
配置优化
Linux 配置优化
vm.overcommit_memory
· Redis 设置合理的 maxmemory ,保证机器有 20%~30% 的闲置内存。·集中化管理 AOF 重写和 RDB bgsave 。·设置 vm.overcommit_memory=1 ,防止极端情况下会造成 fork 失败。
 
vm.swapniess
OOM( OutOfMemory killer 机制是指 Linux 操作系统发现可用内存不足时,强制杀死一些用户进程(非内核进程),来保证系统有足够的可用内存进行分配。
如果Linux>3.5, vm.swapniess=1 ,否则 vm.swapniess=0 ,从而实现如下两个目标:
·物理内存充足时候,使 Redis 足够快。·物理内存不足时候,避免 Redis 死掉(如果当前 Redis 为高可用,死掉比阻塞更好)。
 
TransparentHugePages
Linuxkernel在 2.6.38 内核增加了 THP 特性,支持大内存页( 2MB )分配,默认开启。当开启时可以降低 fork 子进程的速度,但 fork 操作之后,每个内存页从原来 4KB 变为 2MB ,会大幅增加重写期间父进程内存,同时每次写命令引起的复制内存页单位放大了 512 倍,会拖慢写操作的执行时间,导致大量写操作慢查询,例如简单的 incr 命令也会出现在慢查询中。因此 Redis 日志中建议将此特性进行禁用,
 
OOMkiller
OOMkiller进程会为每个用户进程设置一个权值,这个权值越高,被“下手”的概率就越高,反之概率越低。每个进程的权值存放在 /proc/{progress_id}/oom_score 中,这个值是受 /proc/{progress_id}/oom_adj 的控制,对于 Redis 所在的服务器来说,可以将所有 Redis oom_adj 设置为最低值或者稍小的值,降低被 OOMkiller 杀掉的概率

NTP( NetworkTimeProtocol
NTP( NetworkTimeProtocol ,网络时间协议)是一种保证不同机器时钟一致性的服务。我们知道像 RedisSentinel RedisCluster 这两种功能需要多个 Redis 节点的类型,可能会涉及多台服务器。虽然 Redis 并没有对多个服务器的时钟有严格要求,但是假如多个 Redis 实例所在的服务器时钟不一致,对于一些异常情况的日志排查是非常困难的,为此我们可以每天定时去同步一次系统时间,从而使得集群中的时间保持统一。
Ulimit
在Linux中,可以通过 ulimit 查看和设置系统当前用户进程的资源数。其中 ulimit-a 命令包含的 openfiles 参数,是单个用户同时打开的最大文件个数:
Redis建议把 openfiles 至少设置成 10032 ,因为 maxclients 默认是 10000 ,这些是用来处理客户端连接的,除此之外, Redis 内部会使用最多 32 个文件描述符,所以 
TCPbacklog
Redis默认的 tcp-backlog 值为 511 ,可以通过修改配置 tcp-backlog 进行调整,如果 Linux tcp-backlog 小于 Redis 设置的 tcp-backlog

Redis 配置
总体配置
tcp-backlog
在高并发的环境下,你需要把这个值调高以避免客户端连接缓慢的问题。
Linux 内核会一声不响的把这个值缩小成 /proc/sys/net/core/somaxconn 对应的值, 所以你要修改这两个值才能达到你的预期。
lua-time-limit
一个Lua脚本最长的执行时间,单位为毫秒,如果为 0 或负数表示无限执行时间,默认为 5000

客户端相关配置
client-output-buffer-limit
一般无需调整
客户端输出缓冲区的配置,对于Redis服务器的输出(也就是命令的返回值)来说,其大小通常是不可控制的。有可能一个简单的命令,能够产生体积庞大的返回数据。另外也有可能因为执行了太多命令,导致产生返回数据的速率超过了往客户端发送的速率,这是也会导致服务器堆积大量消息,从而导致输出缓冲区越来越大,占用过多内存,甚至导致系统崩溃。
timeout
指定在一个 client 空闲多少秒之后关闭连接,建议设置为 0 ,不然开发时如果不检测连接有可能抛出 JedisConnectionException , 并且 提示 Unexpected end of stream
maxclients:
客户 端 最大 连接 数
tcp- keepalive:
检测 TCP 连接 活性 的 周期那么 Redis 会 每隔 60 秒 对 它 创建 的 TCP 连接 进行 活性 检测, 防止 大量 死 连接 占用 系统 资源。
 
内存配置
Maxmemory
Redis 最大内存,一般推荐 Redis 设置内存为最大物理内存的四分之三
maxmemory-policy
内存不够时的淘汰策略,建议设置为volatile-lru
volatile-lru -> 根据 LRU 算法生成的过期时间来删除。
allkeys-lru -> 根据 LRU 算法删除任何 key
volatile-random -> 根据过期设置来随机删除 key
allkeys->random -> 无差别随机删。
volatile-ttl -> 根据最近过期时间来删除(辅以 TTL
noeviction -> 谁也不删,直接在写操作时返回错误。
maxmemory-samples
是说每次进行淘汰的时候 会随机抽取几个key 从里面淘汰最不经常使用的

Aof 相关配置
appendonly
是否开启AOF,默认关闭( no )  
appendfsync
 aof刷写频率
 always  只要有新添加的数据就 fsync
 everysec  支持延迟 fsync
 no      不需要 fsync
appendfsync-on-rewrite
如果该参数设置为no,是最安全的方式,不会丢失数据,但是要忍受阻塞的问题。如果设置为 yes 呢这就相当于将 appendfsync 设置为 no ,这说明并没有执行磁盘操作,只是写入了缓冲区,因此这样并不会造成阻塞
auto-aof-rewrite-percentage
指定Redis重写 aof 文件的条件,默认为 100 ,表示与上次 rewrite aof 文件大小相比,当前 aof 文件增长量超过上次 afo 文件大小的 100% 时,就会触发 background rewrite 。若配置为 0 ,则会禁用自动
rewriteauto-aof-rewrite-min-size
指定触发rewrite的 aof 文件大小。若 aof 文件小于该值,即使当前文件的增量比例达到 auto-aof-rewrite-percentage 的配置值,也不会触发自动 rewrite 。即这两个配置项同时满足时,才会触发 rewrite
aof-rewrite-incremental-fsync
aof rewrite过程中 , 是否采取增量文件同步策略 , 默认为“ yes ”。 rewrite 过程中 , 32M 数据进行一次文件同步 , 这样可以减少 aof 大文件写入对磁盘的操作次数

Rdb 配置
Save
设置数据保存到数据文件中的save规则
save 900 1     #900秒时间,至少有一条数据更新,则保存到数据文件中
save 300 10    #300秒时间,至少有 10 条数据更新,则保存到数据文件中
save 60 10000  #60秒时间,至少有 10000 条数据更新,则保存到数据文件中
rdbcompression
指定存储至本地数据库时是否压缩数据,默认是yes, redis 采用 LZF 压缩,如果为了节省 CPU 时间,可以关闭该选项,但会导致数据库文件扁的巨大
stop-writes-on-bgsave-error   
当硬盘因为权限等原因无法写入时,停止写入
rdbchecksum
对rdb文件进行校验

慢查询配置
slowlog-log-slower-than:
 单位微妙,指定redis执行命令的最大时间,超过将记录到慢查询日志中 , 不接受负值,如果设置为 0 ,每条命令都要记录到慢查询日志中 .
slowlog-max-len:
设置慢查询日志长度,如果慢查询日志已经到最大值,如果有新命令需要记录,就将最老那条记录删除.
latency-monitor-threshold
Redis 延迟监控,在定位问题时可以开启

数据结构优化配置
指定相应数据结构在以下阈值时会使用压缩存储,这个值无需过大,过大虽然可节省内存,但可能导致性能问题
hash-max-zipmap-entries 64
hash-max-zipmap-value 512
list-max-ziplist-entries 512
list-max-ziplist-value 64
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
set-max-intset-entries 512

复制相关配置
slaveof 复制的主节点 ip
repl-ping-slave-period  repl-timeout
slave会每隔 repl-ping-slave-period( 默认 10 )ping 一次 master ,如果超过 repl-timeout( 默认 60 ) 都没有收到响应,就会认为 Master 挂了。如果 Master 明明没挂但被阻塞住了也会报这个错。可以适当调大 repl-timeout
repl-backlog-size
过小,会导致主从节点拉复制失败,因为全量复制的时候,父节点的更新(应用更新,主动过期删除等)会临时存放在backlog中待全量复制完成后增量发到子节点,必须为此保留足够的空间。
repl-backlog-ttl
多久释放backlog,当确认 master 不再需要 slave 的时候,多久释放。 0 是永远不释放
slave-priority
当master不可用 ,Sentinel 会根据 slave 的优先级选举一个 master 。最低的优先级的 slave, 当选 master. 而配置成 0, 永远不会被选举。 ( 必须≥ 0) 。默认是 100
min-slaves-to-write和 min-slaves-max-lag
从服务器的数量少于min-slaves-to-write个,或者三个从服务器的延迟( lag )值都大于或等于 min-slaves-max-lag 秒时,主服务器将拒绝执行写命令
slave-serve-stale-data
当一个slave失去和 master 的连接,或者同步正在进行中, slave 的行为可以有两种表现:
1) 如果 slave-serve-stale-data 设置为 "yes" ( 默认值 ) slave 会继续响应客户端请求,
 可能是正常数据,或者是过时了的数据,也可能是还没获得值的空数据。
2) 如果 slave-serve-stale-data 设置为 "no" slave 会回复 " 正在从 master 同步
(SYNC with master in progress) " 来处理各种请求,除了 INFO SLAVEOF 命令。
slave-read-only
你可以配置salve实例是否接受写操作。可写的 slave 实例可能对存储临时数据比较有用
repl-disable-tcp-nodelay
 
是否在slave套接字发送 SYNC 之后禁用 TCP_NODELAY
如果你选择“ yes Redis 将使用更少的 TCP 包和带宽来向 slaves 发送数据。但是这将使数据传输到 slave 上有延迟, Linux 内核的默认配置会达到 40 毫秒
如果你选择了 "no" 数据传输到 salve 的延迟将会减少但要使用更多的带宽
repl-diskless-sync
1、支持 disk master 端将 RDB file 写到 disk ,稍后再传送到 slave 端;
2、无磁盘 diskless master 端直接将 RDB file 传到 slave socket ,不需要与 disk 进行交互。
无磁盘diskless方式适合磁盘读写速度慢但网络带宽非常高的环境。 默认不使用 diskless 同步方式
repl-diskless-sync-delay
无磁盘diskless方式在进行数据传递之前会有一个时间的延迟,以便 slave 端能够进行到待传送的目标队列中,这个时间默认是 5

安全相关配置
Requirepass
配置redis访问密码的参数
Bind 监听指定的网络接口
masterauth
 如果master端启用了密码保护( requirepass ),那 slave 端就需要配置此选项
设计开发运维

内存优化
redisObject
Redis存储的数据都使用 redisObject 来封装,包括 string hash list set zset 在内的所有数据类型。理解 redisObject 对内存优化非常有帮助,
· type 字段:表示当前对象使用的数据类型, Redis 主要支持 5 种数据类型: string hash list set zset 。可以使用 type{key} 命令查看对象所属类型, type 命令返回的是值对象类型,键都是 string 类型。
· encoding 字段:表示 Redis 内部编码类型, encoding Redis 内部使用,代表当前对象内部采用哪种数据结构实现。理解 Redis 内部编码方式对于优化内存非常重要,同一个对象采用不同的编码实现内存占用存在明显差异。
· lru 字段:记录对象最后一次被访问的时间,当配置了 maxmemory maxmemory-policy=volatile-lru 或者 allkeys-lru 时,用于辅助 LRU 算法删除键数据。可以使用 objectidletime{key} 命令在不更新 lru 字段情况下查看当前键的空闲时间。
· refcount 字段:记录当前对象被引用的次数,用于通过引用次数回收内存,当 refcount=0 时,可以安全回收当前对象空间。使用 objectrefcount{key} 获取当前对象引用。当对象为整数且范围在 [0-9999] 时, Redis 可以使用共享对象的方式来节省内存。
· *ptr 字段:与对象的数据内容相关,如果是整数,直接存储数据;否则表示指向数据的指针。 Redis 3.0 之后对值对象是字符串且长度 <=39 字节的数据,内部编码为 embstr 类型,字符串 sds redisObject 一起分配,从而只要一次内存操作即可。
高并发写入场景中,在条件允许的情况下,建议字符串长度控制在39字节以内,减少创建 redisObject 内存分配次数,从而提高性能。
 
缩减键值对象
降低Redis内存使用最直接的方式就是缩减键( key )和值( value )的长度。· key 长度:如在设计键时,在完整描述业务情况下,键值越短越好。如 user {uid} friends notify {fid} 可以简化为 u {uid} fs nt {fid} 。· value 长度:值对象缩减比较复杂,常见需求是把业务对象序列化成二进制数组放入 Redis 。首先应该在业务上精简业务对象,去掉不必要的属性避免存储无效数据。其次在序列化工具选择上,应该选择更高效的序列化工具来降低字节数组大小。

共享对象池
共享对象池是指Redis内部维护 [0-9999] 的整数对象池。创建大量的整数类型 redisObject 存在内存开销,每个 redisObject 内部结构至少占 16 字节,甚至超过了整数自身空间消耗。所以 Redis 内存维护一个 [0-9999] 的整数对象池,用于节约内存。除了整数值对象,其他类型如 list hash set zset 内部元素也可以使用整数对象池。因此开发中在满足需求的前提下,尽量使用整数对象以节省内存。

字符串优化
尽量减少字符串频繁修改操作如append、 setrange ,改为直接使用 set 修改字符串,降低预分配带来的内存浪费和内存碎片化。不一定把每份数据作为字符串整体存储,像 json 这样的数据可以使用 hash 结构

编码优化
Redis对外提供了 string list hash set zet 等类型,但是 Redis 内部针对不同类型存在编码的概念,所谓编码就是具体使用哪种底层数据结构来实现。编码不同将直接影响数据的内存占用和读写效率。
ziplist编码
ziplist 编码主要目的是为了节约内存,因此所有数据都是采用线性连续的内存结构。 ziplist 编码是应用范围最广的一种,可以分别作为 hash list zset 类型的底层数据结构实现。 .
 
intset编码是集合( set )类型编码的一种,内部表现为存储有序、不重复的整数集。当集合只包含整数且长度不超过 set-max-intset-entries 配置时被启用。使用 intset 编码的集合时,尽量保持整数范围一致,如都在 int-16 范围内。防止个别大整数触发集合升级操作,产生内存浪费。
控制键的数量
 
防止穿透优化
缓存穿透是指查询一个根本不存在的数据,缓存层和存储层都不会命中,通常出于容错的考虑,如果从存储层查不到数据则不写入缓存穿透将导致不存在的数据每次请求都要到存储层去查询,失去了缓存保护后端存储的意义。缓存空对象,存储层不命中后,仍然将空对象保留到缓存层中,之后再访问这个数据将会从缓存中获取,这样就保护了后端数据源。

无底洞优化
·客户端一次批量操作会涉及多次网络操作,也就意味着批量操作会随着节点的增多,耗时会不断增大。·网络连接数变多,对节点的性能也有一定影响。用一句通俗的话总结就是,更多的节点不代表更高的性能,所谓“无底洞”就是说投入越多不一定产出越多。
就可以将属于同一个节点的key进行归档,得到每个节点的 key 子列表,之后多线程对每个节点执行 mget 或者 Pipeline 操作,它的操作时间 =node 次网络时间 +n 次命令时间,网络次数是 node 的个数
 
雪崩优化
由于缓存层承载着大量请求,有效地保护了存储层,但是如果缓存层由于某些原因不能提供服务,于是所有的请求都会达到存储层,存储层的调用量会暴增,造成存储层也会级联宕机的情况。依赖隔离组件为后端限流并降级。
 
防阻塞
防止高算法复杂度命令和big key
如对一个包含上万个元素的hash结构执行 hgetall 操作,由于数据量比较大且命令算法复杂度是 O n ),这条命令执行速度必然很慢。这个问题就是典型的不合理使用 API 和数据结构。对于高并发的场景我们应该尽量避免在大对象上执行算法复杂度超过 O n )的命令,
调整大对象:缩减大对象数据或把大对象拆分为多个小对象,防止一次命令操作过多的数据。
 
防止CPU饱和
单线程的Redis处理命令时只能使用一个 CPU 。而 CPU 饱和是指 Redis 把单核 CPU 使用率跑到接近 100% 。情况,大量使用高算法复杂的命令将导致这个问题
 
防止持久化阻塞
Fork 阻塞
fork操作发生在 RDB AOF 重写时, Redis 主线程调用 fork 操作产生共享内存的子进程,由子进程完成持久化文件重写工作。如果 fork 操作本身耗时过长,必然会导致主线程的阻塞。
AOF刷盘阻塞
当我们开启AOF持久化功能时,文件刷盘的方式一般采用每秒一次,后台线程每秒对 AOF 文件做 fsync 操作。当硬盘压力过大时, fsync 操作需要等待,直到写入完成
HugePage
HugePage写操作阻塞子进程在执行重写期间利用 Linux 写时复制技术降低内存开销,因此只有写操作时 Redis 才复制要修改的内存页。对于开启 TransparentHugePages 的操作系统,每次写命令引起的复制内存页单位由 4K 变为 2MB ,放大了 512 倍,会拖慢写操作的执行时间,导致大量写操作慢查询
防止进程竞争
Redis是典型的 CPU 密集型应用,不建议和其他多核 CPU 密集型服务部署在一起。当其他进程过度消耗 CPU 时,将严重影响 Redis 吞吐量。可以通过 top sar 等命令定位到 CPU 消耗的时间点和具体进程,这个问题比较容易发现,需要调整服务之间部署结构。
 
禁用内存交换
内存交换(swap)对于 Redis 来说是非常致命的, Redis 保证高性能的一个重要前提是所有的数据在内存中。如果操作系统把 Redis 使用的部分内存换出到硬盘,由于内存与硬盘读写速度差几个数量级,会导致发生交换后的 Redis 性能急剧下降。
网络闪断
一般发生在网络割接或者带宽耗尽的情况,对于网络闪断的识别比较困难
 
Redis连接拒绝
Redis通过 maxclients 参数控制客户端最大连接数,默认 10000 。当 Redis 连接数大于 maxclients 时会拒绝新的连接进入, infostats rejected_connections 统计指标记录所有被拒绝连接的数量: Redis 使用多路复用 IO 模型可支撑大量连接,但是不代表可以无限连接。客户端访问 Redis 时尽量采用 NIO 长连接或者连接池的方式。
连接溢出
操作系统一般会对进程使用的资源做限制,其中一项是对进程可打开最大文件数控制,通过ulimit-n查看,通常默认 1024 。由于 Linux 系统对 TCP 连接也定义为一个文件句柄,因此对于支撑大量连接的 Redis 来说需要增大这个值,如设置 ulimit-n65535 ,防止 Toomanyopenfiles 错误。 backlog 队列溢出系统对于特定端口的 TCP 连接使用 backlog 队列保存。
Redis默认的长度为 511 ,通过 tcp-backlog 参数设置。如果如果 Redis 用于高并发场景为了防止缓慢连接占用,可适当增大这个设置,但必须大于操作系统允许值才能生效。当 Redis 启动时如果 tcp-backlog 设置大于系统允许值将以系统值为准
网络延迟
网络延迟取决于客户端到Redis服务器之间的网络环境。主要包括它们之间的物理拓扑和带宽占用情况。
网卡软中断
网卡软中断是指由于单个网卡队列只能使用一个CPU,高并发下网卡数据交互都集中在同一个 CPU ,导致无法充分利用多核 CPU 的情况。
虚拟机内在延迟
如果您正在使用虚拟机,则可能存在与Redis无关的内部延迟 https://redis.io/topics/latency
 
规避全量复制
·节点运行 ID 不匹配:当主从复制关系建立后,从节点会保存主节点的运行 ID ,如果此时主节点因故障重启,那么它的运行 ID 会改变,从节点发现主节点运行 ID 不匹配时,会认为自己复制的是一个新的主节点从而进行全量复制。对于这种情况应该从架构上规避,比如提供故障转移功能。当主节点发生故障后,手动提升从节点为主节点或者采用支持自动故障转移的哨兵或集群方案。
·复制积压缓冲区不足:当主从节点网络中断后,从节点再次连上主节点时会发送 psync{offset}{runId} 命令请求部分复制,如果请求的偏移量不在主节点的积压缓冲区内,则无法提供给从节点数据,因此部分复制会退化为全量复制。针对这种情况需要根据网络中断时长,写命令数据量分析出合理的积压缓冲区大小。网络中断一般有闪断、机房割接、网络分区等情况。这时网络中断的时长一般在分钟级( net_break_time )。写命令数据量可以统计高峰期主节点每秒 inforeplication master_repl_offset 差值获取( write_size_per_minute )。积压缓冲区默认为 1MB ,对于大流量场景显然不够,这时需要增大积压缓冲区,保证 repl_backlog_size>net_break_time*write_size_per_minute ,从而避免因复制积压缓冲区不足造成的全量复制。
规避复制风暴
复制风暴是指大量从节点对同一主节点或者对同一台机器的多个主节点短时间内发起全量复制的过程。复制风暴对发起复制的主节点或者机器造成大量开销,导致CPU、内存、带宽消耗。因此我们应该分析出复制风暴发生的场景,提前采用合理的方式规避。单主节点复制风暴单主节点复制风暴一般发生在主节点挂载多个从节点的场景。当主节点重启恢复后,从节点会发起全量复制流程,这时主节点就会为从节点创建 RDB 快照,如果在快照创建完毕之前,有多个从节点都尝试与主节点进行全量同步,那么其他从节点将共享这份 RDB 快照。这点 Redis 做了优化,有效避免了创建多个快照。但是,同时向多个从节点发送 RDB 快照,可能使主节点的网络带宽消耗严重,造成主节点的延迟变大,极端情况会发生主从节点连接断开,导致复制失败。解决方案首先可以减少主节点( master )挂载从节点( slave )的数量,或者采用树状复制结构,加入中间层从节点用来保护主节点。
 
 

你可能感兴趣的:(存储整理)