Foundry
실시간 채팅 시스템 설계
심화
핵심

순서 보장과 파티션 나누기

채널 안에서만 보장하면 채널로 나눌 수 있다

앞 단계에서 연결을 받는 계층과 메시지를 처리하는 계층을 나눴습니다. 처리 계층을 여러 대로 늘리면 곧바로 문제가 하나 생깁니다. 같은 채널의 메시지가 서로 다른 서버에서 처리되면 순서가 뒤바뀝니다.

요구사항은 순서를 채널 안에서만 보장한다고 정했습니다. 이 절은 그 약한 보장을 어떻게 지키고, 그 대가로 무엇을 얻는지 다룹니다.

시계로는 순서를 정할 수 없다

가장 먼저 떠오르는 방법이 보낸 시각으로 정렬하는 것입니다. 이게 안 되는 이유가 두 개입니다.

문제무슨 일이 생기나
서버마다 시계가 다르다나중에 보낸 메시지가 더 이른 시각을 갖는다
같은 밀리초에 둘이 들어온다정렬 기준이 없어 조회할 때마다 순서가 바뀐다

클라이언트 시각을 쓰면 더 나쁩니다. 사용자 기기의 시계는 얼마든지 틀릴 수 있습니다.

그래서 서버가 채널마다 하나씩 증가하는 번호를 붙입니다. 이 번호가 순서의 유일한 근거가 됩니다. 시각은 화면에 보여주는 용도로만 씁니다.

채널로 나누면 순서가 남는다

번호를 붙이려면 그 채널의 메시지가 한 곳을 순서대로 지나야 합니다. 모든 채널이 한 곳을 지나면 그곳이 상한이 되지만, 채널을 기준으로 나누면 채널 수만큼 병렬로 처리할 수 있습니다.

채널을 기준으로 파티션을 나누면 같은 채널의 메시지 순서가 파티션 안에 남는다 보낸 메시지 파티션 1 A 12 A 13 A 14 파티션 2 B 7 B 8 채널 A 채널 B 채널 번호로 나눈다 같은 채널은 늘 같은 파티션으로 간다 파티션이 다르면 순서를 보장하지 않는다. 그래도 된다

채널 번호로 어느 파티션에 넣을지 정하면 같은 채널은 늘 같은 파티션으로 갑니다. 파티션 안에서는 들어온 순서대로 처리되므로 채널 안 순서가 저절로 지켜집니다.

파티션 1의 메시지와 파티션 2의 메시지 사이 순서는 보장되지 않습니다. 요구사항이 그것을 요구하지 않기 때문에 문제가 아닙니다. 포기한 보장이 정확히 이 확장을 사 준 것입니다.

파티션 수를 바꾸는 순간이 위험하다

파티션을 늘리면 나누는 기준이 바뀌어 일부 채널이 다른 파티션으로 옮겨 갑니다. 옮겨 가는 도중에는 같은 채널의 메시지가 두 파티션에 걸치고, 그 구간에서만 순서가 깨집니다.

방법대가
옮길 채널만 잠깐 멈추고 남은 것을 비운 뒤 옮긴다그 채널이 몇 초 지연된다
일관 해싱으로 옮겨 가는 채널 수를 줄인다구현이 복잡해진다

어느 쪽이든 파티션 수는 처음에 넉넉하게 잡는 편이 낫습니다. 늘리는 일이 공짜가 아니기 때문입니다.

큰 채널 하나가 파티션을 독점한다

최대 5만 명 채널은 메시지도 많습니다. 그 채널이 들어간 파티션 하나만 뜨거워지고 나머지는 한가한 상태가 됩니다. 파티션을 늘려도 한 채널은 쪼개지지 않으므로 해결되지 않습니다.

이때 쓰는 방법이 순서 보장 범위와 전달을 떼어 놓는 것입니다. 번호를 붙이는 일만 그 채널 전용 자리에서 하고, 5만 명 에게 실제로 보내는 일은 여러 일꾼이 나눠 맡습니다. 순서는 번호로 이미 정해졌으니 전달 순서는 상관없습니다. 받는 쪽이 번호로 정렬합니다.

화면은 서버 번호를 기준으로 다시 세운다

사용자 경험을 위해 보낸 메시지를 서버 응답 전에 화면에 먼저 그립니다. 그때는 번호가 없으니 임시로 맨 아래에 둡니다.

서버 응답으로 번호를 받으면 그 자리에 다시 꽂습니다. 내가 보내는 동안 남이 보낸 메시지가 먼저 번호를 받았다면 내 메시지가 한 칸 아래로 내려갑니다. 번호가 있는 것끼리는 늘 번호 순으로 두는 것이 규칙입니다.

면접에서 이렇게 나옵니다

Q.같은 채널의 메시지 순서를 어떻게 보장하나요

