Redis 实例排除步骤

Redis 应用案例 - 在问题中不断成长

原创 2017-02-05 杜亦舒

本文翻译整理自 Andy Grunwald 发布的一篇文章,写的是作者所在公司使用 Redis 时遇到的问题,以及处理过程,在不断解决调整中积累了很多 Redis 的使用经验

背景

产品类型:酒店搜索

技术选型:前端 PHP + 后端 Java,都会用到 Redis

Redis 使用场景:缓存、数据持久化前的临时存储

2010年开始应用 Redis,PHP 对其操作时使用的是 Predis 这个客户端库

2013年改用了 phpredis 作为客户端库

2014年开始出现问题

问题描述

用户量快速增长,访问量在短时间内翻倍,由于前期容量规划做得比较好,硬件资源可以支撑,可是软件系统方面出现了大问题:

40% 的请求都会返回 HTTP 500: Internal Server Error

通过查看日志,发现错误是在 PHP <-> Redis 的连接处理上

调试处理

第1次

刚开始时并没有找到根本原因,只能尝试各种与错误相关的办法,例如:

  1. 增加 PHP 连接数,并把超时时间从 500ms 增加到 2.5s
  2. 禁止掉 PHP 设置中的 default_socket_timeout
  3. 在主机系统中禁止掉 SYN cookies
  4. 检查 Redis 和 Webservers 的文件描述符数量
  5. 增加主机系统的 mbuffer
  6. 调整 TCP backlog 数量

……

尝试了很多方法,但全部无效

第2次

想在预发布环境中重现这个问题,可惜,还是没成功,应为流量不够大,无法复现

第3次

会不会是代码中没有关闭 Redis 连接呢?

正常来讲,PHP在执行结束时会自动关闭资源连接,但老版本中会有内存泄漏的问题,保险起见,把代码都修改一遍,手动关闭连接

结果还是无效

第4次

怀疑目标:phpredis 这个客户端库

做 A/B 测试,替换回 predis 这个库,部署到数据中心中 20% 的用户量上

得益于良好的代码结构,替换工作很快完成

可结果依旧是无效,但也有好的一面,可以证明 phpredis 没问题嘛

第5次

查看了一下 Redis 的版本,是 v2.6,当时最新版本是 v2.8.9

升级 Redis 试一下吧,升完后还是不行

没事儿,要保持乐观,这不顺便把 Redis 版本升为最新的了

第6次

通过查找大量文档,在官方文档中发现了一个调试好方法 Redis Software Watchdog,打开后执行:

$ redis-cli --latency -p 6380 -h 1.2.3.4 min: 0, max: 463, avg: 2.03 (19443 samples)

查看 Redis 日志:

... [20398] 22 May 09:20:55.351 * 10000 changes in 60 seconds. Saving... [20398] 22 May 09:20:55.759 * Background saving started by pid 41941 [41941] 22 May 09:22:48.197 * DB saved on disk [20398] 22 May 09:22:49.321 * Background saving terminated with success [20398] 22 May 09:25:23.299 * 10000 changes in 60 seconds. Saving... [20398] 22 May 09:25:23.644 * Background saving started by pid 42027 ...

发现了问题

每隔几分钟就向硬盘保存一次数据,fork 一个后台存储进行为什么需要大概 400ms(通过上面日志的第1条和第2条的时间可以看出来)

到这儿,终于找到问题的根源了,因为 Redis 实例中有大量的数据,导致每次持久化操作 fork 后台进程时非常耗时,并且在他们的业务中经常修改key,又导致了频繁触发持久化,也就经常产生对 Redis 的阻塞

处理办法:使用单独的 slave 来做持久化

这个 slave 不处理真实的流量请求,唯一的作用就是处理持久化,把之前 Redis 实例上的持久化操作转移到这个 slave 上

效果非常明显,问题基本解决,但有的时候还是会报错

第7次

排查可能阻塞 Redis 的慢查询,发现有地方使用了 keys *

因为 Redis 中的数据越来越多,这个命令自然会产生严重阻塞

可以使用 scan 进行替换

第8次

经过前面的调整,问题已经解决,随后的几个月,即使流量在不断增长,也都抗住了

但他们意识到了新的问题

现在的方式是,来一个请求就创建一个 Redis 连接,执行几个命令,然后再断开连接,在请求量很大时,这个方式产生了严重的性能浪费,一半以上的命令是用来处理连接操作的,这都超过了业务逻辑上的处理,也使 Redis 变慢

解决方法:引入 proxy,他们选择了 twitter 的 twemproxy,只需要在每个 webserver 上安装代理,twemproxy负责与 Redis 实例进行持久连接,这样就大大减少了连接方面的操作

twemproxy还有两个方便的地方:

  1. 支持 memcached
  2. 可以阻止非常耗时或者危险的命令,例如 keys、flushall

