[입 개발] 왜 Redis는 마스터/마스터 리플리케이션을 지원하지 않을까?

입으로만 개발하고 손으로는 개발하지 않는 입개발 전문가로써 쿨럭… 지금 무슨 얘기를… 다시 원래의 이야기로 돌아가서… 가끔씩 외국도 그렇고 국내도 그렇고 사람들은 다 똑같은가 봅니다. 왜 레디스는 마스터/마스터 리플리케이션을 지원하지 않는가에 대해서 물어봅니다. 뭐, 클러스터만 되면 어느정도 해결이 될것 같기도 하지만… 레디스의 경우 마스터/슬레이브에 대한 리플리케이션이 존재하는 지라… 이것 조금만 고치면 되지 않겠냐라고 생각하시는 분들도 많은 것 같습니다.(저만 그렇게 생각할지도…)

그런데 마스터/마스터 리플리케이션 자체가 그렇게 쉬운 기술이 아닙니다.(저한테만!!! 어려울지도…) 그리고 현재 레디스의 마스터/슬레이브 리플리케이션 구조가 마스터/마스터를 지원하기 힘든 아키텍처로 구성이 되어 있습니다. 그렇다면 먼저 레디스의 리플리케이션 구조를 살펴보는게 우선일듯 합니다.

간단하게 설명하면, 자신이 요청 받은 커맨드가 update 관련(insert/update/delete) 라면 그냥 그대로 자신의 슬레이브 노드들에게 다시 전달해 버립니다. 뭐, 그게 어째서 라고 말씀하시는 분들은 DB의 Bin 로그 같은 것들도 비슷한 방식이지 않냐라고 항변 하시면 할 말이 없습니다만… 여기서 그게 가능할려면 전제조건이 하나 붙어야 합니다. 즉, 자신에게 다시 해당 패킷이 돌아왔을 때, 이게 발신자가 자신이면 거기서 멈춰야 하는 것이죠. 그런데 레디스에서는 이런 정보를 붙일 부분이 없어서, 그렇게 구현을 해버리면 자신에게 해당 패킷이 오면, 계속 다시 무한 반복해서 돌아오게 될것입니다. 즉, 기존의 리플리케이션 구조가 바뀌지 않는 한은 아직은 어렵다라는 거죠.

실제로 Hbase 같은 경우는 클러스터간 마스터/마스터 리플리케이션을 위해서, 패킷에 누가 최초에 명령을 수행해서 리플리케이션 되는 것인지 정보가 추가로 붙어서 전달되게 됩니다. HBase Replication(http://blog.cloudera.com/blog/2012/07/hbase-replication-overview-2/)을 보면 ClusterID를 설정해서 보낸다고 되어 있습니다. 레디스는 현재 이런 작업이 안되어 있는 겁니다. 사이클이 생겼을 때는 또 어떻게 처리할 것인지 등등등 고민거리가 여기에도 꽤 많이 있습니다.

redis.c 의 call() 함수를 보면 propagate() 를 사용하는데, 여기서 AOF와 Replication을 실제로 호출하게 됩니다.

void propagate(struct redisCommand *cmd, int dbid, robj **argv, int argc,
               int flags)
{
    if (server.aof_state != REDIS_AOF_OFF && flags & REDIS_PROPAGATE_AOF)
        feedAppendOnlyFile(cmd,dbid,argv,argc);
    if (flags & REDIS_PROPAGATE_REPL)
        replicationFeedSlaves(server.slaves,dbid,argv,argc);
}

그리고 replication.c 의 replicationFeedSlaves()를 살펴봅니다. 사실 여기서 제 뻥을 하나 눈치채셔야 합니다. 그대로 보낸다라고 했지만, 실제로는 여기서 못보낼 상황이라면 계속 버퍼를 모으게 됩니다. 보낼 수 있다면 바로보냅니다. 여기서도 최종적으로 데이터 변경이 되는 경우에만 리플리케이션이 됩니다. 예를 들어서 이미 지워진 데이터에 대해서 계속 del을 해도 해당 명령은 전달이 되지 않습니다. 이를 확인하는 것은 telnet 으로 레디스에 붙은 뒤에 sync 라는 명령을 주고 실제 레디스 서버에 set 이나 del을 해보시면 됩니다.

SELECT
$1
0
*3
$3
set
$1
a
$3
123
*2
$3
del
$1
a
*1
$4
PING
*1
$4
PING