双11啦,为了给商品详细redis进行扩容,扩容动作就放在了今天晚上进行,很不巧,今天晚上是个多事之秋;

做了次数据恢复,做了次集群迁移,在迁移的时候还踩了个坑!

集群中有个节点挂掉了,并且报错信息如下:
------ STACK TRACE ------

EIP:
/usr/local/bin/redis-server 0.0.0.0:6380 [cluster](migrateCloseSocket+0x52)[0x4644f2] Backtrace:
/usr/local/bin/redis-server 0.0.0.0:6380 [cluster](logStackTrace+0x3c)[0x45bd5c]
/usr/local/bin/redis-server 0.0.0.0:6380 [cluster](sigsegvHandler+0xa1)[0x45cc41]
/lib64/libpthread.so.0[0x336b60f710]
/usr/local/bin/redis-server 0.0.0.0:6380 [cluster](migrateCloseSocket+0x52)[0x4644f2]
/usr/local/bin/redis-server 0.0.0.0:6380 [cluster](migrateCommand+0x7cd)[0x46744d]
/usr/local/bin/redis-server 0.0.0.0:6380 [cluster](call+0x72)[0x424192]
/usr/local/bin/redis-server 0.0.0.0:6380 [cluster](processCommand+0x365)[0x428d75]
/usr/local/bin/redis-server 0.0.0.0:6380 [cluster](processInputBuffer+0x109)[0x435089]
/usr/local/bin/redis-server 0.0.0.0:6380 [cluster](aeProcessEvents+0x13d)[0x41f86d]
/usr/local/bin/redis-server 0.0.0.0:6380 [cluster](aeMain+0x2b)[0x41fb6b]
/usr/local/bin/redis-server 0.0.0.0:6380 [cluster](main+0x370)[0x427220]
/lib64/libc.so.6(__libc_start_main+0xfd)[0x336b21ed1d]
/usr/local/bin/redis-server 0.0.0.0:6380 [cluster][0x41d039]

  

挂掉了之后, 我们用redis_tribe 这个脚本进行对我们的redis 集群进行状态检查,发现有个槽很久都处于import状态和migrate状态之间。
[WARNING] Node 10.112.142.21:7210 has slots in importing state (45).
[WARNING] Node 10.112.142.20:6380 has slots in migrating state (45).
之后我们用 fix 对这个集群进行修复,然后整个集群才ok了。

以下是我们开始尝试着用 rebalance ,让redis 自己来帮我们调整整个集群的solt 分配情况:

[root@GZ-JSQ-JP-REDIS-CLUSTER-142-21 ~]# /usr/local/bin/redis-trib.rb rebalance 10.112.142.21:7211
>>> Performing Cluster Check (using node 10.112.142.21:7211)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Rebalancing across 7 nodes. Total weight = 7
Moving 892 slots from 10.112.142.20:6380 to 10.112.142.21:7210
[ERR] Calling MIGRATE: ERR Target instance replied with error: BUSYKEY Target key name already exists.
>>> Check for open slots...
[WARNING] Node 10.112.142.20:6380 has slots in migrating state (45).
[WARNING] The following slots are open: 45
>>> Fixing open slot 45

*** Found keys about slot 45 in node 10.112.142.21:7210!
Set as migrating in: 10.112.142.20:6380
Set as importing in: 10.112.142.21:7210
Moving slot 45 from 10.112.142.20:6380 to 10.112.142.21:7210:
*** Target key exists. Replacing it for FIX.
/usr/local/lib/ruby/gems/2.3.0/gems/redis-3.3.1/lib/redis/client.rb:121:in `call': MOVED 45 10.112.142.21:6380 (Redis::CommandError)
from /usr/local/bin/redis-trib.rb:942:in `rescue in move_slot'
from /usr/local/bin/redis-trib.rb:937:in `move_slot'
from /usr/local/bin/redis-trib.rb:607:in `fix_open_slot'
from /usr/local/bin/redis-trib.rb:422:in `block in check_open_slots'
from /usr/local/bin/redis-trib.rb:422:in `each'
from /usr/local/bin/redis-trib.rb:422:in `check_open_slots'
from /usr/local/bin/redis-trib.rb:360:in `check_cluster'
from /usr/local/bin/redis-trib.rb:1140:in `fix_cluster_cmd'
from /usr/local/bin/redis-trib.rb:1696:in `<main>'

  