서버가 채널마다 증가하는 번호를 붙이고, 채널을 기준으로 파티션을 나눕니다.

시각으로 정렬하지 않습니다. 서버 시계가 서로 다르고 같은 밀리초에 두 건이 들어올 수 있어서 정렬 결과가 흔들립니다.

채널 번호로 파티션을 고른다
같은 채널은 늘 같은 파티션으로 간다
파티션 안에서는 들어온 순서대로 번호가 붙는다

파티션이 다른 채널끼리는 순서를 보장하지 않지만, 요구사항이 채널 안에서만 보장한다고 정했으므로 문제가 아닙니다. 이 포기가 채널 수만큼의 병렬 처리를 사 줍니다.

흔한 실수: 데이터베이스의 자동 증가 값을 순서로 쓰는 것. 그 값은 전역이라 모든 쓰기가 한 곳을 지나야 하고, 필요 없는 전역 순서를 위해 확장 여지를 버리는 셈입니다. 그리고 여러 대가 미리 번호 구간을 받아 쓰는 방식이면 채널 안 순서도 어긋납니다.

Q.파티션 수를 늘리면 순서가 깨지지 않나요

옮겨 가는 동안 깨집니다. 나누는 기준이 바뀌면 일부 채널이 다른 파티션으로 이동하고, 그 순간 같은 채널의 메시지가 두 파티션에 걸칩니다.

방법대가
옮길 채널을 잠깐 멈추고 남은 것을 비운 뒤 옮긴다그 채널이 몇 초 지연된다
일관 해싱으로 옮겨 가는 채널 수를 줄인다구현이 복잡해진다

그래서 파티션 수는 처음에 넉넉하게 잡습니다. 지금 필요한 수보다 크게 두고 한 대가 여러 파티션을 맡게 하면, 나중에는 파티션을 옮기는 대신 담당만 바꾸면 됩니다.

흔한 실수: 무중단으로 늘릴 수 있다고 답하는 것. 순서를 보장하는 시스템에서는 재분배 구간의 짧은 정지가 정상적인 대가입니다. 그것을 인정하고 어느 범위를 얼마나 멈추는지 말하는 편이 훨씬 신뢰를 줍니다.

Q.5만 명 채널 하나가 한 파티션을 독점하면 어떻게 하나요

번호를 붙이는 일과 전달하는 일을 떼어 놓습니다.

파티션을 늘려도 해결되지 않습니다. 한 채널은 쪼개지지 않으므로 그 채널이 들어간 파티션은 그대로 뜨겁습니다.

하는 일어디서
순서 번호 붙이기그 채널 전용 자리 한 곳. 순서가 필요한 부분은 여기뿐
5만 명 에게 전달여러 일꾼이 나눠 맡는다

전달 순서는 상관없습니다. 순서는 번호로 이미 정해졌고 받는 쪽이 번호로 정렬합니다. 큰 채널만 이 경로를 타게 하고, 평균 12명 채널은 그대로 둡니다.

흔한 실수: 큰 채널을 여러 파티션으로 쪼개 순서 보장을 포기하는 것. 한 대화방 안에서 순서가 흔들리면 사용자가 바로 알아차립니다. 순서가 필요한 구간을 최소로 줄이는 것과 순서를 버리는 것은 다릅니다.

Q.보낸 메시지를 응답 전에 화면에 그리면 순서가 어긋나지 않나요

어긋납니다. 그래서 번호를 받은 뒤 다시 정렬합니다.

응답을 기다리면 느리게 느껴지므로 보낸 메시지를 먼저 그립니다. 그때는 서버 번호가 없으니 임시 상태로 맨 아래에 둡니다.

번호가 있는 메시지끼리는 번호 순으로 둔다
번호가 없는 내 메시지는 그 아래에 둔다
번호를 받으면 제자리에 꽂는다

내가 입력하는 동안 남의 메시지가 먼저 번호를 받았다면 내 메시지가 한 칸 아래로 내려갑니다. 화면이 살짝 움직이지만 결과는 모든 사용자에게 같습니다.

흔한 실수: 임시로 그린 메시지에 클라이언트가 만든 번호를 주는 것. 서버 번호와 섞이면 정렬 기준이 두 개가 되어 사용자마다 다른 순서가 보입니다. 임시 메시지는 번호 없는 상태로 두고, 재전송 때 같은 것으로 알아보게 할 임시 키만 따로 붙입니다.

먼저 스스로 답해보고 아래 답변과 견줘보세요. 막히는 부분은 문제로 확인할 수 있어요.

읽었으면 문제로 확인해보세요

실시간 채팅 시스템 설계 문제를 풀면 틀린 문제가 자동으로 노트에 쌓입니다. 가입 없이 5문제를 먼저 풀어볼 수도 있어요.