Redis 4.0起引入了PSYNC2同步方式,分析源码时我们注意到,server数据中增加了replid2
、second_replid_offset
两个成员。进一步分析逻辑可知,这两个成员是在master变更后,在slave中用于保存原master的ID和已同步的数据偏移的。那他们有什么作用呢?
PSYNC2用以提高特定情况下的从机数据同步效率。比如对于以下拓补
A
+
|
+---+
| |
v v
B C
A为master,A同步数据到B、C。当A故障后,假如B被选为新的master,C成为B的从机。在过去C需要做一次数据全同步才能重新提供服务;而支持PSYNC2后,C大概率只需要从B同步最新的自己还没有接收到的数据即可,就好像自己的master还是A,只是短暂掉线重连了一样。PSYNC2相关的详细资料网络上已有很多,本文不再赘述。
上面提到,C大概率只需要从B同步最新的自己还没有接收到的数据,那就意味着有些情况下C还是需要全同步B的的。考虑下面的情况,还是A同步数据到B、C,当A中最新数据偏移为250时A故障下线,此时B接收到的最新数据偏移为200、C为175。
+----------------+ +----------------+ +----------------+
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| last_byte | | | | |
+----------------+250 | | | |
| | | | | |
| +--------> +----------------+200 | |
| | | | +----------------+175
| | | received | | |
| | | from A | | received |
| | | | | from A |
| | | | | |
+----------------+ +----------------+ +----------------+
Redis A Redis B Redis C
经过哨兵调度,B成为新的master,C成为B的从机。经过一段短暂的时间,B最新数据偏移到达300,而C从B处也接收到了所有数据。此时A恢复了,上线后被哨兵调度为B的从机。因为A之前的最新数据偏移为250,所以发送PSYNC指令请求从251开始部分同步数据。如果B不做判断从251位置开始给A发送数据,很显然A中201-250这一段数据将与B、C中 的不一致。因为A故障后,201-250的数据尚未来得及同步给B,而B在后来已经将新数据追加到从A同步来的的200字节之后了,这些数据与A中的数据是不一致的。
+----------------+ +----------------+ +----------------+
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | | | |
| | | last byte | | |
+----------------+ +----------------+300 +----------------+300
| recv from B | | | | |
+----------------+250<---X--+ new data | | received |
| inconsistent | | | | from B |
+----------------+ +----------------+200 | |
| | | | +----------------+175
| consistent | | received | | |
| | | from A | | received |
| | | | | from A |
| | | | | |
+----------------+ +----------------+ +----------------+
Redis A Redis B Redis C
为了避免上述问题,B需要记住原master的ID,以及从原master获得的数据偏移。当有从机带着原master的ID来请求数据时,需要先检查请求的偏移量是否超过了自己已获得的数据量。如果是,那么从机需要丢弃所有数据并做全同步,以确保数据一致。这就是replid2
、second_replid_offset
的作用。详见replication.c:masterTryPartialResynchronization()
中这段逻辑:
/* Is the replication ID of this master the same advertised by the wannabe
* slave via PSYNC? If the replication ID changed this master has a
* different replication history, and there is no way to continue.
*
* Note that there are two potentially valid replication IDs: the ID1
* and the ID2. The ID2 however is only valid up to a specific offset. */
if (strcasecmp(master_replid, server.replid) && //当前server的ID不是master的ID
(strcasecmp(master_replid, server.replid2) || //并且,自己上一个master不是从机的master 或
psync_offset > server.second_replid_offset)) //是同一个master但从机要求的偏移比自己接收的数据还大
{
/* Run id "?" is used by slaves that want to force a full resync. */
if (master_replid[0] != '?') {
if (strcasecmp(master_replid, server.replid) &&
strcasecmp(master_replid, server.replid2))
{
serverLog(LL_NOTICE,"Partial resynchronization not accepted: "
"Replication ID mismatch (Slave asked for '%s', my "
"replication IDs are '%s' and '%s')",
master_replid, server.replid, server.replid2);
} else {
serverLog(LL_NOTICE,"Partial resynchronization not accepted: "
"Requested offset for second ID was %lld, but I can reply "
"up to %lld", psync_offset, server.second_replid_offset);
}
} else {
serverLog(LL_NOTICE,"Full resync requested by slave %s",
replicationGetSlaveName(c));
}
goto need_full_resync;
}
那么,A中原来的201-250的那段数据呢?丢了呗,还能咋滴。。。要不怎么说Redis并不可靠,不要当关键存储用。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。