那么很明显,这样是行不通的,报了一些奇奇怪怪的问题,说是我们的key 已经存在了,那么开到这里是不是我们的的集群里边有脏数据了呢?于是我们把这两个节点的所有key 拿出来对比了一番,发现并没有重复的key出现,也就是没有胀数据啦,那怎么办呢?我们集群是一定要扩容的,不然双11肯定是抗不住的。

ok, 还好这个工具提供了另外一种人工迁移solt 的方式【reshard】,知道了这个方式后,我们很愉快的迁移了大部分节点, 但是在迁移第45个solt 的时候又出问题了。出的问题就和上边的类似。

看样子问题是出现在这第45 号solt 身上,如果我们不解决这个问题,这个节点上的负载就会很大,双11 就可能会成为瓶颈。我们看一下这个reshard 都做了哪些动作:
通过redis-trib.rb 源码可以看到:
       cluster setslot imporing

       cluster setslot migrating

       cluster getkeysinslot

       migrate

       setslot node

我们进行重新分片的时候我们会进行这几个操作。那么从上边重新分片失败的情况看,有可能是由于在迁移过程中超时导致的,或者说某个很大的key堵塞了redis导致的。
OK,我们先猜想到这里, 那我们接下来开始验证我们的猜想,看看第45 号solt 是不是有比较大的solt。
首先利用上边给出的信息,我们看一下这个solt 里边都有哪些key:

10.205.142.21:6380> CLUSTER GETKEYSINSLOT 45 100
1) "JIUKUIYOU_COM_GetCouponInfo_3479num"
2) "com.juanpi.api.user_hbase_type"
3) "t2526767"
4) "t2593793"
............

然后呢,我们看一下每一个key序列化后都占了多大的空间:
10.205.142.21:6380> DEBUG OBJECT com.juanpi.api.user_hbase_type
Value at:0x7f973b7226d0 refcount:1 encoding:hashtable serializedlength:489435339 lru:1802371 lru_seconds_idle:3013 【466MB!!!!】
(7.11s)
到此,我们看到了一个蛮大的key,看一下量:

10.205.142.21:6380> HLEN com.juanpi.api.user_hbase_type
-> Redirected to slot [45] located at 10.205.142.20:6380
(integer) 6589164 10.205.142.20:6380> HSCAN com.juanpi.api.user_hbase_type 0
1) "3670016"
2) 1) "6581cc3950e071873763e4b016b66914"
2) "{\"type\":\"A1\",\"time\":1434625093}"
3) "4baca6b94be68d704f348ee0a3e45915"
4) "{\"type\":{\"A\":\"A3\",\"C\":\"C1\"},\"time\":1442105611}"
5) "b83ce222b54890bf4de03cfad2362e9e"
6) "{\"type\":\"A1\",\"time\":1434672804}"
7) "821d1f1a27c63d6d40bc1d969bcec5f6"
8) "{\"type\":{\"A\":\"A6\",\"C\":\"C3\"},\"time\":1435705301}"
9) "cbce79067ad2773b87360fb91b9a325c"
10) "{\"type\":\"A3\",\"time\":1433925484}"
11) "8eef2bc24fd819687e017f7bd1ad8e1c"
12) "{\"type\":{\"A\":\"A6\",\"C\":\"C2\"},\"time\":1435543716}"
13) "d4112308e47066f2e3d35dbcf96ba092"
14) "{\"type\":{\"A\":\"A1\",\"C\":\"C4\"},\"time\":1435141321}"
15) "2ca72205e82f5d9188cc9c56285ea161"
16) "{\"type\":{\"A\":\"A2\",\"C\":\"C3\"},\"time\":1434946176}"
17) "b259ce800a0112dbd316f54aae7679a6"
18) "{\"type\":{\"A\":\"A6\",\"C\":\"\"},\"time\":1441892174}"
19) "039b9011d1470791143563a2660d8dc2"
20) "{\"type\":{\"A\":\"A6\",\"C\":\"C3\"},\"time\":1435442319}"
21) "82def11dfe206501074eb200558fb8a5"
22) "{\"type\":\"C2\",\"time\":1434466702}"

  

