redis优化方案 有更新!

  , ,
评论 • 260 浏览

redis优化方案

尽管redis是一个非常快速的内存数据库存储媒介,也并不代表redis不会产生性能问题。

Redis采用单线程模型,所有的命令都是由一个线程串行执行的。所以当某个命令执行耗时比较长时,会拖慢其后的所有命令,这使得redis对每个任务的执行效率更加敏感。

针对redis的性能优化,主要从下面几个方面入手:

  1. 确保没有让redis执行耗时长的命令。

  2. 使用pipeline方式操作数据。

  3. 如果在虚拟机中运行redis,可能天然就有虚拟机环境带来的固有延迟。可以通过./redis-cli –intrinsic-latency 100命令查看固有延迟。如果对redis的性能优较高的要求的话,应尽可能在物理机上直接部署redis。

  4. 检查数据的持久化策略。

  5. 考虑引入读写分离机制。

1 长耗时命令

Redis绝大多数读写命令的时间复杂度都在O(1)到O(N)之间,通常来说,O(1)的命令是安全的,O(N)命令在使用的时候需要注意,如果N的数量级不可预知,则应避免使用。例如:对一个field数位置的Hash数据执行HGETALL/HKEYS/HVALS命令,通常来说这些命令执行的很快,但如果这个Hash中的field数量极多,耗时就会成倍增长。

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

  1. 不要把List当做列表使用,仅当做队列使用。

  2. 控制Hash/Set/Sorted Set大小。

  3. 将排序,并集,交集等操作放在客户端执行。

  4. 禁止使用keys命令。

  5. 避免一次性遍历集合类型的所有成员,而应使用scan类的命令进行分批的,游标式的遍历。

Redis提供了SCAN命令,可以对Redis中存储的key进行游标式的遍历,避免使用keys命令带来的性能问题。同时还有SSCAN/HSCAN/ZSCAN等命令,分别用户对Set/Hash/Sorted Set中的元素进行游标式遍历,官方文档。

2 网络引发的延迟

  1. 尽可能使用长连接或连接池,避免频繁创建销毁连接。

  2. 客户端进行的批量数据操作,应使用pipeline特性在一次交互中完成。

3 数据持久化引发的延迟

Redis的数据持久化工作本身就会带来延迟,需要根据数据的安全级别和性能要求制定合理的持久化策略:

  1. AOF + appendfsync always:设置虽然能够绝对保证数据安全,但每个操作都会触发一次fsync,会对redis性能有比较明显的影响。

  2. AOF + appendfsync everysec:折中方案,每秒fsync一次。

  3. AOF + appendfsync no:提供AOF持久化方案下的最优性能。

  4. 使用rdb持久化通常会提供比使用AOF更高的性能,但需要注意RDB的策略配置。

  5. 每一次rdb快照和AOF重写都需要redis主进程进行fork操作。Fork操作本身可能会产生较高的耗时,与CPU和Redis占用的内存大小有关。根据具体的情况合理配置RDB快照和AOF重写时机,避免过于频繁的fork带来的延迟。

Redis在fork子进程时需要将内存分页表拷贝至子进程,以占用24GB内存的redis实例为例,共需要拷贝24GB/4kb*8=48MB的数据。在使用单Xeon2.27Ghz的物理机上,这一fork操作耗时216ms。可以通过INFO命令返回的latest_fork_usec字段查看上一次fork操作的耗时(微秒)

4 Swap引发的延迟

当Linux将redis所用的内存分页移至swap空间时,将会阻塞redis进程,导致redis出现不正常的延迟。Swap通常在物理内存不足或一些进程在进行大量I/O操作时发生,应尽可能避免上述两种情况的出现。/proc//smaps文件中会保存进程的swap记录,通过查看这个文件,能够判断Redis的延迟是否由Swap产生。如果这个文件中记录了较大的Swap size,则说明延迟很有可能是Swap造成的。

5 数据淘汰引发的延迟

当同一秒内有大量的key过期时,也会引发redis的延迟。在使用时应尽量将key的失效时间错开。(更有可能会导致缓存雪崩,后续会单独写篇文章介绍缓存雪崩和缓存击穿以及应对方案

6 引入读写分离机制

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

7 Pipeline

Redis提供许多批量操作的命令,如MSET/MGET/HMSET/HMGET等等,这些命令存在的意义是减少维护网络连接和传输数据所消耗的资源和时间。例如:连续使用5次SET命令设置5个不同的key,比起使用一次MSET命令设置5个不同的key,效果是一样的,但前者会消耗更多的时长。

redis连接池提供了pipelined()方法返回Pipeline对象,在数据添加完毕后,需调用sync()方法,保证数据全部存储。

评论
validate