效果自然很完美,再也不用担心之前的连接错误

附上教程 twemproxy 代理redis

第9次

通过数据分片来继续优化:

  1. 对不同上下文的数据拆分隔离
  2. 对相同上下文的数据进行一致性哈希分片

效果:

  1. 减少了每台机器上的请求、负载
  2. 提升了缓存的可靠性,不担心节点故障

小结

原文作者写的非常好,详细的描述了他们在 Redis 应用上的成长历程,是很值得参考的实践经验

原文地址

http://tech.trivago.com/2017/01/25/learn-redis-the-hard-way-in-production
时间: 2024-08-05 19:13:01

Redis 实例排除步骤的相关文章

使用Redis分区将数据分割到多个Redis实例

分区是将所有的数据分割到多个Redis实例的过程,所以每个Redis实例存放的是所有键值的子集. Redis分区主要有两个目标:1)允许使用多台计算机的内存来存放更大的数据.如果不做分区的话,单台计算机的内存又限制. 2)使用多台计算的计算能力和网络带宽. 有许多不同的分区场景, 参考资料: http://redis.io/topics/partitioning

在一台机器上搭建多个redis实例

默认Redis程序安装在/usr/local/redis目录下: 配置文件:/usr/local/redis/redis.conf,该配置文件中配置的端口为默认端口:6379: Redis的启动命令路径:/usr/local/bin/redis-server. 可以指定端口启动多个Redis进程. #/usr/local/bin/redis-server --port 6380 &    #启动6380端口的redis实例. ====================以下每个进程对应一个配置文件(

redis的使用:获取redis实例的工具类

package com.wanhua.util; import java.util.HashMap;import java.util.Map;import java.util.Set;import java.util.logging.Logger; import play.Play; import redis.clients.jedis.Jedis;import redis.clients.jedis.JedisPool;import redis.clients.jedis.JedisPoolC

CentOS 6.5下Redis安装详细步骤

CentOS 6.5下Redis安装详细步骤 Redis简介: Redis是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,并提供多种语言的API.从2010年3月15日起,Redis的开发工作由VMware主持. redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset(sorted set –有序集合)和hash(哈希类

一个Redis实例适合存储不同应用程序的数据吗?

Redis支持多个数据库,并且每个数据库的数据是隔离的不能共享,并且基于单机才有,如果是集群就没有数据库的概念. Redis是一个字典结构的存储服务器,而实际上一个Redis实例提供了多个用来存储数据的字典,客户端可以指定将数据存储在哪个字典中.这与我们熟知的在一个关系数据库实例中可以创建多个数据库类似,所以可以将其中的每个字典都理解成一个独立的数据库. 每个数据库对外都是一个从0开始的递增数字命名,Redis默认支持16个数据库(可以通过配置文件支持更多,无上限),可以通过修改redis.co

centos安装redis并开启多个redis实例

1.下载安装包 下载地址 :  http://download.redis.io/releases/,去里面找对应的版本下载 例如  wget http://download.redis.io/releases/redis-5.0.0.tar.gz 2.解压 tar -zxf  redis-5.0.0.tar.gz 3.编译安装 cd redis-5.0.0.tar.gz make && make install 4.修改配置文件 1.将utils下面的redis_init_script复

redis-full-check校验2个不同redis实例数据

redis-full-check校验redis数据是否一致: 校验2个不同的redis实例数据: 6986 为redis实例一6987 为redis实例二 登录6986 redis实例一,模拟设置4个key值 [[email protected] redis-full-check-1.4.7]# redis-cli -h 127.0.0.1 -p 6986 -a 'Y2hJKSGtuEq' Warning: Using a password with '-a' option on the com

Mysql 多实例实施步骤

基本理论:利用同一套安装程序,不同配置文件,不同启动程序,不同数据目录.有公用资源,也有私有资源. 实现步骤: 1.正常安装mysql,二进制安装或者编译安装. 2.创建mysql多实例总目录,总目录下面创建分目录,如3306,3307等目录,各示例目录下创建data文件夹用于数据文件夹. 3.cp support-files目录下small配置文件到3306,3307目录,修改必要的参数如下:sock目录,数据目录,server-id,监听端口号等. 4.总data目录权限设置 chown -

Mysql 5.5多实例部署步骤

由于临时的需求,需要在一台主机上启动多个mysql的实例,对应不同的端口. 步骤如下: 1,安装mysql. 2,分别设置不同实例的配置文件. 3,创建不同实例的datadir,并执行数据库初始化. 4,启动不同的mysql实例. 以下是详细的安装方法: 1,安装mysql 使用源码安装. tar -zxvf mysql-5.5.49.tar.gz cd mysql-5.5.49 mkdir /usr/local/mysql cmake - -DCMAKE_INSTALL_PREFIX=/usr