到此,问题我们基本锁定就是这个solt里边有个非正常大小的key了,那么到底是不是这个key导致的呢?如果是我们又改如何验证呢?
    首先我们想到的就是能不能跳过这个solt 的迁移,或者说迁移指定的solt呢?
    那么对于迁移指定的solt,对于原始的这个工具里边是没有支持的,而如果要实现的话,我们需要手动拆解这几个步骤,自己实现逻辑
    那么跳过这个solt 呢?看起来稍微的比较容易实现一点,那么我们就修改一下redis-trib.rb 源码好了:

首先我们找到函数入口: def reshard_cluster_cmd(argv,opt)    【大约在1200行左右】
    然后找到这句话:print "Do you want to proceed with the proposed reshard plan (yes/no)? "
    在它下边几行添加个逻辑:判读当前solt是否是 45 solt,如果是则跳过,如果不是则迁移

if !opt['yes']
print "Do you want to proceed with the proposed reshard plan (yes/no)? "
yesno = STDIN.gets.chop
exit(1) if (yesno != "yes")
end
reshard_table.each{|e|
xputs "------------------------> #{e[:slot]}"
case e[:slot]
when 45
puts "sb 45"
else
move_slot(e[:source],target,e[:slot],
:dots=>true,
:pipeline=>opt['pipeline'])
END
END
END

Ok,我们就来试一把吧,看看跳过这个solt之后, 我们重新分片是否成功!那么结果符合我们的猜测,就是由于这个solt 里边有个超大的key导致的。事后通过更业务方商量,这个key是个无效的key,可以删掉的!呵呵

最后,问题到此已经顺利解决了。

总结一下:
有时候报错的信息不一定能够准确的反应问题所在,我们需要清理在报错期间我们执行了什么操作,这个操作的具体步骤有哪些,涉及道德这个系统的原理又是怎么样的。通过一步步的推理、猜测、验证最终得到问题的解答。

上边那种粗暴的方法来修改源码其实还有没有考虑到的地方,
     比如有些除了这个solt之外又没有其它solt 有类似这个大的key呢?
     是不是应该在迁移solt前,对整个solt的key大小进行一次扫描,检查呢?

解决 Redis Cluster 扩容故障的更多相关文章

  1. 解决Redis Cluster模式下的排序问题

    通常的redis排序我们可以这么做: 比如按商品价格排序:sort goods_id_set by p_*_price 这样在非集群模式下是没问题的,但如果在集群模式下,就会报错: 说是在集群模式下不 ...

  2. redis cluster 添加/删除节点操作

    RedisCluster 添加/删除节点 添加节点新配置两个测试节点8008和9009 [root@--- ~]# /usr/local/redis-/bin/redis-server /u02/re ...

  3. redis cluster 集群畅谈(三) 之 水平扩容、slave自动化迁移

    上一篇http://www.cnblogs.com/qinyujie/p/9029522.html, 主要讲解 实验多master写入.读写分离.实验自动故障切换(高可用性),那么本篇我们就来聊了聊r ...

  4. Redis Cluster 自动化安装,扩容和缩容

    Redis Cluster 自动化安装,扩容和缩容 之前写过一篇基于python的redis集群自动化安装的实现,基于纯命令的集群实现还是相当繁琐的,因此官方提供了redis-trib.rb这个工具虽 ...

  5. redis cluster异地数据迁移,扩容,缩容

    由于项目的服务器分布在重庆,上海,台北,休斯顿,所以需要做异地容灾需求.当前的mysql,redis cluster,elastic search都在重庆的如果重庆停电了,整个应用都不能用了. 现在考 ...

  6. Redis Cluster 集群扩容与收缩

    http://blog.csdn.net/men_wen/article/details/72896682 Redis 学习笔记(十五)Redis Cluster 集群扩容与收缩 标签: redis集 ...

  7. 【精】搭建redis cluster集群,JedisCluster带密码访问【解决当中各种坑】!

    转: [精]搭建redis cluster集群,JedisCluster带密码访问[解决当中各种坑]! 2017年05月09日 00:13:18 冉椿林博客 阅读数:18208  版权声明:本文为博主 ...

  8. redis集群报Jedis does not support password protected Redis Cluster configurations异常解决办法

    解决spring-data-redis操作redis集群报“Jedis does not support password protected Redis Cluster configurations ...

  9. redis:CLUSTER cluster is down 解决方法

    redis:CLUSTER cluster is down 解决方法 首先进入安装目录的src下: 1. ./redis-trib.rb check 127.0.0.1:7001 检查问题所在 2.  ...

随机推荐

  1. [问题2014S04] 复旦高等代数II(13级)每周一题(第四教学周)

    [问题2014S04]  设 \(A\in M_n(\mathbb{C})\) 为可对角化的 \(n\) 阶复方阵, \(f(x)\in\mathbb{C}[x]\) 为复系数多项式, 证明: \[B ...

  2. int与string之间的类型转换--示例

    package demo; public class IntDemo { public static void main(String[] args) { // String-->int 类型转 ...

  3. Java动态代理的两种实现方式:

    方式一:传统的代理 package cn.hc.domain; import java.lang.reflect.InvocationHandler; import java.lang.reflect ...

  4. centos7 中libgdiplus的安装

    yum -y install libtool* git clone git://github.com/mono/libgdiplus.git cd libgdiplus ./autogen.sh -- ...

  5. 《BI项目笔记》数据源视图设置

    目的数据源视图是物理源数据库和分析维度与多维数据集之间的逻辑数据模型.在创建数据源视图时,需要在源数据库中指定包含创建维度和多维数据集所需要的数据表格和视图.BIDS与数据库连接,读取表格和视图定义, ...

  6. HDU-4531 吉哥系列故事——乾坤大挪移 模拟

    题意:给定一个九宫格,然后能够选择某行或者是某列滚动,每个小方格分为上下左右四个块,每个块可以涂上4种不同的颜色.问最少使用多少步能够使得所有相同颜色相互联通. 分析:由于九宫格的所有的状态只有9!( ...

  7. HTML5/CSS3hack

    以下兼容技术我只测试了IE8+ Media Query 媒体查询 <script src="respond.min.js"></script> respon ...

  8. java高薪之路__010_设计模式

    设计模式只是一个在构建大型工程时,为了方便更改,添加,查询和管理的一种代码工具,没有必要单独为了设计模式而使用设计模式,使简单的事情复杂化. 总体来说设计模式分为三大类: 1. 创建型模式,共五种 - ...

  9. 《高级Web应用程序设计》课程学习资料

    任务1:什么是ASP.NET MVC 1.1  ASP.NET MVC简介 1.2 认识ASP.NET MVC项目结构 1.3 ASP.NET MVC生命周期 任务2:初识ASP.NET MVC项目开 ...

  10. CSS中!important的优先级

    本篇文章使用最新的IE10以及firefox与chrome测试(截止2013年5月27日22:23:22) CSS的原理: 我们知道,CSS写在不同的地方有不同的优先级, .css文件中的定义 < ...