본문 바로가기

데이터베이스(DA, AA, TA)/대용량DB

[대용량DB] Sharding & Query Off Loading

출처: http://bcho.tistory.com/670

 

RDBMS는 크게 request 바로 처리하는 트랜잭션 처리용의 OLTP(On-Line Transaction Processing)성과, 데이터를 모아서 분석하고 리포팅하는 OLAP(On-Line Analytical Processing) 두가지로 분리됩니다. 여기서 설명하는 RDBMS는 OLTP성의 데이터베이스입니다. 

 

RDBMS는 2차원 테이블 구조의 데이터를 KEY 값을 중심으로 여러개의 컬럼으로 저장되며, 저장된 각각의 로우(행)은 다른 테이블의 로우와 관계를 가질 수 있습니다.

 

RDBMS를 이요한 설계를 하는데, 고려할만한 아키텍쳐는 성능향상을 위한 Query Off Loading과, Sharding이라는 기법이 있습니다.

 

 

Query Off Loading


Query Off Loading은 DB의 성능 향상을 위한 기법입니다. (정확하게 이야기하면, 처리량을 증가시키기 위한 설계 기법) DB 트랜잭션의 70~90%는 대부분 READ성이 많습니다. 나머지 10~30%가 Create/Delete/Update와 같은 트랜잭션으로, Update성 트랜잭션과 Read 트랜잭션을 분리하는 기법입니다.

 

 

먼저 Master DB에는 쓰기(Update)만을 허용하고, Master DB의 내용을 중간의 Staging DB라는 곳으로 복사합니다. 이 Staging DB는 복제된 내용을 N개의 Slave DB로 복제합니다.

 

애플리케이션은 Master DB에만 쓰기를 하고, 읽기는 Slave DB에서만 수행합니다. 이를 위해서 Application을 DB에 대한 쓰기 로직과 읽기 로직을 분리해서 구현해야 하며, 이 분리된 로직은 쓰기 DB로 접근하기 위한 DB Connection과 읽기 DB로 접근하기 위한 DB Connection을 이용해서 접근합니다. 일반적으로 application server에서는 이러한 Connection을 ConnectionPool을 이용해서 관리하는데, 읽기 DB의 경우에는 N개의 Slave DB로부터 읽기 때문에, Application이 이 N개의 Slave DB에 대한 요청을 Load Balancing을 해야 합니다. 

 

또한, 특정 Slave DB 장애시 다른 Slave DB 인스턴스에 접근할 수 있도록 HA(High Availability) 기능을 제공해야 하는데 Connection Pool 자체에 Load Balancing과 HA 기능을 가지고 있는 Connection Pool을 이용하거나 또는 JDBC Driver와 같이 DBMS용 Driver 자체에 Load Balancing과 HA 기능을 사용합니다. 

 

Master DB와 Slave DB는 각각 쓰기와 읽기를 위해서 접근된다고 하지만 그렇다면 중간의 Staging DB의 역할을 무엇일까요? Staging DB는 Slave DB로 복제하기 위한 중간 경우지 역할을 합니다. 다수의 Slave DB로 복제를 해야하기 때문에, 이에 대한 부하가 큽니다. 만약 Master DB에서 Slave DB로 바로 복제를 하게 되면, Master DB가 쓰기 트랜잭션 이외에 복제에 대한 부분을 처리해야 하기 때문에 성능 저하를 유발할 수 있습니다. 이를 방지하기 위해 중간에 Staging DB를 넣는 것입니다.

 

 

Master → Staging → Slave DB로의 복제는 어떤 기술을 이용할까요?

CDC(Change Data Capture)라는 기술을 이용합니다. DBMS들은 공통적으로 Create/Update/Delete와 같은 쓰기 관련 작업을 수행할때, 데이터를 실제로 저장하기 전에 먼저 해당 작업에 대한 request를 BackLog라는 곳에 저장합니다. (BackLog는 일반적으로 Local 파일) 이는 실제로 데이터를 쓰기전에 장애가 났을때, restart하면서 이 BackLog를 읽어서 복구를 위한 용도로 사용이 됩니다. CDC는 Back Log를 이용해서 데이터를 복제하는데, Source DB로부터 이 Back Log를 읽어서, 복제를 하고자하는 Target DB에 replay하는 형식입니다.

 

즉 Source DB에서, insert A,B,C를 하면 이는 모두 Back Log에 기록되고, 이를 읽어서 Target DB에서 다시 replay - insert A,B,C를 순차적으로 수행하는 것입니다.

 

