最近接了大数据项目的postgresql运维,刚接过来他们的报表系统就出现高峰期访问不了的问题,报表涉及实时数据和离线数据,离线读pg,实时读redis。而后天然而然就把redis也挪到咱们这边优化了 -_-! 。在此次优化过程当中也是再次深入感觉到redis的各类坑mysql
大数据报表周末晚上高峰期实时报表打不开,基本上处于不能使用状态,实时报表主要访问redis数据,监控发现Redis CPU占用太高,高峰期2个从库实例的CPU达到100%,因为redis是单进程单线程结构,因此单核CPU达到100%致使查询阻塞redis
1主1从 ,应用手动读写分离,持久化主从默认都开启开启rdb持久化,没有作aof,参数基本走默认(灰常牛批 -_-!)sql
1. redis持久化致使阻塞 2. 是否存在慢查询 3. 主从存在频繁全量同步) 4. value值是否过大 5. 架构问题,当前全部业务读取仅在一个从库读取 6. 网络问题 7. 链接数问题
首先看的网络问题,跟运维小伙伴沟经过,结合监控结果发现,网络基本上没有问题,网卡流量也远远没有到瓶颈,首先排除网络问题。可是,在redis从库的日志中,发现有个报错很频繁:后端
47960:S 16 Apr 12:05:36.951 # Connection with master lost. 47960:S 16 Apr 12:05:36.951 * Caching the disconnected master state. 47960:S 16 Apr 12:05:37.799 * Connecting to MASTER 192.168.63.2:6379 47960:S 16 Apr 12:05:37.799 * MASTER <-> SLAVE sync started 47960:S 16 Apr 12:05:37.799 * Non blocking connect for SYNC fired the event. 47960:S 16 Apr 12:05:42.871 * Master replied to PING, replication can continue... 47960:S 16 Apr 12:05:42.873 * Trying a partial resynchronization (request 2cf6338d2d3a72131d5f2f18a0bd8c271302e058:228189063173). 47960:S 16 Apr 12:05:43.085 * Full resync from master: 2cf6338d2d3a72131d5f2f18a0bd8c271302e058:228814173990 47960:S 16 Apr 12:05:43.085 * Discarding previously cached master state.
看字面意思就是主从链接断开了,从库尝试作增量同步还不成功,最后作了全量同步。
WTF???既然网络没问题,为何链接断了。OK,引出主从问题服务器
而后主从出现了频繁全量同步,如上面的日志显示,从库链接断开从连并尝试增量同步失败,结果作了全量同步。这个操做开销很大:主库bgsave->传到从库->从库加载rbd到内存(加载的时候是没法操做redis的)。出现这种状况又有几个缘由。。。网络
repl-backlog-size
client-output-buffer-limit normal client-output-buffer-limit slave client-output-buffer-limit pubsub
关于架构问题,其实早在报表高峰期读取问题出现的初期,大数据的同事就提出增长redis从库实例,作负载均衡的想法了。鉴于redis是单线程模型,只能用到一个cpu核心,多增长几个实例能够多利用到几个cpu核心这个想法确实也没错。当时因为从库物理机有富余的内存资源,因此临时新增了三个从库实例,并添加haproxy轮询访问后端4个redis实例。总体架构变为1主4从+haproxy作从库负载均衡。可是我始终认为,cpu高主要仍是跟具体的业务查询有关,架构扩展应该是在单实例优化到最佳以后才考虑的。这就比如在mysql当中,有大量慢查询致使cpu太高,你光靠扩展从库而不去先优化SQL,扩展到何时是个头呢?架构
慢查询问题:某个促销活动的晚上,大数据报表果真又准时出现打开慢的现象。redis依然是cpu占用率爆满。话很少说进入redis ,slowlog get 50 , 发现慢查询中基本都是keys xxx* 这样的查询,这。。。我几乎确定cpu占用率跟这种慢查询有很大关系了。执行时间在0.5秒左右,0.5秒对于redis来讲应该是很是慢了。若是这样的查询比较多的话,那么redis确实极可能出现阻塞,在看了下value值的大小,应该还好不算大。redis slowlog默认只保存在内存,只保留当前的128条,因此这也算是个小小的麻烦,不太好统计慢查询发生的频率负载均衡
持久化策略:运维
rdb持久化:每次都是全量的bgsave,具体策略下面说。
缺点:
非实时
全量持久化
每次保存 RDB 的时候,Redis 都要 fork() 出一个子进程,并由子进程来进行实际的持久化工做。 在数据集比较庞大时, fork()可能会很是耗时,形成服务器在某某毫秒内中止处理客户端dom
aof持久化:每秒写aof文件,实时性较高,增量写,顺序记录语句,便于误操做恢复
缺点:
bgrewrite重写,fork进程,短暂阻塞
重写时fork进程可能致使swap和OOM(预留1半内存)
简单介绍完两种持久化策略以后,最后给出我实际优化后的策略:
主/从业务库关闭rdb和aof持久化,新增一台从库(不参与业务)单独作rdb持久化,该从库持久化配置:save 900 1 也就是900秒作一次bgrewrite,最多丢失15分钟数据
链接数问题:
这块目前来讲因为作了负载均衡,高峰期看haproxy入口的链接最大也就去到500-600,仍是有阻塞的状况下,每一个redis实例connected_clients最多也就到100左右(能够经过client list查看具体链接),排除链接数的问题。
优化主要避免了持久化,以及频繁主从全量同步带来的性能影响。可是实际主要瓶颈仍是在慢查询,若是keys xxx*这种查询不能避免,那么必定会形成阻塞
下面这张图应该更加生动:
一、主库的used_memory_peak_human达到60.97G,实际上主库的maxmemory只配置了32G
127.0.0.1:6379> info memory
Memory used_memory:3531621728 used_memory_human:3.29G used_memory_rss:70885924864 used_memory_peak:65461144384 used_memory_peak_human:60.97G used_memory_lua:36864 mem_fragmentation_ratio:20.07 mem_allocator:libc
解决方式:内存碎片形成,查看资料说是大量写入形成,目前没有太好的解决方法,只能经过重启进程释放
二、redis过时的key会不会自动删除?策略如何配置
redis过时的key当内存使用maxmemory才会进行删除
maxmemory-policy 六种方式:
* volatile-lru:(默认值)从已设置过时时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰 * volatile-random:从已设置过时时间的数据集(server.db[i].expires)中任意选择数据淘汰 * volatile-ttl : 从已设置过时时间的数据集(server.db[i].expires)中挑选将要过时的数据淘汰 * allkeys-lru : 从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰 * allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰 * noeviction : 禁止驱逐数据,永不过时,返回错误
三、redis主从同步原理(全量/增量)
一张图一目了然:
复制积压缓冲区=repl-backlog
redis2.8以前不支持增量备份
增量备份的两个条件