Redis性能调优

Redis性能调优

尽管Redis是一个很是快速的内存数据存储媒介,也并不表明Redis不会产生性能问题。
前文中提到过,Redis采用单线程模型,全部的命令都是由一个线程串行执行的,因此当某个命令执行耗时较长时,会拖慢其后的全部命令,这使得Redis对每一个任务的执行效率更加敏感。git

针对Redis的性能优化,主要从下面几个层面入手:github

  • 最初的也是最重要的,确保没有让Redis执行耗时长的命令
  • 使用pipelining将连续执行的命令组合执行
  • 操做系统的Transparent huge pages功能必须关闭:
    echo never > /sys/kernel/mm/transparent_hugepage/enabled
  • 若是在虚拟机中运行Redis,可能自然就有虚拟机环境带来的固有延迟。能够经过./redis-cli --intrinsic-latency 100命令查看固有延迟。同时若是对Redis的性能有较高要求的话,应尽量在物理机上直接部署Redis。
  • 检查数据持久化策略
  • 考虑引入读写分离机制

长耗时命令

Redis绝大多数读写命令的时间复杂度都在O(1)到O(N)之间,在文本和官方文档中均对每一个命令的时间复杂度有说明。redis

一般来讲,O(1)的命令是安全的,O(N)命令在使用时须要注意,若是N的数量级不可预知,则应避免使用。例如对一个field数未知的Hash数据执行HGETALL/HKEYS/HVALS命令,一般来讲这些命令执行的很快,但若是这个Hash中的field数量极多,耗时就会成倍增加。
又如使用SUNION对两个Set执行Union操做,或使用SORT对List/Set执行排序操做等时,都应该严加注意。算法

避免在使用这些O(N)命令时发生问题主要有几个办法:sql

  • 不要把List当作列表使用,仅当作队列来使用
  • 经过机制严格控制Hash、Set、Sorted Set的大小
  • 可能的话,将排序、并集、交集等操做放在客户端执行
  • 绝对禁止使用KEYS命令
  • 避免一次性遍历集合类型的全部成员,而应使用SCAN类的命令进行分批的,游标式的遍历

Redis提供了SCAN命令,能够对Redis中存储的全部key进行游标式的遍历,避免使用KEYS命令带来的性能问题。同时还有SSCAN/HSCAN/ZSCAN等命令,分别用于对Set/Hash/Sorted Set中的元素进行游标式遍历。SCAN类命令的使用请参考官方文档:https://redis.io/commands/scan数据库

Redis提供了Slow Log功能,能够自动记录耗时较长的命令。相关的配置参数有两个:缓存

slowlog-log-slower-than xxxms  #执行时间慢于xxx毫秒的命令计入Slow Log slowlog-max-len xxx #Slow Log的长度,即最大纪录多少条Slow Log

使用SLOWLOG GET [number]命令,能够输出最近进入Slow Log的number条命令。
使用SLOWLOG RESET命令,能够重置Slow Log安全

网络引起的延迟

  • 尽量使用长链接或链接池,避免频繁建立销毁链接
  • 客户端进行的批量数据操做,应使用Pipeline特性在一次交互中完成。具体请参照本文的Pipelining章节

数据持久化引起的延迟

Redis的数据持久化工做自己就会带来延迟,须要根据数据的安全级别和性能要求制定合理的持久化策略:性能优化

  • AOF + fsync always的设置虽然可以绝对确保数据安全,但每一个操做都会触发一次fsync,会对Redis的性能有比较明显的影响
  • AOF + fsync every second是比较好的折中方案,每秒fsync一次
  • AOF + fsync never会提供AOF持久化方案下的最优性能
  • 使用RDB持久化一般会提供比使用AOF更高的性能,但须要注意RDB的策略配置
  • 每一次RDB快照和AOF Rewrite都须要Redis主进程进行fork操做。fork操做自己可能会产生较高的耗时,与CPU和Redis占用的内存大小有关。根据具体的状况合理配置RDB快照和AOF Rewrite时机,避免过于频繁的fork带来的延迟

Redis在fork子进程时须要将内存分页表拷贝至子进程,以占用了24GB内存的Redis实例为例,共须要拷贝24GB / 4kB * 8 = 48MB的数据。在使用单Xeon 2.27Ghz的物理机上,这一fork操做耗时216ms。bash

