redis进阶(复制与集群) weir 2016-10-12 22:25:59.0 redis 3415 接着上一次说起,赏析我们聊了redis的持久化RDB和AOF,结论也很模糊,因为这里更多的是考虑你们的应用场景和使用的力度。比如一个电商系统,可以说百分之90的东西都可以放在redis里面,可以想象如此大力度的使用redis缓存那对他的管理也是要花费相当大的精力的,管理不好整个系统就会收到很大的冲击和影响。所以说对于大规模使用此类内存缓存的东西都要做大量的工作和准备包括测试、容灾、高可用等方面的周密计划和测试。 那今天我们再说说redis的复制和集群,那我们现在说的都是在redis3.X之后的版本了之前的我们就不说了,先说redis的复制,这方面redis做得相当简单好用,几个命令就搞定了。那我们首先认清楚复制是干吗用的?复制其实就是构建主从和读写分离,为高可用做准备,对于主从 我们只需要在从节点上面这样做slaveof 192.168.38.13 6379 (主节点的IP和端口)这样就可以了。我现在想说说高可用的问题大致有几种方案: 1. Keepalived 2.zookeeper 3.sentinel(这个是官方提供的) 这三种方案使用要看你对数据的容忍程度,keepalived会有丢失数据的问题,zookeeper配置起来复杂,sentinel最新的版本在主从的架构下做得还很好,客户端做了对Sentinel的支持,让客户端可以知道发生了故障切换从而获取新的主节点。不好的是在集群情况下的数据分片Sentinel的ShardedJedisPool无法感知分片的主从切换,所以网上有人自己写了一个东西做到了即使在分片情况下也是可以的: http://warm-breeze.iteye.com/blog/2020413 该项目的GitHub主页: https://github.com/warmbreeze/sharded-jedis-sentinel-pool 从这点来看redis还需要完善,这么一来redis的HA高可用就可以解决了,而且需要做的工作很少就能实现。 说完复制我们再说说集群,redis3之后集群做的也是相当不错,我看到也有公司自己实现了一套:https://www.oschina.net/p/codis 我相信随着redis 的发展官方会做得越来越好,根本不需要自己实现这东西 修改redis.conf文件: port 6379 pidfile "/var/run/redis_6379.pid" logfile "redis_6379.log" dbfilename "dump_6379.rdb" cluster-enabled yes cluster-config-file nodes_6379.conf 我本来想做6个的结果弄了7个,这倒没什么。 然后都把他们启动了: [root@h3 redis-3.2.4]# src/redis-server redis_6379.conf [root@h3 redis-3.2.4]# src/redis-server redis_6380.conf [root@h3 redis-3.2.4]# src/redis-server redis_6381.conf [root@h3 redis-3.2.4]# src/redis-server redis_6382.conf [root@h3 redis-3.2.4]# src/redis-server redis_6383.conf [root@h3 redis-3.2.4]# src/redis-server redis_6384.conf [root@h3 redis-3.2.4]# src/redis-server redis_6385.conf [root@h3 redis-3.2.4]# ll 总用量 548 -rw-rw-r-- 1 root root 77971 9月 26 15:10 00-RELEASENOTES -rw-rw-r-- 1 root root 53 9月 26 15:10 BUGS -rw-rw-r-- 1 root root 1805 9月 26 15:10 CONTRIBUTING -rw-rw-r-- 1 root root 1487 9月 26 15:10 COPYING drwxrwxr-x 7 root root 4096 10月 9 03:50 deps -rw-rw-r-- 1 root root 11 9月 26 15:10 INSTALL -rw-rw-r-- 1 root root 151 9月 26 15:10 Makefile -rw-rw-r-- 1 root root 4223 9月 26 15:10 MANIFESTO -rw-r--r-- 1 root root 112 10月 13 01:20 nodes_6379.conf -rw-r--r-- 1 root root 112 10月 13 01:21 nodes_6380.conf -rw-r--r-- 1 root root 112 10月 13 01:21 nodes_6381.conf -rw-r--r-- 1 root root 112 10月 13 01:21 nodes_6382.conf -rw-r--r-- 1 root root 112 10月 13 01:21 nodes_6383.conf -rw-r--r-- 1 root root 112 10月 13 01:21 nodes_6384.conf -rw-r--r-- 1 root root 112 10月 13 01:21 nodes_6385.conf -rw-rw-r-- 1 root root 6834 9月 26 15:10 README.md -rw-rw-r-- 1 root root 46823 10月 12 17:21 redis_6379.conf -rw-r--r-- 1 root root 2355 10月 13 01:20 redis_6379.log -rw-r--r-- 1 root root 46823 10月 12 17:25 redis_6380.conf -rw-r--r-- 1 root root 2355 10月 13 01:21 redis_6380.log -rw-r--r-- 1 root root 46823 10月 12 17:25 redis_6381.conf -rw-r--r-- 1 root root 2355 10月 13 01:21 redis_6381.log -rw-r--r-- 1 root root 46823 10月 12 17:25 redis_6382.conf -rw-r--r-- 1 root root 2355 10月 13 01:21 redis_6382.log -rw-r--r-- 1 root root 46823 10月 12 17:26 redis_6383.conf -rw-r--r-- 1 root root 2355 10月 13 01:21 redis_6383.log -rw-r--r-- 1 root root 46823 10月 12 17:26 redis_6384.conf -rw-r--r-- 1 root root 2355 10月 13 01:21 redis_6384.log -rw-r--r-- 1 root root 46823 10月 12 17:26 redis_6385.conf -rw-r--r-- 1 root root 2355 10月 13 01:21 redis_6385.log -rwxrwxr-x 1 root root 271 9月 26 15:10 runtest -rwxrwxr-x 1 root root 280 9月 26 15:10 runtest-cluster -rwxrwxr-x 1 root root 281 9月 26 15:10 runtest-sentinel -rw-rw-r-- 1 root root 7746 10月 10 22:33 sentinel.conf drwxrwxr-x 2 root root 4096 10月 9 03:51 src drwxrwxr-x 10 root root 4096 9月 26 15:10 tests drwxrwxr-x 7 root root 4096 9月 26 15:10 utils [root@h3 redis-3.2.4]# 启动后就可以进入每个里面了: 127.0.0.1:6379> info 127.0.0.1:6379> cluster info 127.0.0.1:6379> cluster nodes 就可以看一下这些命令 会输出什么。 把节点放在一个集群中: 127.0.0.1:6379> cluster meet 127.0.0.1 6380 OK 127.0.0.1:6379> cluster meet 127.0.0.1 6381 OK 127.0.0.1:6379> cluster meet 127.0.0.1 6382 OK 127.0.0.1:6379> cluster meet 127.0.0.1 6383 OK 127.0.0.1:6379> cluster meet 127.0.0.1 6384 OK 127.0.0.1:6379> 完了之后会看到: 127.0.0.1:6379> cluster nodes f11865bc1527f475d0a4dc00a0e16672393f723c 127.0.0.1:6383 master - 0 1476293706001 4 connected 32d74dfdf80c6e3a1ab6047ac4c3f7fe4e663cf8 127.0.0.1:6379 myself,master - 0 0 1 connected d56878882ec70a7a2957847c075c589607f58f5a 127.0.0.1:6381 master - 0 1476293708032 2 connected 46b16111a7752db0323befc50f7aea8686044f99 127.0.0.1:6384 master - 0 1476293703978 5 connected d82bcdeaf13b485ff824ee0d1e7ca6f2fc19f579 127.0.0.1:6380 master - 0 1476293704482 3 connected f41f20523ecd51f2718176705b73c733544f3b90 127.0.0.1:6382 master - 0 1476293707014 0 connected 127.0.0.1:6379> 再看: 127.0.0.1:6379> cluster info cluster_state:fail cluster_slots_assigned:0 cluster_slots_ok:0 cluster_slots_pfail:0 cluster_slots_fail:0 cluster_known_nodes:6 cluster_size:0 cluster_current_epoch:5 cluster_my_epoch:1 cluster_stats_messages_sent:298 cluster_stats_messages_received:298 127.0.0.1:6379> 接下来设置三主三从: 6379---6382 6380---6383 6381---6384 记住是在从节点上设置: 127.0.0.1:6382> cluster nodes f11865bc1527f475d0a4dc00a0e16672393f723c 127.0.0.1:6383 master - 0 1476293917807 4 connected d82bcdeaf13b485ff824ee0d1e7ca6f2fc19f579 127.0.0.1:6380 master - 0 1476293918313 3 connected f41f20523ecd51f2718176705b73c733544f3b90 127.0.0.1:6382 myself,master - 0 0 0 connected 46b16111a7752db0323befc50f7aea8686044f99 127.0.0.1:6384 master - 0 1476293919323 5 connected 32d74dfdf80c6e3a1ab6047ac4c3f7fe4e663cf8 127.0.0.1:6379 master - 0 1476293920333 1 connected d56878882ec70a7a2957847c075c589607f58f5a 127.0.0.1:6381 master - 0 1476293917302 2 connected 127.0.0.1:6382> CLUSTER REPLICATE 32d74dfdf80c6e3a1ab6047ac4c3f7fe4e663cf8 OK 127.0.0.1:6382> cluster nodes f11865bc1527f475d0a4dc00a0e16672393f723c 127.0.0.1:6383 master - 0 1476293993242 4 connected d82bcdeaf13b485ff824ee0d1e7ca6f2fc19f579 127.0.0.1:6380 master - 0 1476293992232 3 connected f41f20523ecd51f2718176705b73c733544f3b90 127.0.0.1:6382 myself,slave 32d74dfdf80c6e3a1ab6047ac4c3f7fe4e663cf8 0 0 0 connected 46b16111a7752db0323befc50f7aea8686044f99 127.0.0.1:6384 master - 0 1476293991222 5 connected 32d74dfdf80c6e3a1ab6047ac4c3f7fe4e663cf8 127.0.0.1:6379 master - 0 1476293990210 1 connected d56878882ec70a7a2957847c075c589607f58f5a 127.0.0.1:6381 master - 0 1476293989202 2 connected 127.0.0.1:6382> 剩余两个一样。 接下来分配插槽(命令方式): 127.0.0.1:6379> CLUSTER SLOTS (empty list or set) 127.0.0.1:6379> CLUSTER ADDSLOTS 1 8 10 OK 127.0.0.1:6379> CLUSTER SLOTS 1) 1) (integer) 1 2) (integer) 1 3) 1) "127.0.0.1" 2) (integer) 6379 3) "32d74dfdf80c6e3a1ab6047ac4c3f7fe4e663cf8" 4) 1) "127.0.0.1" 2) (integer) 6382 3) "f41f20523ecd51f2718176705b73c733544f3b90" 2) 1) (integer) 8 2) (integer) 8 3) 1) "127.0.0.1" 2) (integer) 6379 3) "32d74dfdf80c6e3a1ab6047ac4c3f7fe4e663cf8" 4) 1) "127.0.0.1" 2) (integer) 6382 3) "f41f20523ecd51f2718176705b73c733544f3b90" 3) 1) (integer) 10 2) (integer) 10 3) 1) "127.0.0.1" 2) (integer) 6379 3) "32d74dfdf80c6e3a1ab6047ac4c3f7fe4e663cf8" 4) 1) "127.0.0.1" 2) (integer) 6382 3) "f41f20523ecd51f2718176705b73c733544f3b90" 127.0.0.1:6379> 而且在每一个节点上面都可以看到。 目前我不知道大家有没有更好地方法分配插槽,还有一种办法是去修改配置文件: 32d74dfdf80c6e3a1ab6047ac4c3f7fe4e663cf8 127.0.0.1:6379 myself,master - 0 0 1 connected 0-6000 f41f20523ecd51f2718176705b73c733544f3b90 127.0.0.1:6382 slave 32d74dfdf80c6e3a1ab6047ac4c3f7fe4e663cf8 0 1476295542796 1 connected d82bcdeaf13b485ff824ee0d1e7ca6f2fc19f579 127.0.0.1:6380 master - 0 1476295540780 3 connected 6001-12000 f11865bc1527f475d0a4dc00a0e16672393f723c 127.0.0.1:6383 slave d82bcdeaf13b485ff824ee0d1e7ca6f2fc19f579 0 1476295541789 4 connected d56878882ec70a7a2957847c075c589607f58f5a 127.0.0.1:6381 master - 0 1476295536714 2 connected 12001-16383 46b16111a7752db0323befc50f7aea8686044f99 127.0.0.1:6384 slave d56878882ec70a7a2957847c075c589607f58f5a 0 1476295543704 5 connected vars currentEpoch 5 lastVoteEpoch 0 这是其中的一份 集群中每一分都要这样修改,其实还是很麻烦的。 弄完之后启动redis: [root@h3 redis-3.2.4]# src/redis-cli -p 6379 127.0.0.1:6379> CLUSTER INFO cluster_state:ok cluster_slots_assigned:16384 cluster_slots_ok:16384 cluster_slots_pfail:0 cluster_slots_fail:0 cluster_known_nodes:6 cluster_size:3 cluster_current_epoch:5 cluster_my_epoch:1 cluster_stats_messages_sent:801 cluster_stats_messages_received:68 127.0.0.1:6379> CLUSTER NODES 32d74dfdf80c6e3a1ab6047ac4c3f7fe4e663cf8 127.0.0.1:6379 myself,master - 0 0 1 connected 0-6000 f41f20523ecd51f2718176705b73c733544f3b90 127.0.0.1:6382 slave 32d74dfdf80c6e3a1ab6047ac4c3f7fe4e663cf8 0 1476295566030 1 connected d82bcdeaf13b485ff824ee0d1e7ca6f2fc19f579 127.0.0.1:6380 master - 0 1476295569063 3 connected 6001-12000 f11865bc1527f475d0a4dc00a0e16672393f723c 127.0.0.1:6383 slave d82bcdeaf13b485ff824ee0d1e7ca6f2fc19f579 0 1476295568052 4 connected d56878882ec70a7a2957847c075c589607f58f5a 127.0.0.1:6381 master - 0 1476295567042 2 connected 12001-16383 46b16111a7752db0323befc50f7aea8686044f99 127.0.0.1:6384 slave d56878882ec70a7a2957847c075c589607f58f5a 0 1476295570074 5 connected 127.0.0.1:6379> CLUSTER SLOTS 1) 1) (integer) 0 2) (integer) 6000 3) 1) "127.0.0.1" 2) (integer) 6379 3) "32d74dfdf80c6e3a1ab6047ac4c3f7fe4e663cf8" 4) 1) "127.0.0.1" 2) (integer) 6382 3) "f41f20523ecd51f2718176705b73c733544f3b90" 2) 1) (integer) 6001 2) (integer) 12000 3) 1) "127.0.0.1" 2) (integer) 6380 3) "d82bcdeaf13b485ff824ee0d1e7ca6f2fc19f579" 4) 1) "127.0.0.1" 2) (integer) 6383 3) "f11865bc1527f475d0a4dc00a0e16672393f723c" 3) 1) (integer) 12001 2) (integer) 16383 3) 1) "127.0.0.1" 2) (integer) 6381 3) "d56878882ec70a7a2957847c075c589607f58f5a" 4) 1) "127.0.0.1" 2) (integer) 6384 3) "46b16111a7752db0323befc50f7aea8686044f99" 127.0.0.1:6379> 会看到集群ok了。 之后就可以插入数据了: 127.0.0.1:6379> CLUSTER KEYSLOT k1 (integer) 12706 127.0.0.1:6379> CLUSTER KEYSLOT k2 (integer) 449 127.0.0.1:6379> set k1 v1 (error) MOVED 12706 127.0.0.1:6381 127.0.0.1:6379> get k1 (error) MOVED 12706 127.0.0.1:6381 127.0.0.1:6379> exit [root@h3 redis-3.2.4]# src/redis-cli -c -p 6379 127.0.0.1:6379> get k1 -> Redirected to slot [12706] located at 127.0.0.1:6381 "v1" 127.0.0.1:6381> CLUSTER KEYSLOT k1 告诉我们k1应该数据存放在12706插槽上,对我现在的是6381,,所以当我127.0.0.1:6379> set k1 v1 (error) MOVED 12706 127.0.0.1:6381 报错了,我需要切换到6381上面才能执行: [root@h3 ~]# /home/redis-3.2.4/src/redis-cli -p 6381 127.0.0.1:6381> ket k1 (error) ERR unknown command 'ket' 127.0.0.1:6381> get k1 (nil) 127.0.0.1:6381> set k1 v1 OK 127.0.0.1:6381> 当我在6379上面get时还是报错: 127.0.0.1:6379> get k1 (error) MOVED 12706 127.0.0.1:6381 所以我退出来重连了一次: [root@h3 redis-3.2.4]# src/redis-cli -c -p 6379 127.0.0.1:6379> get k1 -> Redirected to slot [12706] located at 127.0.0.1:6381 "v1" 127.0.0.1:6381> 我多加了个-c 然后再去get k1 时直接重定向到了6381了。 [root@h3 redis-3.2.4]# src/redis-cli -p 6379 127.0.0.1:6379> set k2 v2 OK 127.0.0.1:6379> set k3 v3 OK 127.0.0.1:6379> set k4 v4 (error) MOVED 8455 127.0.0.1:6380 127.0.0.1:6379> keys * 1) "k3" 2) "k2" 127.0.0.1:6379> [root@h3 ~]# /home/redis-3.2.4/src/redis-cli -p 6382 127.0.0.1:6382> keys * 1) "k3" 2) "k2" 127.0.0.1:6382> get k2 (error) MOVED 449 127.0.0.1:6379 127.0.0.1:6382> 大家从这两个命令块看出问题了么? 集群下的从节点读都不可以。 127.0.0.1:6382> keys * 可以看到但是不能 get命令。 我们手动关闭一个master 比如6380: 1) "k4" 127.0.0.1:6380> SHUTDOWN not connected> exit 因为6380的从节点是6383,我们看它的信息: 127.0.0.1:6383> CLUSTER NODES d56878882ec70a7a2957847c075c589607f58f5a 127.0.0.1:6381 master - 0 1476307340178 2 connected 12001-16383 d82bcdeaf13b485ff824ee0d1e7ca6f2fc19f579 127.0.0.1:6380 master,fail - 1476307303581 1476307301560 3 disconnected f41f20523ecd51f2718176705b73c733544f3b90 127.0.0.1:6382 slave 32d74dfdf80c6e3a1ab6047ac4c3f7fe4e663cf8 0 1476307333086 1 connected 46b16111a7752db0323befc50f7aea8686044f99 127.0.0.1:6384 slave d56878882ec70a7a2957847c075c589607f58f5a 0 1476307335109 5 connected 32d74dfdf80c6e3a1ab6047ac4c3f7fe4e663cf8 127.0.0.1:6379 master - 0 1476307339157 1 connected 0-6000 f11865bc1527f475d0a4dc00a0e16672393f723c 127.0.0.1:6383 myself,master - 0 0 6 connected 6001-12000 127.0.0.1:6383> CLUSTER INFO cluster_state:ok cluster_slots_assigned:16384 cluster_slots_ok:16384 cluster_slots_pfail:0 cluster_slots_fail:0 cluster_known_nodes:6 cluster_size:3 cluster_current_epoch:6 cluster_my_epoch:6 cluster_stats_messages_sent:26396 cluster_stats_messages_received:25746 127.0.0.1:6383> keys * 1) "k4" 127.0.0.1:6383> set k5 v5 (error) MOVED 12582 127.0.0.1:6381 127.0.0.1:6383> set k4 v444 OK 127.0.0.1:6383> keys * 1) "k4" 127.0.0.1:6383> get k4 "v444" 127.0.0.1:6383> 自动升级为master 如果6380这个master又启动了呢? [root@h3 redis-3.2.4]# src/redis-server redis_6380.conf [root@h3 ~]# /home/redis-3.2.4/src/redis-cli -p 6380 127.0.0.1:6380> CLUSTER NODES f11865bc1527f475d0a4dc00a0e16672393f723c 127.0.0.1:6383 master - 0 1476307607467 6 connected 6001-12000 32d74dfdf80c6e3a1ab6047ac4c3f7fe4e663cf8 127.0.0.1:6379 master - 0 1476307608880 1 connected 0-6000 d82bcdeaf13b485ff824ee0d1e7ca6f2fc19f579 127.0.0.1:6380 myself,slave f11865bc1527f475d0a4dc00a0e16672393f723c 0 0 3 connected 46b16111a7752db0323befc50f7aea8686044f99 127.0.0.1:6384 slave d56878882ec70a7a2957847c075c589607f58f5a 0 1476307608476 5 connected d56878882ec70a7a2957847c075c589607f58f5a 127.0.0.1:6381 master - 0 1476307605380 2 connected 12001-16383 f41f20523ecd51f2718176705b73c733544f3b90 127.0.0.1:6382 slave 32d74dfdf80c6e3a1ab6047ac4c3f7fe4e663cf8 0 1476307609486 1 connected 127.0.0.1:6380> 6380节点 就变成了 6383 的从节点了。 那如果是从节点挂了呢?这里有两种情况: 1. 该从节点的主节点没有问题正常运行的,那从节点再次运行后还可以加入集群,严谨一点就是slave挂了但是master没有变化且运行正常的slave重新启动后可以自动加入集群 2. 该从节点的主节点如果也出了问题,那从节点就要通过修改配置的方式从新找一个正常运行的主节点才能加入集群,严谨一点说就是slave挂了切它之前的master也发生了变化(没有正常运行或者变成了别的节点的slave),这是该slave还想加入集群就必须修改配置去指定一个新的master才可以加入集群 这些都是手动的搭建集群。 后面写一些理论的: 复制的配置 主数据库不做配置;从数据库需要在配置中设置“slaveof 主数据库ip 主数据库端口”。 复制的基本操作命令 1:info replication :可以查看复制节点的相关信息 2:slaveof:可在运行期间修改slave节点的信息,如果该数据库已经是某个主数据库的从数据库,那么会停止和原主数据库的同步关系,转而和新的主数据库同步 3:slaveof no one:使当前数据库停止与其他数据库的同步,转成主数据库 4:从节点 天然的只读不能写 集群操作基本命令 1:CLUSTER INFO:获取集群的信息 2:CLUSTER NODES:获取集群当前已知的所有节点,以及这些节点的相关信息 3:CLUSTER MEET <ip> <port>:将ip和port所指定的节点添加到集群当中 4:CLUSTER FORGET <node_id>:从集群中移除node_id 指定的节点 5:CLUSTER REPLICATE <node_id>:将当前节点设置为node_id 指定的节点的从节点 6:CLUSTER SAVECONFIG:将节点的配置文件保存到硬盘里面 7:CLUSTER ADDSLOTS <slot> [slot ...]:将一个或多个槽分配给当前节点 8:CLUSTER DELSLOTS <slot> [slot ...]:从当前节点移除一个或多个槽 9:CLUSTER FLUSHSLOTS:移除分配给当前节点的所有槽 10:CLUSTER SETSLOT <slot> NODE <node_id>:将槽分配给node_id 指定的节点,如果槽已经分配给另一 个节点,那么先让另一个节点删除该槽>,然后再进行分配 11:CLUSTER SETSLOT <slot> MIGRATING <node_id>:将本节点的槽迁移到指定的节点中 12:CLUSTER SETSLOT <slot> IMPORTING <node_id>:从指定节点导入槽到本节点 13:CLUSTER SETSLOT <slot> STABLE:取消对槽的导入(import)或迁移(migrate) 14:CLUSTER KEYSLOT <key>:计算键key 应该被放置在哪个槽 15:CLUSTER COUNTKEYSINSLOT <slot>:返回槽目前包含的键值对数量 16:CLUSTER GETKEYSINSLOT <slot> <count>:返回count 个槽中的键 17:migrate 目的节点ip 目的节点port 键名数据库号码超时时间[copy] [replace]:迁移某个键值对 移动已分配的插槽 1:在B上执行cluster setslot 123 importing A 2:在A上执行cluster setslot 123 migrating B 3:在A上执行cluster getkeysinslot 123 要返回的数量 4:对上一步获取的每个键执行migrate命令,将其从A迁移到B 5:在集群中每个服务器上执行cluster setslot 123 node B 还有 使用redis-trib.rb来操作集群。这个先对就简单多了,但是原理还是用手动那一套只不过是做成了命令组的形式,我这里就不多说了,相信网上也有很多大家稍微研究一下就能搞定。 最都稍微总结一下,redis集群虽然做得还可以,但是真正用到大型的项目中 还是有很多考验的特别是对分片技术的管理,其实做的还不是很好包括服务端的自动切换和客户端的透明连接,真正的高可用时无声无息的进行,在你不知不觉中把问题处理了那才牛,最近比较火的 kubernetes(k8s) 对吧,不多说了。