Redis运维实践

| |
[不指定 2014/07/15 19:25 | by ipaddr ]
彩票业务的核心流程使用了Redis来实现消息队列,近期在世界杯的影响下,业务爆发式增长,给各组件尤其是Redis的运维也提出了新的挑战,简单整理一下运维过程中的一些心得:

Redis是什么?

Redis是一个快速,小巧,简单,但又十分强大的K/V组件;
http://www.iamadmin.com/ 原创,转载请注明)
主要特性:
  • 所有数据都在内存中。 
  • 多种的数据结构:String / Hash / List / Set / Ordered Set。 
  • 数据过期时间支持。 
  • 不完全的事务支持。 
  • 服务端脚本:使用Lua Script编写,类似存储过程的作用。 
  • PubSub:捞过界的消息一对多发布订阅功能,起码Redis-Sentinel使用了它。 
  • 持久化:支持定期导出内存的Snapshot 与 记录写操作日志的Append Only File两种模式。 
  • Replication:Master-Slave模式,Master可连接多个只读Slave,暂无专门的Geographic Replication支持。 
  • Fail-Over:Redis-Sentinel节点负责监控Master节点,在master失效时提升slave,独立的仲裁节点模式有效防止脑裂。 
  • Sharding:开发中的Redis-Cluser。 
  • 动态配置:所有参数可用命令行动态配置不需重启,并重新写回配置文件中,对云上的大规模部署非常合适。

为什么要用Redis?

类似的KV组件非常多,业界有非常出名的Memcached、MongoDB,公司有大名鼎鼎的CKV;但Redis应用场景不止是简单的cache,与它们相比,个人觉得Redis的最大特色是拥有非常丰富的数据类型,比如新浪微博曾维护世界上最大的Redis来存储他们的关系链,消息队列;这是其它KV组件所无法实现的功能;
此外,Redis的持久化、复制功能等功能也非常完备,大大提升了组件本身的可用性;

Redis的缺陷?

Redis也不是完美的,同样存在一些使用限制:
  • 异常IO+单进程模型:由单个进程对外提供服务,一些内部异步操作(例如持久化,复制)通过fork+copy on write来实现,单进程模型最大的优点就是简单、高效,但带来的问题就是同时只有一个请求被处理;如果单个操作延时较大,或是单个客户端死循环的方式读写的话,必然会堵塞其它请求;程序设计时需要重点考虑这个限制,避免单个复杂操作或死循环式读写;
  • 数据全部存储在内存,优点还是简单、快速;缺点是内存成本较高并且单机有上限,稍不注意会有OOM Killer的风险;解决方案主要有:
    • 数据水平、垂直拆分,集群化管理;新版的Reids 3.0自带了cluster功能,当前仍是Beta阶段,稳定性未知;
    • 程序识别冷热数据,并将冷数据落地到其它组件;

Redis监控

Redis不仅仅是Cache,通常情况下还存储了相当多的业务第一手数据,所以对Redis的可用性、可靠性要求非常高;除了优化Redis本身的配置,还需要监控Redis运行情况,为排障、优化、预警提供数据;
组件本身有相当多的统计数据,可以通过info指令获取,我们通过脚本,每分钟获取info输出,再进行对比和分析,就可以实现有效的监控;
当前我们监控的主要数据有:
  • Redis当前已连接客户端  
  • Redis堵塞连接客户端 
  • Redis使用内存数(M) 
  • Redis占用内存数(M) 
  • Redis未保存操作次数 
  • Redis当前周期新连接数
  • Redis当前周期处理命令数
当前我们监控的主要异常有:
  • Redis无法连接; 
  • Redis角色为slave,但是同步中断; 
  • Redis角色为master时,所属的slave增加或减少都会有异常通知; 
  • Redis使用内存数,超过物理内存80%

常见错误:

1. 连接数不够
当前彩票架构还没有做到服务化,导致所有PHP进程都是直连Redis,且考虑到一些后台服务需要频率连接Redis而使用了pconnect,带来的问题就是Redis连接数非常多;
Redis在2.6以前,连接数限制在10240,如果在增加的话需要修改程序重编,在2.6之后组件自身没有做限制;
操作系统层面还有程序的FD限制,请在启动Redis前或系统初始化的时候设置ulimit -n
架构设计时,需要重点考虑连接数问题,即使在2.6+后无连接数限制,也应该将业务尽量服务化,收敛业务逻辑以及到Redis,DB,Memcached等组件的连接;
2. 无法持久化或复制
当Redis内存使用量约超过物理内存一半时,Redis在fork进程进行bgsave时将会失败,此时无法rdb持久化以及新slave同步将失败,通常在日志中可以看到:
Can't save in background: fork: Cannot allocate memory
这种情况,可以将Linux的 vm.overcommit_memory = 1,但理论上也会存在风险,如果在rdb dump期间业务有相当多的数据更新,将会导致系统内存不足而发生OOM;
3. 持久化时请求被堵塞
虽然持久化时会fork进程出来,避免主进程堵塞,但大量的磁盘IO仍然会影响系统负载,导致处理性能严重下降,业务请求受到堵塞;
为避免给业务带来影响,通常做法有:
  • 在master上面关闭持久化,但需要特别注意,master挂掉后,不能自动拉起进程,否则所有数据都会被清空,slave也不能幸免;
  • 配置调优,修改no-appendfsync-on-rewrite,appendfsync两个参数,可以一定程度缓解IO压力;
总体来看,Redis是一个很棒的KV组件,在性能、稳定性、持久化、可用性方面都表现非常出色,但也需要正确使用和优化。

附:

Redis 2.8新特性
  • Slaves 能够和master部分重新同步,所以不用再担心网络瞬断导致master重新向各个 slaves发送完整的RDB文件的彻底的重新同步了。
  • 增加了IPv6支持
  • 添加了slaves 能够明确的ping master,master也能够独立的检测每个slave的超时
  • 当没有足够的给出最大延时的slave链接时,master可以停止接收写操作
  • 设置了进程的名字,方便使用ps命令输出时候能够辨别实例的监听端口或者是一个saveing的子进程
  • 能够绑定多个IP地址
  • 实现了配置重写,当使用CONFIG SET命令修改配置之后使用CONFIG REWRITE的能够同时将配置文件修改。
Redis 2.6新特性
  • 支持lua脚本。
  • VM(虚拟内存)去掉了。
  • 对于client的limit限制变成无限制。
  • aof性能提升了不少。
  • key的过滤时间可以支持毫秒级别了,原来是秒。
  • list与hash 的属性filed或value包含小整数,内存优化列好(使用了jemalloc,以前是malloc)。
  • 提供了BITCOUNT与BITOP,前者支持位值count,后者支持了位操作。(以前只支持key-value 的置位操作)
  • 支持新命令dump以及restore ,即序列化与反序列化操作。
  • 大数据存储性能优化
SA | 评论(0) | 引用(0) | 阅读(100404)