能够经过INFO命令返回的latest_fork_usec字段查看上一次fork操做的耗时(微秒)

Swap引起的延迟

当Linux将Redis所用的内存分页移至swap空间时,将会阻塞Redis进程,致使Redis出现不正常的延迟。Swap一般在物理内存不足或一些进程在进行大量I/O操做时发生,应尽量避免上述两种状况的出现。

/proc/<pid>/smaps文件中会保存进程的swap记录,经过查看这个文件,可以判断Redis的延迟是否由Swap产生。若是这个文件中记录了较大的Swap size,则说明延迟颇有多是Swap形成的。

数据淘汰引起的延迟

当同一秒内有大量key过时时,也会引起Redis的延迟。在使用时应尽可能将key的失效时间错开。

引入读写分离机制

Redis的主从复制能力能够实现一主多从的多节点架构,在这一架构下,主节点接收全部写请求,并将数据同步给多个从节点。
在这一基础上,咱们可让从节点提供对实时性要求不高的读请求服务,以减少主节点的压力。
尤为是针对一些使用了长耗时命令的统计类任务,彻底能够指定在一个或多个从节点上执行,避免这些长耗时命令影响其余请求的响应。

关于读写分离的具体说明,请参见后续章节

主从复制与集群分片

主从复制

Redis支持一主多从的主从复制架构。一个Master实例负责处理全部的写请求,Master将写操做同步至全部Slave。
借助Redis的主从复制,能够实现读写分离和高可用:

  • 实时性要求不是特别高的读请求,能够在Slave上完成,提高效率。特别是一些周期性执行的统计任务,这些任务可能须要执行一些长耗时的Redis命令,能够专门规划出1个或几个Slave用于服务这些统计任务
  • 借助Redis Sentinel能够实现高可用,当Master crash后,Redis Sentinel可以自动将一个Slave晋升为Master,继续提供服务

启用主从复制很是简单,只须要配置多个Redis实例,在做为Slave的Redis实例中配置:

slaveof 192.168.1.1 6379 #指定Master的IP和端口

当Slave启动后,会从Master进行一次冷启动数据同步,由Master触发BGSAVE生成RDB文件推送给Slave进行导入,导入完成后Master再将增量数据经过Redis Protocol同步给Slave。以后主从之间的数据便一直以Redis Protocol进行同步

使用Sentinel作自动failover

Redis的主从复制功能自己只是作数据同步,并不提供监控和自动failover能力,要经过主从复制功能来实现Redis的高可用,还须要引入一个组件:Redis Sentinel

Redis Sentinel是Redis官方开发的监控组件,能够监控Redis实例的状态,经过Master节点自动发现Slave节点,并在监测到Master节点失效时选举出一个新的Master,并向全部Redis实例推送新的主从配置。

Redis Sentinel须要至少部署3个实例才能造成选举关系。

关键配置:

sentinel monitor mymaster 127.0.0.1 6379 2 #Master实例的IP、端口,以及选举须要的同意票数 sentinel down-after-milliseconds mymaster 60000 #多长时间没有响应视为Master失效 sentinel failover-timeout mymaster 180000 #两次failover尝试间的间隔时长 sentinel parallel-syncs mymaster 1 #若是有多个Slave,能够经过此配置指定同时重新Master进行数据同步的Slave数,避免全部Slave同时进行数据同步致使查询服务也不可用

另外须要注意的是,Redis Sentinel实现的自动failover不是在同一个IP和端口上完成的,也就是说自动failover产生的新Master提供服务的IP和端口与以前的Master是不同的,因此要实现HA,还要求客户端必须支持Sentinel,可以与Sentinel交互得到新Master的信息才行。

集群分片

为什么要作集群分片:

  • Redis中存储的数据量大,一台主机的物理内存已经没法容纳
  • Redis的写请求并发量大,一个Redis实例以没法承载

当上述两个问题出现时,就必需要对Redis进行分片了。
Redis的分片方案有不少种,例如不少Redis的客户端都自行实现了分片功能,也有向Twemproxy这样的以代理方式实现的Redis分片方案。然而首选的方案还应该是Redis官方在3.0版本中推出的Redis Cluster分片方案。

本文不会对Redis Cluster的具体安装和部署细节进行介绍,重点介绍Redis Cluster带来的好处与弊端。