대표적인 CDC 제품으로는 Oracle의 Golden Gate, Quest의 Share Flex가 있고, 오픈소스 제품으로는 Galera라는 제품이 있습니다.

 

 

Sharding


Sharding은 데이터베이스의 용량 한계를 극복하기 위한 기술입니다. 클러스터링 기술을 사용하더라도 데이터베이스는 물리적인 용량 한계를 갖는 경우가 많습니다. 수년전만 해도, 하나의 서비스에 수천만명이 사용하는 서비스는 없었습니다. 인터넷의 발전등에 따라서 사용자가 급격하게 늘어나고 저장해야 하는 데이터의 양도 급격하게 늘어났습니다. 데이터베이스 시스템은 이러한 용량 증가를 따라가지 못했습니다. 그래서 Sharding이라는 아키텍처가 유행하기 시작했는데, Sharding은 쉽게 말해서 데이터를 여러개의 데이터베이스에 나눠 담는 방법입니다.

 

하나의 데이터베이스가 10억개의 레코드만 저장할 수 있다면, 100억개의 데이터를 저장하려면 10개의 데이터베이스를 사용하여 분산 저장하는 방법입니다. 이 각 10개의 데이터베이스를 Shard라고 합니다.

 

Sharding은 데이터를 분산하는 방식에 따라 Vertical Sharding과 Horizontal Sharding으로 나뉘어집니다. Vertical Sharding은 연속된 데이터에 대해서 범위별로 데이터를 나누는 방법입니다.

 

 

 

다음으로 Horizontal Sharding이 있는데, 이는 연속된 키가 아니라 "Category"와 같은 종류에 따라서 데이터를 수평적으로 분리하는 방법입니다.

 

 

 

데이터를 분산 저장할때 위와 같이 meaningful한 데이터를 사용할 수 있는데, 이 경우에는 데이터의 몰림현상이라는 것이 발생할 수 있습니다. 위의 Vertical Sharding을 예로 들어보면, 해당 서비스를 사용하는 연령층이 20대와 30대에 편중되어 있다면, 20,30대 Shard에는 데이터가 몰리게 되고, 부하도 더 많이 받게될 것입니다.

 

그래서 이렇게 meaningful한 데이터를 KEY로 사용할 경우에는 데이터 몰림현상을 고려하여 각 Shard 서버의 성능을 비대칭적으로 설계할 수 있습니다. 즉 20,30대의 Shard에는 더 좋은 CPU와 메모리를 갖는 서버를 배치하는 방법이 대안이 됩니다.

 

만약에 데이터 편중 현상에 대한 예측이 어려운 경우에는, meaningful하지 않은 KEY를 사용해서 Sharding을 할 수 있습니다. 10개의 Shard를 갖는 데이터베이스에서, 사용자레코드를 등록할때, KEY를 Sequence를 이용해서 순차적으로 부여합니다. 첫번째 사용자는 1, 두번째는 2, 다음은 3,4,5,6 ... 등으로 그리고 이 ID를 10으로 나눈 나머지 값을 가지고 Shard를 결정하면, 데이터를 모든 Shard에 걸쳐서 골고루 분산시켜 배치할 수 있습니다(Hash 방식)

 

Sharding을 구현하는 방법은 DBMS단에서 Sharding을 지원하는 방법과, OR Mapper와 같은 DB 접근용 프레임워크에서 Sharding을 제공하는 방법 그리고, Application Code 자체에서 지원하는 방법 세가지가 있습니다.

 

DBMS단에서 제공하는 방법으로는 Microsoft SQL Server Azure의 Federation model이나 RDBMS는 아니지만 NoSQL 중 MongoDB의 경우 Sharding을 DB단에서 지원합니다.

 

프레임워크단에서 자바의 Hibernate의 경우 Hibernate Shard라는 기능을 통해서 Sharding을 지원합니다. 프로그래밍 언어인 Grail의 경우에도 자체 프레임워크에서 "Grail Sharding Plug in"을 통한 Sharding을 지원합니다.

 

직접 Application에서 구현할 경우 Key에 따라서 DB Instance를 선택적으로 고를수 있는 구조를 가져야 하며, 특히 다른 Shard간의 데이터 Join 등은 불가능하기 때문에, 구현상에 이에 대한 고려가 필요합니다.

 

Sharding이라는 것이 데이터를 분산 저장함으로써 시스템의 전체 용량을 늘릴 수는 있지만 Application의 복잡도가 올라가고, 데이터 편중 방지 등 여러가지 요소를 고려한 후에 설계, 반영해야 합니다.