Redis时延问题分析及应对( 二 )

  • 不让fork带来的限制太多,可以从内存量上控制fork的时延;
  • 一般建议不超过20G,可根据自己服务器的性能来确定(内存越大,持久化的时间越长,复制页表的时间越长,对事件循环的阻塞就延长)
  • 新浪微博给的建议是不超过20G,而我们虚机上的测试,要想保证应用毛刺不明显,可能得在10G以下;
  • 使用大内存页,默认内存页使用4KB,这样,当使用40G的内存时,页表就有80M;而将每个内存页扩大到4M,页表就只有80K;这样复制页表几乎没有阻塞,同时也会提高快速页表缓冲TLB(translation lookaside buffer)的命中率;但大内存页也有问题,在写时复制时,只要一个页快中任何一个元素被修改,这个页块都需要复制一份(COW机制的粒度是页面),这样在写时复制期间,会耗用更多的内存空间;
  • 使用物理机;
  • 如果有的选,物理机当然是最佳方案,比上面都要省事;
  • 当然,虚拟化实现也有多种,除了Xen系统外,现代的硬件大部分都可以快速的复制页表;
  • 但公司的虚拟化一般是成套上线的,不会因为我们个别服务器的原因而变更,如果面对的只有Xen,只能想想如何用好它;
  • 杜绝新进程的产生,不使用持久化,不在主结点上提供查询;实现起来有以下方案:
  • 1) 只用单机,不开持久化,不挂slave结点 。这样最简单,不会有新进程的产生;但这样的方案只适合缓存;
  • 如何来做这个方案的高可用?
  • 要做高可用,可以在写redis的前端挂上一个消息队列,在消息队列中使用pub-sub来做分发,保证每个写操作至少落到2个结点上;因为所有结点的数据相同,只需要用一个结点做持久化,这个结点对外不提供查询;

  • Redis时延问题分析及应对

    文章插图
     
    1.  
    2. 2) master-slave:在主结点上开持久化,主结点不对外提供查询,查询由slave结点提供,从结点不提供持久化;这样,所有的fork耗时的操作都在主结点上,而查询请求由slave结点提供;
    3. 这个方案的问题是主结点坏了之后如何处理?
    4. 简单的实现方案是主不具有可替代性,坏了之后,redis集群对外就只能提供读,而无法更新;待主结点启动后,再继续更新操作;对于之前的更新操作,可以用MQ缓存起来,等主结点起来之后消化掉故障期间的写请求;

    Redis时延问题分析及应对

    文章插图
     
    1.  
    2. 如果使用官方的Sentinel将从升级为主,整体实现就相对复杂了;需要更改可用从的ip配置,将其从可查询结点中剔除,让前端的查询负载不再落在新主上;然后,才能放开sentinel的切换操作,这个前后关系需要保证;
    持久化造成的阻塞
    执行持久化(AOF / RDB snapshot)对系统性能有较大影响,特别是服务器结点上还有其它读写磁盘的操作时(比如,应用服务和redis服务部署在相同结点上,应用服务实时记录进出报日志);应尽可能避免在IO已经繁重的结点上开Redis持久化;
    子进程持久化时,子进程的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秒;
    子进程AOF重写时,系统的sync造成主进程的write阻塞


    推荐阅读