Redis Cluster的能力

  • 可以自动将数据分散在多个节点上
  • 当访问的key不在当前分片上时,可以自动将请求转发至正确的分片
  • 当集群中部分节点失效时仍能提供服务

其中第三点是基于主从复制来实现的,Redis Cluster的每一个数据分片都采用了主从复制的结构,原理和前文所述的主从复制彻底一致,惟一的区别是省去了Redis Sentinel这一额外的组件,由Redis Cluster负责进行一个分片内部的节点监控和自动failover。

Redis Cluster分片原理

Redis Cluster中共有16384个hash slot,Redis会计算每一个key的CRC16,将结果与16384取模,来决定该key存储在哪个hash slot中,同时须要指定Redis Cluster中每一个数据分片负责的Slot数。Slot的分配在任什么时候间点均可以进行从新分配。

客户端在对key进行读写操做时,能够链接Cluster中的任意一个分片,若是操做的key不在此分片负责的Slot范围内,Redis Cluster会自动将请求重定向到正确的分片上。

hash tags

在基础的分片原则上,Redis还支持hash tags功能,以hash tags要求的格式明明的key,将会确保进入同一个Slot中。例如:{uiv}user:1000和{uiv}user:1001拥有一样的hash tag {uiv},会保存在同一个Slot中。

使用Redis Cluster时,pipelining、事务和LUA Script功能涉及的key必须在同一个数据分片上,不然将会返回错误。如要在Redis Cluster中使用上述功能,就必须经过hash tags来确保一个pipeline或一个事务中操做的全部key都位于同一个Slot中。

有一些客户端(如Redisson)实现了集群化的pipelining操做,能够自动将一个pipeline里的命令按key所在的分片进行分组,分别发到不一样的分片上执行。可是Redis不支持跨分片的事务,事务和LUA Script仍是必须遵循全部key在一个分片上的规则要求。

主从复制 vs 集群分片

在设计软件架构时,要如何在主从复制和集群分片两种部署方案中取舍呢?

从各个方面看,Redis Cluster都是优于主从复制的方案

  • Redis Cluster可以解决单节点上数据量过大的问题
  • Redis Cluster可以解决单节点访问压力过大的问题
  • Redis Cluster包含了主从复制的能力

那是否是表明Redis Cluster永远是优于主从复制的选择呢?

并非。

软件架构永远不是越复杂越好,复杂的架构在带来显著好处的同时,必定也会带来相应的弊端。采用Redis Cluster的弊端包括:

  • 维护难度增长。在使用Redis Cluster时,须要维护的Redis实例数倍增,须要监控的主机数量也相应增长,数据备份/持久化的复杂度也会增长。同时在进行分片的增减操做时,还须要进行reshard操做,远比主从模式下增长一个Slave的复杂度要高。
  • 客户端资源消耗增长。当客户端使用链接池时,须要为每个数据分片维护一个链接池,客户端同时须要保持的链接数成倍增多,加大了客户端自己和操做系统资源的消耗。
  • 性能优化难度增长。你可能须要在多个分片上查看Slow Log和Swap日志才能定位性能问题。
  • 事务和LUA Script的使用成本增长。在Redis Cluster中使用事务和LUA Script特性有严格的限制条件,事务和Script中操做的key必须位于同一个分片上,这就使得在开发时必须对相应场景下涉及的key进行额外的规划和规范要求。若是应用的场景中大量涉及事务和Script的使用,如何在保证这两个功能的正常运做前提下把数据平均分到多个数据分片中就会成为难点。

