포토로그



"프랜드피드는 어떻게 MySQL을 사용해서 스키마 없는 데이터를 저장하나" 요약


상황

프랜드피드는 약 2억 5천만 개의 피드 항목을 저장하고 있으며 다른 데이터도 많다. 서비스가 성장하면서 스케일링 문제 겪었고 복제, 캐시, 분할 등을 도입하여 해결했다. 하지만 성장을 거듭할수록 기존 기능을 더 많은 트래픽에서 사용할 수 있게 하는 것보다 새 기능을 추가하기가 더 어려워졌다.

문제

특히 스키마 변경과 인덱스 추가 시에는 몇 시간씩 데이터베이스를 멈춰야 했다. 쓰지 않는 인덱스를 제거하는 것도 시간이 걸렸고 제거하지 않으면 INSERT 마다 사용하지도 않는 인덱스 블럭을 읽고 써야 해서 중요한 데이터가 캐시에서 밀려나 성능 저하를 가져왔다. 슬레이브에서 새 인덱스를 만들어서 슬레이브와 마스터를 교체하는 등의 방법도 써보았지만 복잡한 작업이라 실수하기 쉽다. 그래서 스키마나 인덱스 변경이 필요한 새로운 기능은 암묵적으로 제제되었다.

해결책

데이터베이스는 상당히 분할되어 있어서 RDBMS의 JOIN 등은 전혀 쓸모가 없었다. CouchDB 등 유연한 스키마와 온라인 인덱스 변경이 가능한 비관계형 데이터베이스를 살펴봤으나 아직 대규모 사이트에서 널리 사용되지도 않고, 충분히 안정적이지도 않았다. MySQL은 안정적이고, 이미 장단점을 잘 알고 있어서 MySQL을 저장소로 쓰는데 아무 불만이 없다. MySQL 위에 스키마가 없는 데이터베이스를 구현하기로 했다.

새 데이터베이스 API는 다음과 같이 동작한다.

  • 객체를 담을 테이블을 만들고 PK로 UUID를 쓴다. 객체는 테이블에 인코딩하여 저장한다.
  • 검색를 위한 인덱스가 필요하면 인덱스 테이블을 만들고 검색에 필요한 필드만 컬럼으로 추가한다.
  • 객체가 추가, 변경, 삭제 되면 데이터 테이블과 인덱스 테이블들에 객체와 인덱스들을 추가, 변경, 삭제한다.
  • 오류로 인해 인덱스 테이블이 데이터 테이블과 일치하지 않을 수 있다. 이는 별도의 인덱스 동기화 프로세스를 돌려서 즉시 복구될 수 있도록 한다.

기존 구성과의 차이점을 그림으로 나타내면 다음과 같다.

결과

안정적이고 편리한 관계형 데이터베이스를 쓰면서도 데이터베이스를 멈추지 않고 스키마 변경이나 인덱스 추가, 삭제를 할 수 있게 되었다. 객체를 JSON 등으로 인코딩하여 한 컬럼에 저장하기 때문에 스키마 변경은 전적으로 어플리케이션에서 다룰 수 있다. 인덱스도 별도의 인덱스 테이블로 관리되기 때문에 가동 중에 추가하고 삭제할 수 있다.

추가로 다음과 같은 장점도 얻을 수 있다.
  • 성능 향상. 평균 대기 시간이 짧아졌으며 대기 시간의 편차도 줄었다.
  • 객체 단위의 캐시 적용이 아주 쉽다.
  • 인덱스마다 다른 컬럼을 기준으로 분할 할 수 있다.


덧글

댓글 입력 영역