Table of Contents
replication 这个问题, 包括同步和failover两个方面:
master挂了之后, 如何选择新的master.
对redis来说, 这时候A会从B做一次全量同步, 从而保证一致
关于一致性, 我们可以看这个例子:
------------------------------------------------------------- +-------+ +-------+ | A | | B | +-------+ +-------+ t1 insert 1 --\ | \ t2 insert 2 --\ \-- insert 1 | \ t3 insert 3 --\ \-- insert 2 | \ t4 insert 4 \-- insert 3 --------------------------------> A down, B is master now t5 /-- insert 5 / | t6 insert 5 --/ /-- insert 6 / t7 insert 6 --- #到这里, 我们已经看到了不一致: 1,2,3,4,5,6 1,2,3,5,6
接下来我们从这几个方面, 研究redis/mysql/mongo如何实现
redis 的主从同步和aof记录的都是操作, 而不是结果, 也就是非幂等
这造成重放aof时不可重入.
STATEMENT模式记录操作, ROW模式记录结果.
一般来说, mixed性能可能更好, 多数都是用mixed模式.
Adding an AUTO_INCREMENT column to a table with ALTER TABLE might not produce the same ordering of the rows on the slave and the master
不像redis那样全量同步数据, 很难避免这些问题.
具体保存方法:
shell> mysqlbinlog binlog.0000003 The output includes events contained in binlog.000003. Event information includes the SQL statement, the ID of the server on which it was executed, the timestamp when the statement was executed, how much time it took, and so forth. Events are preceded by header comments that provide additional information. For example: # at 141 #100309 9:28:36 server id 123 end_log_pos 245 Query thread_id=3350 exec_time=11 error_code=0 In the first line, the number following at indicates the file offset, or starting position, of the event in the binary log file. The second line starts with a date and time indicating when the statement started on the server where the event originated. For replication, this timestamp is propagated to slave servers. server id is the server_id value of the server where the event originated. end_log_pos indicates where the next event starts (that is, it is the end position of the current event + 1). thread_id indicates which thread executed the event. exec_time is the time spent executing the event, on a master serve
slave通过sync命令连上来后, master 每次做完一个写操作, 就会 调用replicationFeedSlaves, 向这个socket 发送命令
也就是说, 是master主动推到slave
slave 来拉
slave拉
mongo的oplog里面是带时间戳的, 从库来同步的时候, 相当于: 找到这个时间点后的更新序列.
主库上的一个操作oplog的time为t1, 同步到丛库上后, 丛库oplog的time应该也是t1
而redis, mysql 的同步都是用一个位置来记录, 比如redis是一个offset, mysql是file + pos
repl_backlog用字节数做索引:
+------------------------------------------------+ | | | | +------------------------------------------------+ ^ ^ | | | | repl_backlog_idx repl_backlog_size feed 10个byte:: +------------------------------------------------+ | | | | +------------------------------------------------+ ^ ^ | | | | idx repl_backlog_size
客户端连上后, 通过 addReplyReplicationBacklog 发送backlog:
/* Feed the slave 'c' with the replication backlog starting from the * specified 'offset' up to the end of the backlog. */ long long addReplyReplicationBacklog(redisClient *c, long long offset) {
s和m之间有一个runid 来确认, 上次是从它开始同步的:
int masterTryPartialResynchronization(redisClient *c) {
如果master收到slave的sync命令, 要求的runid和master不同, 或者要求的oplog不在当前oplog范围内, 都要求客户端做全量同步.
假设这样一个场景:
. A / | \ B C D
A是主库, BCD是丛库, A机器宕机后, 我们决定B成为新的主库:
. B | \ C D
这里涉及第一个问题:
问题一: 如何从三个丛库B, C, D里面选择B作为新的master
我们考虑 主从切换后, 老主重新成为从库时的行为, 当A复活后, 我们希望把A挂回去, 这时候存在第二个问题:
问题二: A上可能有领先于BCD的数据, 如何让A和BCD上数据一致
最简单的做法是全量同步, 如redis/ledisdb, 实现简单, 但是太慢, 网卡打满等问题, 对于磁盘存储的数据来说, 不可接受
mysql 5.6以前, 主从同步依赖于每个mysql 实例的binlog file 和binlog pos
假设我们找到B的binlog最新, 在通过下面命令继续令CD向B同步:
stop slave; change master to master_host='',master_port=,master_user='',master_password='',master_log_file='',master_log_pos=; start slave;
当A又加回来后,无论是使用人工找同步点, 还是GTID自动找点. 都可能出现A领先于B的情况(A上的数据没有被同步到B)
参看notes/mongo/rollback.rst
我们从推拉模式, binlog格式, 如何发生主从切换来考察
- mysql redis mongo hadooop 模式 推 推 拉 推. 格式 row/statement statement row ? 主从切换机制 人肉 sentinel 选举 ? 主从切换 人肉找点/ 全量同步 自动GTID ? 后如何继续同步 用GTID同步
在mysql这样复杂, 操作丰富的数据库里面实现binlog同步有很多难点, 比如LAST_INSERT_ID() 等.
oplog记录操作和记录结果, 各有好处.
GTID的全称为 global transaction identifier , 可以翻译为全局事务标示符,GTID在原始master上的事务提交时被创建。GTID需要在全局的主-备拓扑结构中保持唯一性,GTID由两部分组成:
A global transaction identifier (GTID) is a unique identifier created and associated with each transaction committed on the server of origin (master). This identifier is unique not only to the server on which it originated, but is unique across all servers in a given replication setup. There is a 1-to-1 mapping between all transactions and all GTIDs.
GTID = source_id:transaction_id
一个GTID的生命周期包括: 1.事务在主库上执行并提交 给事务分配一个gtid(由主库的uuid和该服务器上未使用的最小事务序列号),该GTID被写入到binlog中。 2.备库读取relaylog中的gtid,并设置session级别的gtid_next的值,以告诉备库下一个事务必须使用这个值 3.备库检查该gtid是否已经被其使用并记录到他自己的binlog中。slave需要担保之前的事务没有使用这个gtid,也要担保此时已分读取gtid,但未提交的事务也不恩呢过使用这个gtid. 4.由于gtid_next非空,slave不会去生成一个新的gtid,而是使用从主库获得的gtid。这可以保证在一个复制拓扑中的同一个事务gtid不变。
支持启用GTID,对运维人员来说应该是一件令人高兴的事情,在配置主从复制,传统的方式里,你需要找到binlog和POS点,然后change master to指向,而不是很有经验的运维,往往会将其找错,造成主从同步复制报错,在mysql5.6里,无须再知道binlog和POS点,需要知道master的IP、端口,账号密码即可,因为同步复制是自动的,mysql通过内部机制GTID自动找点同步。
mysql> show slave status\G *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 192.168.18.201 Master_User: repluser Master_Port: 3306 Connect_Retry: 60 Master_Log_File: master-bin.000001 Read_Master_Log_Pos: 151 Relay_Log_File: relay-log.000002 Relay_Log_Pos: 363 Relay_Master_Log_File: master-bin.000001 Slave_IO_Running: Yes #IO线程与SQL线程都是yes,说明复制启动完成。 Slave_SQL_Running: Yes Replicate_Do_DB: Replicate_Ignore_DB: Replicate_Do_Table: Replicate_Ignore_Table: Replicate_Wild_Do_Table: Replicate_Wild_Ignore_Table: Last_Errno: 0 Last_Error: Skip_Counter: 0 Exec_Master_Log_Pos: 151 Relay_Log_Space: 561 Until_Condition: None Until_Log_File: Until_Log_Pos: 0 Master_SSL_Allowed: No Master_SSL_CA_File: Master_SSL_CA_Path: Master_SSL_Cert: Master_SSL_Cipher: Master_SSL_Key: Seconds_Behind_Master: 0 Master_SSL_Verify_Server_Cert: No Last_IO_Errno: 0 Last_IO_Error: Last_SQL_Errno: 0 Last_SQL_Error: Replicate_Ignore_Server_Ids: Master_Server_Id: 1 Master_UUID: 6b27d8b7-0e14-11e3-9eab-000c291192e4 Master_Info_File: mysql.slave_master_info SQL_Delay: 0 SQL_Remaining_Delay: NULL Slave_SQL_Running_State: Slave has read all relay log; waiting for the slave I/O thread to update it Master_Retry_Count: 86400 Master_Bind: Last_IO_Error_Timestamp: Last_SQL_Error_Timestamp: Master_SSL_Crl: Master_SSL_Crlpath: Retrieved_Gtid_Set: Executed_Gtid_Set: Auto_Position: 1 1 row in set (0.00 sec)