因此说,在主从复制和集群分片两个方案中作出选择时,应该从应用软件的功能特性、数据和访问量级、将来发展规划等方面综合考虑,只在确实有必要引入数据分片时再使用Redis Cluster。
下面是一些建议:

  1. 须要在Redis中存储的数据有多大?将来2年内可能发展为多大?这些数据是否都须要长期保存?是否可使用LRU算法进行非热点数据的淘汰?综合考虑前面几个因素,评估出Redis须要使用的物理内存。
  2. 用于部署Redis的主机物理内存有多大?有多少能够分配给Redis使用?对比(1)中的内存需求评估,是否足够用?
  3. Redis面临的并发写压力会有多大?在不使用pipelining时,Redis的写性能能够超过10万次/秒(更多的benchmark能够参考 https://redis.io/topics/benchmarks )
  4. 在使用Redis时,是否会使用到pipelining和事务功能?使用的场景多很少?

综合上面几点考虑,若是单台主机的可用物理内存彻底足以支撑对Redis的容量需求,且Redis面临的并发写压力距离Benchmark值还尚有距离,建议采用主从复制的架构,能够省去不少没必要要的麻烦。同时,若是应用中大量使用pipelining和事务,也建议尽量选择主从复制架构,能够减小设计和开发时的复杂度。

Redis Java客户端的选择

Redis的Java客户端不少,官方推荐的有三种:Jedis、Redisson和lettuce。

在这里对Jedis和Redisson进行对比介绍

Jedis:

  • 轻量,简洁,便于集成和改造
  • 支持链接池
  • 支持pipelining、事务、LUA Scripting、Redis Sentinel、Redis Cluster
  • 不支持读写分离,须要本身实现
  • 文档差(真的不好,几乎没有……)

Redisson:

  • 基于Netty实现,采用非阻塞IO,性能高
  • 支持异步请求
  • 支持链接池
  • 支持pipelining、LUA Scripting、Redis Sentinel、Redis Cluster
  • 不支持事务,官方建议以LUA Scripting代替事务
  • 支持在Redis Cluster架构下使用pipelining
  • 支持读写分离,支持读负载均衡,在主从复制和Redis Cluster架构下均可以使用
  • 内建Tomcat Session Manager,为Tomcat 6/7/8提供了会话共享功能
  • 能够与Spring Session集成,实现基于Redis的会话共享
  • 文档较丰富,有中文文档

对于Jedis和Redisson的选择,一样应遵循前述的原理,尽管Jedis比起Redisson有各类各样的不足,但也应该在须要使用Redisson的高级特性时再选用Redisson,避免形成没必要要的程序复杂度提高。

Jedis:
github:https://github.com/xetorthio/jedis
文档:https://github.com/xetorthio/jedis/wiki

Redisson:
github:https://github.com/redisson/redisson
文档:https://github.com/redisson/redisson/wiki

 

 

 

 

1. 什么是redis

Redis是一个nosql的高性能Key-Value内存数据库,支持网络,亦可本地持久化。3.0.0Beta版已支持集群。

详细资料可见http://www.redis.cn/

2. Redis关键参数

Ø 客户端最大链接数(maxclients)

可能的错误信息:max number of clients reached。

 

默认为0,即不限制,通常不须要更改,因此客户端链接限制,取决于操做系统参数ulimit -n(max open files),可经过修改/etc/security/limits.conf文件以永久生效。

 

如下场景在性能压测时出现,涉及三个参数:

可能的错误信息:scheduled to be closed ASAP for overcoming of output buffer limits。

有时候明明master/slave都活得好好的,忽然间就说要从新进行全同步了:

1.Slave显示:# MASTER time out: no data nor PING received…

2.Master显示:# Client addr=10.175.162.123:44670 flags=S oll=104654 omem=2147487792 events=rw cmd=sync scheduled to be closed ASAP for overcoming of output buffer limits.

 

Ø 主从响应策略(repl-ping-slave-period/repl-timeout)

slave会每隔repl-ping-slave-period(默认10秒)ping一次master,若是超过repl-timeout(默认 60秒)都没有收到响应,就会认为Master挂了。若是Master明明没挂但被阻塞住了也会报这个错。能够适当调大repl-timeout。

 

Ø 客户端输出缓冲区(client-output-buffer-limit)

该参数有三种场景策略,主要是第二种slave场景。当使用主从复制时,性能压测下,数据量会急剧增加,致使从节点须要复制的数据很大,消耗时长增长。slave没挂但被阻塞住了,好比正在loading Master发过来的RDB, Master的指令不能马上发送给slave,就会放在output buffer中(见oll是命令数量,omem是大小),在配置文件中有以下配置:client-output-buffer-limit slave 256mb 64mb 60, 这是说负责发数据给slave的client,若是buffer超过256m或者连续60秒超过64m,就会被马上强行关闭。因此此时应该相应调大数值,不然就会出现很悲剧的循环:Master传输一个很大的RDB给Slave,Slave努力地装载,但还没装载完,Master对client的缓存满了,再来一次。

平时能够在master执行 redis-cli client list 找那个cmd=sync,flag=S的client,注意OMem的变化。

 

Ø 日志级别和输出(loglevel、logfile)

 

生产可调整为warning级别,并重定向到某个文件。这对排除问题颇有帮助。

 

3. 性能调优

Ø 内存分配限制

可能的错误信息:Cannot allocate memory

 

Redis在主从复制时,须要fork子进程来进行操做,若是你的应用堆积了很大数据在内存中,那么就须要针对这个子进程申请相应的内存空间,此时会受到操做系统的限制。经过更改系统配置文件/etc/sysctl.conf的vm.overcommit_memory=1以永久生效。该参数有0、一、2三个值。1表示容许分配全部的物理内存,而无论当前的内存状态如何。

 

Ø 客户端频繁获取链接限制

可能的错误信息:Cannot assign requested address

 

频繁地连服务器,但每次链接都在短期内结束,致使不少的TIME_WAIT,以致于用光端口号,因此新链接没办法绑定端口。修改以下2个内核参数:

sysctl -w net.ipv4.tcp_timestamps=1,开启对于TCP时间戳的支持,若该项设置为0,则下面一项设置不起做用;

sysctl -w net.ipv4.tcp_tw_recycle=1,表示开启TCP链接中TIME-WAIT sockets的快速回收。

 

 

 

1、 Redis部署结构优化建议

1. Master不作AOF或RDB持久化,Slave作AOF持久化,建议同时作RDB持久化 
2. 全部Master所有增长Slave 
3. Master挂载Slave不超过2个,采用M-S-S方式挂载。若想保证高可用,即主从切换,可采用Keepalived机制.

备注:以上是基于Redis部署结构不合理提出的建议,同时也参考了新浪微博、淘宝架构中Redis优化方案给出

2、 Redis配置优化建议

1.tcp-keepalive 60 阻止因为某个command执行过长达到timeout超时时间而被断开链接,且能够提升链接错误的检测.
  • 1
  • 2
2.stop-writes-on-bgsave-error no 当bgsave快照操做出错时中止写数据到磁盘,这样后面写操做均会失败,为了避免影响后续写操做,故需将该项值改成no.
  • 1
  • 2
3.rdbchecksum no 检查RDB数据的正确性,会牺牲10%的性能,故建议关闭.
  • 1
  • 2
4.auto-aof-rotate-max-size 20gb auto-aof-rotate-max-total 4 auto-aof-rewrite-percentage 0 (关闭rewrite模式) 将AOF rewrite模式改成rotate模式,即将AOF在线实时Rewrite的功能,切换到线下操做,1份AOF文件切割成多份(相似日志切割),这样提高了redis性能的同时提高内存的利用率.
  • 1
  • 2
  • 3
  • 4
5.no-appendfsync-on-rewrite yes 避免新修改数据刷磁盘时出现IO阻塞
  • 1
  • 2

备注:以上是基于Redis配置不合理提出的优化建议

3、 系统内核配置优化建议

1.开启了AOF模式,为了缓解IO阻塞 
编辑/etc/sysctl.conf ,添加以下配置:

vm.dirty_background_ratio = 5 vm.dirty_ratio = 10
  • 1
  • 2

而后sysctl -p 使配置文件生效.

2.开启了RDB模式,为了不Fork失败

编辑/etc/sysctl.conf ,改vm.overcommit_memory=1, 
而后sysctl -p 使配置文件生效

备注:以上是基于测试结果给出的系统内核优化建议

后续计划:

以上是从架构角度提出的优化建议,后续会从业务角度,分析内存类型是否合理、冷热数据划分是否合理等

备注: 
关于冷热数据划分,可以使用以下Redis命令进行统计分析:

OBJECT REFCOUNT 该命令主要用于调试(debugging),它可以返回指定key所对应value被引用的次数.

OBJECT ENCODING 该命令返回指定key对应value所使用的内部表示(representation)(译者注:也能够理解为数据的压缩方式).

OBJECT IDLETIME 该命令返回指定key对应的value自被存储以后空闲的时间,以秒为单位(没有读写操做的请求) ,这个值返回以10秒为单位的秒级别时间,这一点可能在之后的实现中改善

相关文章
相关标签/搜索