[사례] 플리커 - 티켓서버

혀어어언·2025년 10월 12일
0

"대규모 시스템 설계" 책 스터디 중
티켓서버 관련 내용을 보완하기 위해 서칭하다가 찾게 된 내용입니다
플리커의 기술 블로그 내용 중 "대규모 시스템 설계"와 관련있고, 제가 관심있는 부분을 정리하였습니다(참고: 2010년 2월 8일 작성)

제 포스팅을 읽는 다른 분들에게도 도움이 되는 내용이기를 바랍니다.


1. 플리커의 티켓서버

1-1. 플리커의 데이터 저장 방식

  • 플리커는 샤딩 방식으로 데이터 저장소를 확장하고 있음
    - 하나의 거대한 DB 대신 여러 DB에 데이터를 분산저장함으로써 부하 분산
  • DB 간 데이터 마이그레이션이 필요한 경우 존재

1-2. MySQL 샤드

  • 마스터-마스터 복제 쌍으로 구성되어 있음
  • 샤드 간 이동과 마스터-마스터 복제 모두에서 충돌이 없도록 전역적으로 고유한 키가 보장되어야 함

1-3. GUID를 사용할 수도 있지 않을까?

  • 전역적으로 유일한 키가 필요하다면 GUID를 고려해도 될 것 같은데 쓰지 않는 이유?
  • GUID는 크기가 크고, MySQL에서 인덱스 성능이 좋지 않음

1) 플리커의 MySQL 성능 유지 방법

  • 쿼리 대상이 되는 모든 항목에 인덱스 생성
  • 인덱스만으로 쿼리 수행
  • GUID는 인덱스 팽창 문제 때문에 이러한 전략과 상충함

2) 티켓 서버의 유용성

  • 티켓 서버는 순차성(sequentiality) 제공
  • 리포팅과 디버깅을 좀 더 직관적으로 만들어주고
  • 캐싱 트릭(ID 순서를 이용한 캐시 전략)이 가능함

1-4. 안전 해시를 사용할 수도 있지 않을까?

  • 아마존의 Dynamo 같은 시스템은 안전 해시를 통해 GUID/샤딩 문제 처리함
    - 이는 쓰기 비용이 낮은(Write-cheap) 환경(예:LSM-tree)에 적합함
  • MySQL은 빠른 랜덤 읽기(fast random reads)에 최적화 되어 있음
  • Flickr는 MySQL의 빠른 랜덤 읽기 특성을 살리기 위해 순차 ID를 유지하는 선택을 한 것

2. 중앙 집중 Auto-Increments

2-1. 한계

  • 단일 DB에서 ID만 발급받는 방식은 단순함
  • 하지만 초당 60장+ 업로드와 댓글/즐겨찾기/그룹/태그 등 모든 엔터티가 ID를 필요로 해 중앙 테이블이 급격히 비대해짐
  • 그래서 Flickr는 티켓 서버라는 가벼운 시퀀스 전용 테이블을 운용

2-2. 플리커 방식의 중앙 집중 Auto-Increments

  • 단일 DB에 새 행 삽입 시, 해당 테이블의 자동 증가 ID를 모든 DB의 기본키로 사용
  • MySQL의 REPLACE INTO를 통해 기존 행 삭제 후 새 행 INSERT
    - DB의 단일 행을 원자적으로 업데이트하고, 자동 증가된 새 기본키를 획득
    • 참고: 일반적으로는 INSERT … ON DUPLICATE KEY UPDATE가 더 권장됨
  • 플리커의 티켓 서버는 전용 DB 서버로 단일 DB를 보유하고 있음
  • 해당 DB에는 32비트 ID용 Tickets32 테이블과 64비트 ID용 Tickets64 테이블 등이 존재

1) 테이블 구조

CREATE TABLE `Tickets64` (
  `id` bigint(20) unsigned NOT NULL auto_increment,
  `stub` char(1) NOT NULL default '',
  PRIMARY KEY  (`id`),
  UNIQUE KEY `stub` (`stub`)
) ENGINE=InnoDB;

2) 조회

  • Tickets64 테이블에서 조회 수행 시 다음과 같이 단일 행이 반환됨
+-------------------+------+
| id                | stub |
+-------------------+------+
| 72157623227190423 |    a |
+-------------------+------+

3) 전역 유니크 키 발급

  • stub는 유니크 제약이 걸린 더미 컬럼으로, 테이블에는 보통 'a' 한 값만 유지
REPLACE INTO Tickets64 (stub) VALUES ('a');  -- PK/UNIQUE 충돌 시 기존 행 삭제 후 새 행 INSERT
SELECT LAST_INSERT_ID(); -- 반드시 같은 커넥션에서 호출

3. SPOFs

  • 티켓서버 방식에는 티켓서버 그 자체가 단일 장애지점이 될 수 있다는 문제가 있음
  • 이 문제를 해결하기 위해 플리커에서는 ID 공간을 짝수/홀수로 나눈 두 개의 티켓 서버 운영
    • 라운드 로빈 방식을 통해 두 서버 간 로드밸런싱과 다운타임 처리
    • 참고: 양쪽 카운트가 조금 달라도 기능적으로 무방함 (실제 플리커도 홀수가 더 많다고 함)
TicketServer1:
auto-increment-increment = 2
auto-increment-offset = 1

TicketServer2:
auto-increment-increment = 2
auto-increment-offset = 2

4. 여러 시퀀스 분리 운용

4-1. 개별 시퀀스 테이블 운용

  • OfflineTasks: 발급량이 많아 다른 개체들의 카운트를 불필요하게 끌어올리지 않으려 분리
  • Groups/Accounts: 발급량이 적어 따로 분리
  • Photos: 과거 auto-increment와 동기화해 총 업로드 수의 직관적 파악이 가능하도록 설계

5. 작동하는 가장 단순한 것

  • 플리커의 티켓서버 방식은 2006년 1월 13일(금)부터 운영
  • 특별히 우아한 방식은 아니지만, 작동하는 가장 단순한 것 설계 원칙을 보여주는 훌륭한 사례

6. 레퍼런스

0개의 댓글