本文为本系列中重要指数的5颗星章节。
如果从机还没有与主机建立主从关系时,它还是master,这时如果它已经写入了一些键值对,如果之后建立主从关系变成从机后,这些老的键值对还在吗?如果还在,如果和主机同步过来的键值对有冲突怎么办?
Redis的复制(Master/Slave)
1. 是什么(who)
官网原文介绍:
ReplicationAt the base of Redis replication there is a very simple to use and configure master-slave replication that allows slave Redis servers to be exact copies of master servers. The slave will automatically reconnect to the master every time the link breaks, and will attempt to be an exact copy of it regardless of what happens to the master. This system works using three main mechanisms:
Redis uses by default asynchronous replication, which being high latency and high performance, is the natural replication mode for the vast majority of Redis use cases. However Redis slaves asynchronously acknowledge the amount of data the received periodically with the master. Synchronous replication of certain data can be requested by the clients using the command. However is only able to ensure that there are the specified number of acknowledged copies in the other Redis instances: acknowledged writes can still be lost during a failover for different reasons during a failover or depending on the exact configuration of the Redis persistence. You could check the Sentinel or Redis Cluster documentation for more information about high availability and failover. The rest of this document mainly describe the basic characteristics of Redis basic replication. The following are some very important facts about Redis replication:
|
行话:也就是我们所说的主从复制,主机数据更新后根据配置和策略,自动同步到备机的master/slaver机制,Master以写为主,Slave以读为主。
2. 能干嘛(do What)
读写分离
容灾恢复
3. 怎么玩(How)
原则: 配从(库)不配主(库)
打个比方,应该是小弟拜码头认大哥,而不是告诉大哥你小弟有哪些。
从库配置:slaveof 主库IP 主库端口
每次与master断开之后,都需要重新连接,除非你配置进redis.conf文件
查看某个节点的主从信息:使用命令 info replication
修改配置文件细节操作
拷贝多个redis.conf文件,用来配置和启动三个redis-server,进而配置主从关系。这里我因为是在自己的PC上虚拟机环境,没那么大内存真搞三个虚拟os镜像(谁让内存条涨价了呢 T T),而是在同一个机器上启动了三个redis服务。三个服务自然对应三个端口,相应的log、pid、rdb等文件自然也要配制成不一样的,所以建三个配置文件分别配置是必须的。下面我将之前的配置文件复制三份,分别以6379、6380、6381位端口,相应的其他配置项也会做对应的修改,如下:
[hadoop@localhost myconfig]$ mkdir config6379[hadoop@localhost myconfig]$ mkdir config6380[hadoop@localhost myconfig]$ mkdir config6381[hadoop@localhost myconfig]$[hadoop@localhost myconfig]$ cp redis.conf config6379/redis6379.conf[hadoop@localhost myconfig]$ cp redis.conf config6380/redis6380.conf[hadoop@localhost myconfig]$ cp redis.conf config6381/redis6381.conf[hadoop@localhost myconfig]$[hadoop@localhost myconfig]$ vim config6380/redis6380.conf[hadoop@localhost myconfig]$ vim config6381/redis6381.conf
改端口、改pid、改log、改rdb:(当然,原先介绍过得一定要配置的配置项也要自己设置,如daemonize设置成yes,这里不再赘述)
# Accept connections on the specified port, default is 6379 (IANA #815344).# If port 0 is specified Redis will not listen on a TCP socket.port 6380
# If a pid file is specified, Redis writes it where specified at startup# and removes it at exit.## When the server runs non daemonized, no pid file is created if none is# specified in the configuration. When the server is daemonized, the pid file# is used even if not specified, defaulting to "/var/run/redis.pid".## Creating a pid file is best effort: if Redis is not able to create it# nothing bad happens, the server will start and run normally.pidfile /var/run/redis_6380.pid
# Specify the log file name. Also the empty string can be used to force# Redis to log on the standard output. Note that if you use standard# output for logging but daemonize, logs will be sent to /dev/nulllogfile "log6380.log"
# The filename where to dump the DBdbfilename dump6380.rdb
其他两个配置文件对应都配置成6379和6381。
port 6379pidfile /var/run/redis_6379.pidlogfile "log6379.log"dbfilename dump6379.rdb
port 6381pidfile /var/run/redis_6381.pidlogfile "log6381.log"dbfilename dump6381.rdb
好吧,还是罗列一下吧,以后配置多个redis主从节点之前,可以做个参照,对比配置一下这么几个地方:
(1)开启daemonize yes
(2)pid文件名字
(3)指定端口
(4)log文件名字
(5)dump.rdb名字
配置主从复制的常用3招
(1)一主二仆
一个Master,两个Slave
配置命令,从机上配置(还记得刚才说的“配从不配主”吗?对,小弟拜大哥)
日志查看: info replication命令,在主机和备机上分别查看各自的主机日志或备机日志日志。
主从问题演示
先按照刚才的三个配置,启动3个redis服务:(我这里远程开了三个窗口登录虚拟机,模拟自己再操作三台机器)
先启动6379那个
[hadoop@localhost myconfig]$ cd config6379/[hadoop@localhost config6379]$ sudo /usr/local/bin/redis-server redis6379.conf[sudo] password for hadoop:[hadoop@localhost config6379]$ ps -ef | grep redisroot 90841 1 0 22:15 ? 00:00:00 /usr/local/bin/redis-server 127.0.0.1:6379hadoop 90845 89612 0 22:15 pts/2 00:00:00 grep --color=auto redis[hadoop@localhost config6379]$
再启动6380那个
[hadoop@localhost myconfig]$ cd config6380/[hadoop@localhost config6380]$ sudo /usr/local/bin/redis-server redis6380.conf[sudo] password for hadoop:[hadoop@localhost config6380]$ ps -ef | grep redisroot 90841 1 0 22:15 ? 00:00:00 /usr/local/bin/redis-server 127.0.0.1:6379root 90874 1 0 22:16 ? 00:00:00 /usr/local/bin/redis-server 127.0.0.1:6380hadoop 90878 89824 0 22:16 pts/3 00:00:00 grep --color=auto redis[hadoop@localhost config6380]$
之后是6381那个
[hadoop@localhost myconfig]$ cd config6381/[hadoop@localhost config6381]$[hadoop@localhost config6381]$ sudo /usr/local/bin/redis-server redis6381.conf[sudo] password for hadoop:[hadoop@localhost config6381]$ ps -ef | grep redisroot 90841 1 0 22:15 ? 00:00:00 /usr/local/bin/redis-server 127.0.0.1:6379root 90874 1 0 22:16 ? 00:00:00 /usr/local/bin/redis-server 127.0.0.1:6380root 91026 1 0 22:21 ? 00:00:00 /usr/local/bin/redis-server 127.0.0.1:6381hadoop 91030 90026 0 22:21 pts/4 00:00:00 grep --color=auto redis[hadoop@localhost config6381]$
之后我们远程客户端登录三个机器:
可以看到三台机器现在都没有数据,且replication信息里显示 各个机器的角色都是主节点master
[hadoop@localhost config6379]$ /usr/local/bin/redis-cli -p 6379127.0.0.1:6379> keys *(empty list or set)127.0.0.1:6379>127.0.0.1:6379> info replication# Replicationrole:masterconnected_slaves:0master_repl_offset:0repl_backlog_active:0repl_backlog_size:1048576repl_backlog_first_byte_offset:0repl_backlog_histlen:0127.0.0.1:6379>
[hadoop@localhost config6380]$ /usr/local/bin/redis-cli -p 6380127.0.0.1:6380> keys *(empty list or set)127.0.0.1:6380>127.0.0.1:6380> info replication# Replicationrole:masterconnected_slaves:0master_repl_offset:0repl_backlog_active:0repl_backlog_size:1048576repl_backlog_first_byte_offset:0repl_backlog_histlen:0127.0.0.1:6380>
[hadoop@localhost config6381]$ /usr/local/bin/redis-cli -p 6381127.0.0.1:6381> keys *(empty list or set)127.0.0.1:6381>127.0.0.1:6381> info replication# Replicationrole:masterconnected_slaves:0master_repl_offset:0repl_backlog_active:0repl_backlog_size:1048576repl_backlog_first_byte_offset:0repl_backlog_histlen:0127.0.0.1:6381>
相应的,对应的rdb文件、log文件等已经各自生成了:
首先,我在6379节点上,设置一些键值对:
127.0.0.1:6379> set k1 v1OK127.0.0.1:6379> set k2 v2OK127.0.0.1:6379> set k3 v3OK127.0.0.1:6379> get k1"v1"
然后在6380或6381上查看对应的键,如k1是否存在:
127.0.0.1:6380> get k1(nil)
127.0.0.1:6381> get k1(nil)
好,我们现在正是开始配置redis主从复制:
记得刚才说的,配从不配主,小弟拜大哥,配置主从关系需要在从节点上配置其主节点是谁(包括主节点的IP和服务端口)。
127.0.0.1:6380> SLAVEOF 127.0.0.1 6379OK127.0.0.1:6380>
127.0.0.1:6381> SLAVEOF 127.0.0.1 6379OK127.0.0.1:6381>
然后我们马上在6380和6381两个刚刚配置的从节点上查询k1:
发现在设置主从关系之前原先在主节点上的原有键值对,已经被同步到了所有从节点上。即在slaveof命令在从节点上执行生效后,从节点就立即将主节点上的所有数据都同步过来供其他应用读取。
127.0.0.1:6380> get k1"v1"
127.0.0.1:6381> get k1"v1"
这里,提出四个问题,举一反三:
(1)在建立主从关系之后,我们在主节点上做的修改或是新增加的数据,能否被从节点及时“感知”到呢?
我们此时在6379主节点上输入:
127.0.0.1:6379> set k4 v4OK
然后立即访问两个从节点:我们发现,从节点能够及时同步在slaveof主从配置之后主节点做的任何数据修改。
127.0.0.1:6380> get k4"v4"
127.0.0.1:6381> get k4"v4"
(2)如果我们在从节点上修改某个同步过来的数据会如何?
答案是报错,从节点是只读节点,只读!
127.0.0.1:6380> set k2 v22(error) READONLY You can't write against a read only slave.127.0.0.1:6380>
(3)如果我们在从节点上新建一个其他主从节点都没有的数据,会怎么样?
答案也是报错,从节点是只读节点,只读!
127.0.0.1:6381> set k5 v5(error) READONLY You can't write against a read only slave.
(4)如果我们在起初未建立主从复制关系时,从节点上已经存在一些数据,这些已经存在键值对如果与后面建立的主节点上的数据键值对存在一样的键会冲突或者覆盖吗?如果起初从节点上存在一些原始数据,但这些数据的键未出现在主节点的键值对数据中,那么建立主从关系后,从几点上的数据是否还能存在?
要回答(4)这个问题,我们先放一下,我们留到文末再实验。
我们先看看此时的,主节点6379和两个从节点6380、6381的replication的信息吧。
主节点6379,此时角色还是master,但是现在多了两项slave0和slave1,分别标注了两台从节点6380和6381的信息。
127.0.0.1:6379> INFO replication# Replicationrole:masterconnected_slaves:2slave0:ip=127.0.0.1,port=6380,state=online,offset=3651,lag=0slave1:ip=127.0.0.1,port=6381,state=online,offset=3651,lag=0master_repl_offset:3651repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:2repl_backlog_histlen:3650127.0.0.1:6379>
从节点上,无论是80还是81,角色已经都变成了slave,同时多了master_host、master_port等很多项,用于显示其隶属于那一台主节点及主节点的信息。
127.0.0.1:6380> INFO replication# Replicationrole:slavemaster_host:127.0.0.1master_port:6379master_link_status:upmaster_last_io_seconds_ago:7master_sync_in_progress:0slave_repl_offset:3693slave_priority:100slave_read_only:1connected_slaves:0master_repl_offset:0repl_backlog_active:0repl_backlog_size:1048576repl_backlog_first_byte_offset:0repl_backlog_histlen:0127.0.0.1:6380>
127.0.0.1:6381> INFO replication# Replicationrole:slavemaster_host:127.0.0.1master_port:6379master_link_status:upmaster_last_io_seconds_ago:8master_sync_in_progress:0slave_repl_offset:4337slave_priority:100slave_read_only:1connected_slaves:0master_repl_offset:0repl_backlog_active:0repl_backlog_size:1048576repl_backlog_first_byte_offset:0repl_backlog_histlen:0127.0.0.1:6381>
这里我们提出第5个问题:
(5)如果此时,主节点挂了,那么从节点会如何?是始终做个忠诚,誓死等待主君;还是会有臣子跳出来称王?
好,我们把主节点6379那台关了:
127.0.0.1:6379> SHUTDOWNnot connected>
然后看6380和6381两台从节点的replication信息:
127.0.0.1:6380> INFO replication# Replicationrole:slavemaster_host:127.0.0.1master_port:6379master_link_status:downmaster_last_io_seconds_ago:-1master_sync_in_progress:0slave_repl_offset:5723master_link_down_since_seconds:8slave_priority:100slave_read_only:1connected_slaves:0master_repl_offset:0repl_backlog_active:0repl_backlog_size:1048576repl_backlog_first_byte_offset:0repl_backlog_histlen:0127.0.0.1:6380>127.0.0.1:6380> get k4"v4"127.0.0.1:6380>
(本文出自ochina博主happybks的原创文章,请勿盗用:https://my.oschina.net/happyBKs/blog/1587238)
127.0.0.1:6381> INFO replication# Replicationrole:slavemaster_host:127.0.0.1master_port:6379master_link_status:downmaster_last_io_seconds_ago:-1master_sync_in_progress:0slave_repl_offset:5723master_link_down_since_seconds:14slave_priority:100slave_read_only:1connected_slaves:0master_repl_offset:0repl_backlog_active:0repl_backlog_size:1048576repl_backlog_first_byte_offset:0repl_backlog_histlen:0127.0.0.1:6381>127.0.0.1:6381> set k5 v5(error) READONLY You can't write against a read only slave.127.0.0.1:6381>
可以看到,两台机器依然是slave,他们在master挂了之后,依然保持slave的角色不变。并且,在master没恢复期间,从节点依然是只读的:可以读已有的键值对,但也仅限于读,不能修改添加数据。
如果你印象不够深刻,请看下面的几张图,应该就忘不掉了:
6379主节点 (挂了!) | master, game over |
6380从节点:“我是忠臣” (陛下你走吧,该我干的活 我继续好好干) | slave,forever |
6381从节点:“我是忠臣” (陛下你走吧,不该干的事情 我还是不干) | slave,forever |
再提一个问题:
(6)master节点此时恢复了,从节点是否还与之有主从关系?
(如果之后原来已挂掉的主君复活了,是否还是其忠臣?是否需要重新认主?)
我们重新启动6379那台redis服务
[hadoop@localhost config6379]$ sudo /usr/local/bin/redis-server redis6379.conf
我客户端登录6379,同时新增加一个键值对:
[hadoop@localhost config6379]$ /usr/local/bin/redis-cli -p 6379127.0.0.1:6379>127.0.0.1:6379>127.0.0.1:6379> set k5 v5OK127.0.0.1:6379>
然后我们看看:
127.0.0.1:6380> get k5"v5"127.0.0.1:6380>
127.0.0.1:6381> get k5"v5"127.0.0.1:6381>
结论:master节点此时恢复了,从节点与它之前配置的主从关系依然有效,仍然能够同步master的后续数据更新。
来,再来一波加深印象:
6379主节点 (重获新生!) | master,again |
6380从节点:“先帝活了,我还是忠臣” | slave,forever |
6381从节点:“先帝活了,我还是忠臣” | slave,forever |
如果挂的不是大哥,而是小弟,那么小弟复活后还认大哥吗?
相应的,我们再提出一个问题:
(7)如果是slave节点挂了,那么slave节点恢复服务后,中从关系是否存在?
我们在刚才的例子配置的环境基础上继续实验:
首先我们将其中一个从节点6380那台的服务关闭:
127.0.0.1:6380> SHUTDOWNnot connected> exit
之后我们看看主节点6379那台的replication信息:
看到了吗?8380那台slave已经从6379master的主从复制信息中消失了。(果然臣子都是守着先帝尸体的忠臣,皇帝都是君让臣死臣死了白死的皇帝)
127.0.0.1:6379> INFO replication# Replicationrole:masterconnected_slaves:1slave0:ip=127.0.0.1,port=6381,state=online,offset=5107,lag=0master_repl_offset:5107repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:2repl_backlog_histlen:5106127.0.0.1:6379>
那么我们让6380那台重新恢复服务:
我们再看6380这台原先的从节点 现在服务恢复之后的replication信息,惊奇的发现,6380的角色编程了master!!!
[hadoop@localhost config6380]$ sudo /usr/local/bin/redis-server redis6380.conf[sudo] password for hadoop:[hadoop@localhost config6380]$ /usr/local/bin/redis-cli -p 6380127.0.0.1:6380>127.0.0.1:6380> INFO replication# Replicationrole:masterconnected_slaves:0master_repl_offset:0repl_backlog_active:0repl_backlog_size:1048576repl_backlog_first_byte_offset:0repl_backlog_histlen:0127.0.0.1:6380>
好,既然,你臣子不认主子了,master节点6379当然也不认你这个旧臣。6379主节点的replication信息中也当然不再有6380作为slave信息。
127.0.0.1:6379> INFO replication# Replicationrole:masterconnected_slaves:1slave0:ip=127.0.0.1,port=6381,state=online,offset=5345,lag=0master_repl_offset:5345repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:2repl_backlog_histlen:5344127.0.0.1:6379>
所以,我们这里总结一个很重要的结论:
redis通过slaveOf配置了主从复制的各个节点,如果是master节点挂了,那么只要master恢复,一切如旧;如果是某个slave节点挂了,必须在该slave节点恢复之后重新通过slaveOf命令在从节点上配置其与原master节点的主从关系。
刚才我们说过的:每次与master断开之后,都需要重新连接,除非你配置进redis.conf文件
通俗的说,大哥出事,小弟傻等;小弟嗝屁,大哥无情,得重新拜码头、跑龙套。
补充说一下,虽然现在6380那台节点恢复服务之后,不再是6379那台的从节点,但是原先同步过来的数据依然还在,可以读取。
127.0.0.1:6380> get k1"v1"127.0.0.1:6380> get k5"v5"
并且,现在6380已经自己是master节点了,因此不再是只读的,所以可以修改这些键值对了,对6379和6381没有任何影响。
127.0.0.1:6380> set k1 vv1OK127.0.0.1:6380> get k1"vv1"
127.0.0.1:6379> get k1"v1"127.0.0.1:6379>
但是,此时,6379上新做的添加、删除和修改数据都不会再被复制到6380上了。
127.0.0.1:6379> set k6 v6OK
127.0.0.1:6380> get k6(nil)
好,我们现在回到刚才提出的问题(4),现在我们在目前的环境上来实验一下,如果我们在已经对6380上的数据做了修改,即6380上存在与6379上键相同但值不同的键值对(刚才我们对6380的k1做了修改);并且我们再在6380上新建一个k7键值对,6379那台上没有k7。如果此时,我为6380建立它与6379的slaveof的关系,6380会发生什么呢?
我们先新建一个k7键值对在6380上,同时我们验证了此时6379上确实没有k7。
127.0.0.1:6380> set k7 vv7OK127.0.0.1:6380>
127.0.0.1:6379> get k7(nil)
然后建立主从关系:
127.0.0.1:6380> INFO replication# Replicationrole:slavemaster_host:127.0.0.1master_port:6379master_link_status:upmaster_last_io_seconds_ago:8master_sync_in_progress:0slave_repl_offset:9448slave_priority:100slave_read_only:1connected_slaves:0master_repl_offset:0repl_backlog_active:0repl_backlog_size:1048576repl_backlog_first_byte_offset:0repl_backlog_histlen:0127.0.0.1:6380>
127.0.0.1:6379> INFO replication# Replicationrole:masterconnected_slaves:2slave0:ip=127.0.0.1,port=6381,state=online,offset=9462,lag=0slave1:ip=127.0.0.1,port=6380,state=online,offset=9462,lag=0master_repl_offset:9462repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:2repl_backlog_histlen:9461127.0.0.1:6379>
然后我们惊奇地发现:6380在变成6379的slave节点之后,竟然将自己的数据全部清除,并全部复制了master节点6379上所有的数据,不管是否存在键冲突。
127.0.0.1:6380> get k1"v1"127.0.0.1:6380> get k7(nil)127.0.0.1:6380>
真是小弟忠心忠得死心塌地,君让臣抛弃妻子,臣绝不说个“不”字。
(2) 薪火相传
薪火相传,即redis的级联复制。
要解决的问题:一个master直接连接过多的slave,中心化太严重。
怎么解决(何谓“薪火相传”):上一个Slave可以是下一个slave的Master,Slave同样可以接收其他slaves的连接和同步请求,那么该slave作为了链条中下一个的master, 可以有效减轻master的写压力。
中途变更转向:会清除之前的数据,重新建立拷贝最新的。
这个很好理解,认了其他节点为自己的master,一定要抛弃自己的所有数据,拷贝master上的数据,这个刚才也提到了。
配多级slave的方法:slaveof 新主库IP 新主库端口
好,我们还是来实际操作一下:
我们在刚才的例子上修改6381,将6381slaveof 到6380。
127.0.0.1:6381> SLAVEOF 127.0.0.1 6380OK127.0.0.1:6381>
然后我们发现6379上原先两个slave信息项,现在只剩下6380。
127.0.0.1:6379> INFO replication# Replicationrole:masterconnected_slaves:1slave0:ip=127.0.0.1,port=6380,state=online,offset=32254,lag=0master_repl_offset:32254repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:2repl_backlog_histlen:32253127.0.0.1:6379>
而6380的变化请注意:它作为6379的slave没变,因此6380的角色仍然是slave;但是6380作为6381的master,现在多了一个信息项connected_slaves:1,即6380连接着其他slave,个数为1,后面还附着这个下一级slave的信息——6381那台的IP和端口等。
127.0.0.1:6380> INFO replication# Replicationrole:slavemaster_host:127.0.0.1master_port:6379master_link_status:upmaster_last_io_seconds_ago:3master_sync_in_progress:0slave_repl_offset:32268slave_priority:100slave_read_only:1connected_slaves:1slave0:ip=127.0.0.1,port=6381,state=online,offset=15,lag=0master_repl_offset:15repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:2repl_backlog_histlen:14127.0.0.1:6380>
此时的6381当然角色还是slave,但是所属的master已经变成了6380。
127.0.0.1:6381> INFO replication# Replicationrole:slavemaster_host:127.0.0.1master_port:6380master_link_status:upmaster_last_io_seconds_ago:2master_sync_in_progress:0slave_repl_offset:29slave_priority:100slave_read_only:1connected_slaves:0master_repl_offset:0repl_backlog_active:0repl_backlog_size:1048576repl_backlog_first_byte_offset:0repl_backlog_histlen:0127.0.0.1:6381>
(本文出自ochina博主happybks的原创文章,请勿盗用:https://my.oschina.net/happyBKs/blog/1587238)
注意这里6380的角色还是slave,但是多了一个 slave connection项,里面赫然有6381。
好,redis级联主从复制我们已经配置好了,现在来试试:
127.0.0.1:6379> set k8 v8OK127.0.0.1:6379>
127.0.0.1:6380> get k8"v8"127.0.0.1:6380>
127.0.0.1:6381> get k8"v8"127.0.0.1:6381>
这里需要注意的另一个问题,刚才提到过的,就是中途转向:如果我们此时将某个中间的slave配置到了其他节点下,那么它和它以下的所有从节点的数据都会清除,重新复制。例子我就粘贴了吧。
(3)反客为主
刚才我们提到一主二从的主从复制,如果master挂了,让从一直傻等有时候是不现实的,领导要杀人的,他命令立即回复整个redis集群,可这时候master节点一时半会儿修不好或硬件坏了,这时候我们就需要从从机里选一个来当新的master,并把其他所有节点从新配置为这个新master节点的slave。
那么要怎么揭竿称帝呢?额,不对,要怎么从slave变回master呢?一句话我不是任何人的臣子:请用命令 SLAVEOF no one: 使当前数据库停止与其他数据库的同步,转成主数据库。
我们还是通过例子来看,首先,我们先将环境恢复到一主二从刚才的状态:
127.0.0.1:6381> SLAVEOF 127.0.0.1 6379OK127.0.0.1:6381>127.0.0.1:6381>127.0.0.1:6381> INFO replication# Replicationrole:slavemaster_host:127.0.0.1master_port:6379master_link_status:upmaster_last_io_seconds_ago:6master_sync_in_progress:0slave_repl_offset:40860slave_priority:100slave_read_only:1connected_slaves:0master_repl_offset:0repl_backlog_active:0repl_backlog_size:1048576repl_backlog_first_byte_offset:0repl_backlog_histlen:0127.0.0.1:6381>
127.0.0.1:6379> INFO replication# Replicationrole:masterconnected_slaves:2slave0:ip=127.0.0.1,port=6380,state=online,offset=40860,lag=0slave1:ip=127.0.0.1,port=6381,state=online,offset=40860,lag=0master_repl_offset:40860repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:2repl_backlog_histlen:40859127.0.0.1:6379>
127.0.0.1:6380> INFO replication# Replicationrole:slavemaster_host:127.0.0.1master_port:6379master_link_status:upmaster_last_io_seconds_ago:3master_sync_in_progress:0slave_repl_offset:40846slave_priority:100slave_read_only:1connected_slaves:0master_repl_offset:8593repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:2repl_backlog_histlen:8592127.0.0.1:6380>
之后,我们假装6379挂了,把它关闭:其他两个节点默认还是slave。
127.0.0.1:6379> SHUTDOWNnot connected>
127.0.0.1:6380> INFO replication# Replicationrole:slavemaster_host:127.0.0.1master_port:6379master_link_status:downmaster_last_io_seconds_ago:-1master_sync_in_progress:0slave_repl_offset:41406master_link_down_since_seconds:23slave_priority:100slave_read_only:1connected_slaves:0master_repl_offset:9181repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:2repl_backlog_histlen:9180127.0.0.1:6380>
127.0.0.1:6381> INFO replication# Replicationrole:slavemaster_host:127.0.0.1master_port:6379master_link_status:downmaster_last_io_seconds_ago:-1master_sync_in_progress:0slave_repl_offset:41406master_link_down_since_seconds:28slave_priority:100slave_read_only:1connected_slaves:0master_repl_offset:0repl_backlog_active:0repl_backlog_size:1048576repl_backlog_first_byte_offset:0repl_backlog_histlen:0127.0.0.1:6381>
这时候,我让6380跳反称帝:)
127.0.0.1:6380> SLAVEOF no oneOK127.0.0.1:6380> INFO replication# Replicationrole:masterconnected_slaves:0master_repl_offset:9335repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:2repl_backlog_histlen:9334127.0.0.1:6380>
相应的再把6381那台配置为6380的从机:
127.0.0.1:6381> SLAVEOF 127.0.0.1 6380OK127.0.0.1:6381> INFO replication# Replicationrole:slavemaster_host:127.0.0.1master_port:6380master_link_status:upmaster_last_io_seconds_ago:4master_sync_in_progress:0slave_repl_offset:9419slave_priority:100slave_read_only:1connected_slaves:0master_repl_offset:0repl_backlog_active:0repl_backlog_size:1048576repl_backlog_first_byte_offset:0repl_backlog_histlen:0127.0.0.1:6381>
127.0.0.1:6380> INFO replication# Replicationrole:masterconnected_slaves:1slave0:ip=127.0.0.1,port=6381,state=online,offset=9643,lag=1master_repl_offset:9643repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:2repl_backlog_histlen:9642127.0.0.1:6380>
当然,如果之后原master节点6379服务恢复,原主从复制的关系肯定不在了。原来的小弟们拜了新大哥:“你谁啊”。
但是请注意,这里使我们重新手动指定新master并建立新的主从关系。等会儿我们介绍哨兵模式,它是反客为主的自动化版。
以下为开脑洞时间:)
皇帝哥哥:大家都得听我的 | master 6379 |
臣弟:我是忠臣 | slave 6380 |
其他臣子:我们都是忠臣 我们的master是6379 | slave 6381等等 |
然后瓦剌军来了,皇帝英勇被俘,皇帝不能为人民服务了:6379挂了
皇帝:快放了朕,朕还要为臣民服务。 (瓦剌军首领:你弟称帝了) |
然后,臣弟不做忠臣了上位:
新皇帝(臣弟): 我没有主子 我是为了剩下的臣民服务 | 6380slaveof no one -> master |
其他臣子:我们都是忠臣 我们的master是6380 | slave 6381等等 |
之后,朱祁镇回来了,结果:
太上兄(原皇帝):我是master啊 你们怎么都不认我呢??? | master 6379 |
新皇帝(原臣弟): 朕是皇帝,朕是堂堂正正的皇帝 臣子们现在都听我的 | master 6380 |
其他臣子:我们都是忠臣 我们的master是6380 6379?不认得 | slave 6381等等 |
复制原理
slave启动成功连接到master后会发送一个sync命令
Master接到命令启动后台的存盘进程,同时收集所有接收到的用于修改数据集命令,在后台进程执行完毕之后,master将传送整个数据文件到slave,以完成一次完全同步
全量复制:而slave服务在接收到数据库文件数据后,将其存盘并加载到内存中。
增量复制:Master继续将新的所有收集到的修改命令依次传给slave,完成同步
但是只要是重新连接master,一次完全同步(全量复制)将被自动执行
总结一下,简单地说,就是“首次全量,以后增量”
哨兵模式(sentinel)
(1)是什么
简单地说,sentinel模式就是反客为主的自动版,能够后台监控主机是否故障,如果故障了根据投票数自动将从库转换为主库。
(2)怎么玩(使用步骤)
(a)调整结构,6379带着80、81
恢复到一主二从原始状态:
127.0.0.1:6380> SLAVEOF 127.0.0.1 6379OK127.0.0.1:6380>127.0.0.1:6380> INFO replication# Replicationrole:slavemaster_host:127.0.0.1master_port:6379master_link_status:upmaster_last_io_seconds_ago:3master_sync_in_progress:0slave_repl_offset:57slave_priority:100slave_read_only:1connected_slaves:0master_repl_offset:43repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:2repl_backlog_histlen:42127.0.0.1:6380>
127.0.0.1:6381> SLAVEOF 127.0.0.1 6379OK127.0.0.1:6381>127.0.0.1:6381> INFO replication# Replicationrole:slavemaster_host:127.0.0.1master_port:6379master_link_status:upmaster_last_io_seconds_ago:7master_sync_in_progress:0slave_repl_offset:57slave_priority:100slave_read_only:1connected_slaves:0master_repl_offset:0repl_backlog_active:0repl_backlog_size:1048576repl_backlog_first_byte_offset:0repl_backlog_histlen:0127.0.0.1:6381>
127.0.0.1:6379> info replication# Replicationrole:masterconnected_slaves:2slave0:ip=127.0.0.1,port=6380,state=online,offset=29,lag=0slave1:ip=127.0.0.1,port=6381,state=online,offset=29,lag=0master_repl_offset:29repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:2repl_backlog_histlen:28127.0.0.1:6379>
(b)配置哨兵,填写内容
因为内存有限,我还是在同一个虚拟机上新建一个目录假装我是在一台新机器上配置启动哨兵:
这里需要注意,真实环境下sentinel一定不是和master配置在一台主机上,否则可能会一起挂掉。
[hadoop@localhost myconfig]$ mkdir sentinelconfig[hadoop@localhost myconfig]$ cd sentinelconfig/[hadoop@localhost sentinelconfig]$[hadoop@localhost sentinelconfig]$[hadoop@localhost sentinelconfig]$ touch sentinel.conf[hadoop@localhost sentinelconfig]$ vim sentinel.conf[hadoop@localhost sentinelconfig]$
需要建立一个sentinel.conf的哨兵配置文件,注意名称一定不能写错。
然后在sentinel.conf中配置这样一行:
sentinel monitor 被监控数据库名字(自己起名字) 127.0.0.1 6379 1
sentinel monitor host6379 127.0.0.1 6379 1
前面sentinel monitor代表哨兵模式来监控;host6379是给master主机取个名字,这个可以随便写;然后是需要监控的mater主机的IP地址和服务端口,用于指定哨兵模式监控哪里;最重要的是上面最后一个数字1,表示主机挂掉后salve投票看让谁接替成为主机,得票数多少后成为主机。
(c)启动哨兵
redis-sentinel sentinel.conf ()目录依照各自的实际情况配置,可能目录不同)
演示如下:在sentinel配置的节点上启动哨兵监控:
可以看到这个sentinel不是daemon的,所以可以看到控制台打印的日志。从日志可以看到sentinel启动成功,监控着sentinel.conf文件里配置的6379主机master,同时也识别到6379这台master的slave节点有6380和6381。
[hadoop@localhost sentinelconfig]$ sudo /usr/local/bin/redis-sentinel sentinel.conf[sudo] password for hadoop:115446:X 09 Dec 14:16:53.482 * Increased maximum number of open files to 10032 (it was originally set to 1024). _._ _.-``__ ''-._ _.-`` `. `_. ''-._ Redis 3.2.11 (00000000/0) 64 bit .-`` .-```. ```\/ _.,_ ''-._ ( ' , .-` | `, ) Running in sentinel mode |`-._`-...-` __...-.``-._|'` _.-'| Port: 26379 | `-._ `._ / _.-' | PID: 115446 `-._ `-._ `-./ _.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | http://redis.io `-._ `-._`-.__.-'_.-' _.-' |`-._`-._ `-.__.-' _.-'_.-'| | `-._`-._ _.-'_.-' | `-._ `-._`-.__.-'_.-' _.-' `-._ `-.__.-' _.-' `-._ _.-' `-.__.-'115446:X 09 Dec 14:16:53.485 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.115446:X 09 Dec 14:16:53.525 # Sentinel ID is 328a80cfb54d5a691d2626a50f18f63a4f212832115446:X 09 Dec 14:16:53.525 # +monitor master host6379 127.0.0.1 6379 quorum 1115446:X 09 Dec 14:16:53.561 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ host6379 127.0.0.1 6379115446:X 09 Dec 14:16:53.563 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ host6379 127.0.0.1 6379
(d)原有的master挂了,投票新选
我先把6379这台master服务搞挂:
127.0.0.1:6379> SHUTDOWNnot connected>
这个时候,我立即重新主从继续开工,info replication查查看6380和6381
结果发现,6380和6381仍然是slave,并没有选举产生新的master。看看sentinel的控制台界面,也没有输出任何新东西。
别急,sentinel监控到master挂机,选举新的master需要一定的时间。并且,sentinel选举过程中还可能一次选举不出结果——还记得我们在sentinel.conf配置文件里配置的那一行最后设置的1吗?一定要选举得票数超过这个1这个配置了的数字,而且是得票最多的唯一一个slave节点才能被选择为新master。
127.0.0.1:6380> INFO replication# Replicationrole:slavemaster_host:127.0.0.1master_port:6379master_link_status:downmaster_last_io_seconds_ago:-1master_sync_in_progress:0slave_repl_offset:31895master_link_down_since_seconds:2slave_priority:100slave_read_only:1connected_slaves:0master_repl_offset:62884repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:2repl_backlog_histlen:62883127.0.0.1:6380>
127.0.0.1:6381> INFO replication# Replicationrole:slavemaster_host:127.0.0.1master_port:6379master_link_status:downmaster_last_io_seconds_ago:-1master_sync_in_progress:0slave_repl_offset:31895master_link_down_since_seconds:4slave_priority:100slave_read_only:1connected_slaves:0master_repl_offset:0repl_backlog_active:0repl_backlog_size:1048576repl_backlog_first_byte_offset:0repl_backlog_histlen:0127.0.0.1:6381>
我们等待一段时间,果然sentinel的控制台界面开始打印新信息,里面展示了整个选举的几轮过程:
最后几行注意了,将6380认了6381为master;连6379也被代为认了6381为master,只不过6379目前没有恢复服务,所以sdown。
115446:X 09 Dec 14:16:53.485 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.115446:X 09 Dec 14:16:53.525 # Sentinel ID is 328a80cfb54d5a691d2626a50f18f63a4f212832115446:X 09 Dec 14:16:53.525 # +monitor master host6379 127.0.0.1 6379 quorum 1115446:X 09 Dec 14:16:53.561 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ host6379 127.0.0.1 6379115446:X 09 Dec 14:16:53.563 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ host6379 127.0.0.1 6379115446:X 09 Dec 14:25:21.655 # +sdown master host6379 127.0.0.1 6379115446:X 09 Dec 14:25:21.655 # +odown master host6379 127.0.0.1 6379 #quorum 1/1115446:X 09 Dec 14:25:21.655 # +new-epoch 1115446:X 09 Dec 14:25:21.655 # +try-failover master host6379 127.0.0.1 6379115446:X 09 Dec 14:25:21.656 # +vote-for-leader 328a80cfb54d5a691d2626a50f18f63a4f212832 1115446:X 09 Dec 14:25:21.656 # +elected-leader master host6379 127.0.0.1 6379115446:X 09 Dec 14:25:21.656 # +failover-state-select-slave master host6379 127.0.0.1 6379115446:X 09 Dec 14:25:21.709 # +selected-slave slave 127.0.0.1:6381 127.0.0.1 6381 @ host6379 127.0.0.1 6379115446:X 09 Dec 14:25:21.709 * +failover-state-send-slaveof-noone slave 127.0.0.1:6381 127.0.0.1 6381 @ host6379 127.0.0.1 6379115446:X 09 Dec 14:25:21.776 * +failover-state-wait-promotion slave 127.0.0.1:6381 127.0.0.1 6381 @ host6379 127.0.0.1 6379115446:X 09 Dec 14:25:22.334 # +promoted-slave slave 127.0.0.1:6381 127.0.0.1 6381 @ host6379 127.0.0.1 6379115446:X 09 Dec 14:25:22.334 # +failover-state-reconf-slaves master host6379 127.0.0.1 6379115446:X 09 Dec 14:25:22.400 * +slave-reconf-sent slave 127.0.0.1:6380 127.0.0.1 6380 @ host6379 127.0.0.1 6379115446:X 09 Dec 14:25:23.374 * +slave-reconf-inprog slave 127.0.0.1:6380 127.0.0.1 6380 @ host6379 127.0.0.1 6379115446:X 09 Dec 14:25:23.374 * +slave-reconf-done slave 127.0.0.1:6380 127.0.0.1 6380 @ host6379 127.0.0.1 6379115446:X 09 Dec 14:25:23.439 # +failover-end master host6379 127.0.0.1 6379115446:X 09 Dec 14:25:23.439 # +switch-master host6379 127.0.0.1 6379 127.0.0.1 6381115446:X 09 Dec 14:25:23.439 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ host6379 127.0.0.1 6381115446:X 09 Dec 14:25:23.439 * +slave slave 127.0.0.1:6379 127.0.0.1 6379 @ host6379 127.0.0.1 6381115446:X 09 Dec 14:25:53.495 # +sdown slave 127.0.0.1:6379 127.0.0.1 6379 @ host6379 127.0.0.1 6381
此时我们再看6380和6381的info replication:
6380还是slave,但是它的master变成了6381;
6381已经成了master,它下面有一个slave是6380。
127.0.0.1:6380> INFO replication# Replicationrole:slavemaster_host:127.0.0.1master_port:6381master_link_status:upmaster_last_io_seconds_ago:0master_sync_in_progress:0slave_repl_offset:703slave_priority:100slave_read_only:1connected_slaves:0master_repl_offset:0repl_backlog_active:0repl_backlog_size:1048576repl_backlog_first_byte_offset:2repl_backlog_histlen:64788127.0.0.1:6380>
127.0.0.1:6381> INFO replication# Replicationrole:masterconnected_slaves:1slave0:ip=127.0.0.1,port=6380,state=online,offset=1515,lag=1master_repl_offset:1648repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:2repl_backlog_histlen:1647127.0.0.1:6381>
问题:如果之前的master重启回来,会不会双master冲突?
我们恢复6379:
结果发现,6379自动变为了6381的slave。
[hadoop@localhost config6379]$ sudo /usr/local/bin/redis-serverappendonly.aof dump6379.rdb log6379.log redis6379.conf[hadoop@localhost config6379]$ sudo /usr/local/bin/redis-server redis6379.conf[sudo] password for hadoop:[hadoop@localhost config6379]$[hadoop@localhost config6379]$[hadoop@localhost config6379]$ /usr/local/bin/redis-cli -p 6379127.0.0.1:6379>127.0.0.1:6379>127.0.0.1:6379> INFO replication# Replicationrole:slavemaster_host:127.0.0.1master_port:6381master_link_status:upmaster_last_io_seconds_ago:0master_sync_in_progress:0slave_repl_offset:68108slave_priority:100slave_read_only:1connected_slaves:0master_repl_offset:0repl_backlog_active:0repl_backlog_size:1048576repl_backlog_first_byte_offset:0repl_backlog_histlen:0127.0.0.1:6379>
6381那里也收了这个原先的大哥当小弟:
127.0.0.1:6381> INFO replication# Replicationrole:masterconnected_slaves:2slave0:ip=127.0.0.1,port=6380,state=online,offset=84418,lag=1slave1:ip=127.0.0.1,port=6379,state=online,offset=84418,lag=1master_repl_offset:84551repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:2repl_backlog_histlen:84550127.0.0.1:6381>
这个时候,新的主从复制就建立了。我们最后验证一下:
我们在6381上写新数据,注意,这个时候6381是master,已经不是只读的了:
127.0.0.1:6381> set myname happybksOK127.0.0.1:6381>
其他slave,6379和6380能够复制读到新数据:
127.0.0.1:6379> get myname"happybks"127.0.0.1:6379>
127.0.0.1:6380> get myname"happybks"127.0.0.1:6380>
并且,6379作为原来的master,现在的slave,已经变为只读模式,不能再在6379上写入或修改数据了:
127.0.0.1:6379> set blogsite oschina(error) READONLY You can't write against a read only slave.127.0.0.1:6379>
一组sentinel能同时监控多个Master
还记得我们刚才配置的sentinel.conf文件中我们只配置了一行监控吗?其实sentinel可以同时监控多个master,只要你按照刚才介绍的方法配置就可以了。这里我就不举例子了,聪明如你,一定能够明白怎么弄的:)
复制的缺点
复制延时
由于所有的写操作都是先在Master上操作,然后同步更新到Slave上,所以从Master同步到Slave机器有一定的延迟,当系统很繁忙的时候,延迟问题会更加严重,Slave机器数量的增加也会使这个问题更加严重。