RDB 스케일링

RDB 스케일링

RDB는 흔히 말하길 스케일링 (스케일 아웃) 하기 까다로운 데이터베이스라고들 한다. NoSQL이 등장하며 내세웠던 차별점 역시 이러한 부분(확장성)이 포함되어있다. 하지만 RDB가 스케일 아웃이 불가능하다는 건 절대 아니다. 많은 거대한 서비스들이 RDB를 사용하고 있고, 이 서비스들은 많은 방법으로 스케일 아웃을 구현하고 있다. 이 방법에 대해서 정리한 글이다.

우선 글 내용에서 구체적인 부분들은 InnoDB 스토리지 엔진의 케이스를 다루고 있다. 핵심적인 원리에 대해서는 사실 모든 RDB에서 같다고 생각된다.

거대한 테이블의 문제점

스케일 아웃이 필요한 근본적인 이유는 무엇일지 생각해보자. In-memory 데이터베이스가 아니라면 일반적으로 RDB는 정보의 영구적인 저장을 위해 디스크에 파일을 작성하게 된다. 서비스가 성장하면서 메모리 사이즈보다 데이터 용량이 커지면 OS 레벨에서 캐시해주는 범위를 초과하면서 Disk I/O가 급등하게 된다. 인덱스도 마찬가지이다. 인덱스 역시 파일로 관리되게 되는데, 이 인덱스 파일의 사이즈가 커지면 같은 이유로 Disk I/O가 많아지고 속도는 Memory 접근에 비해 백만 배까지 느려진다. CRUD를 할 때 직, 간접적으로 인덱스 파일을 사용하게 되는데, 모든 동작이 이렇게 느려진다.

해결할 수 있는 원리

근본적으로 해결하기 위해서는 테이블의 사이즈를 줄여줘야 한다. 이 방법으로는 두 가지를 여기서 언급하는데, 첫 번째는 일반적으로 RDB에서 제공하는 파티셔닝과 엔지니어가 직접 테이블을 분리하는 샤딩에 대해 다룬다. 간단히 말해서 파티셔닝은 하나의 RDB 안에서 테이블 하나를 내부적으로 여러 테이블로 나눠주는 것이고, 샤딩은 여러 RDB 서버를 사용해 데이터를 분할하는 방식이다.

파티셔닝 (Partitioning)

파티셔닝은 논리적으로는 하나의 테이블인데, 내부에서는 물리적으로 여러 테이블로 나눠 관리하는 방법이다. RDB마다 다를 수 있지만 일반적으로는 PARTITION 키워드를 통해 테이블을 분할할 수 있다. 사용하는 데이터들의 인덱스를 여러 개로 분할해서 사용할 수 있게 된다. 따라서 이전에 발생한 문제를 해결할 수도 있고, 데이터를 목정성에 맞게 나눠 관리하다가 요구에 따라 간단하게 삭제할 수도 있다.

흔히 이 파티셔닝을 “스케일 아웃”이라고 표현하지 않는다. 일종의 기술로 대량의 데이터를 특정 기준별로 데이터베이스에 부하가 적게 생기면서 삭제할 수 있도록 하는 목적이 더 크다. 그렇지만 근본적으로 큰 테이블을 여러 테이블로 나눠주는 과정이 포함되어있어서 큰 테이블에서 발생할 수 있는 문제를 해결해줄 수 있다.

파티션 키

파티션을 만들 때, 특정 데이터가 어디에 위치하게 될지를 결정하는 키를 파티션 키라고 한다. CRUD를 할 때, 이 키를 활용해(활용할 수 있는 상황이라면) 파티션을 선택한다. 그다음 명령 동작을 수행하는 구조이다. 한 단계를 거치지만 거대한 테이블을 모두 찾아보지 않아도 된다. 이렇게 불필요한 다른 서브 테이블을 배제하는 동작을 프루닝이라고 한다.


조금 구체적인 얘기인데, 파티션 키를 선택할 때 제한사항이 존재한다. 유니크 키는 논리적인 테이블 안에서 유일해야 하는 값이기 때문에 파티션 키를 통해 해당 유니크 키가 어디에 있는지 결정할 수 있어야 한다. 따라서 파티션 키는 유니크 인덱스의 일부 또는 전체를 사용해 표현해야 한다.

예를 들어서 유니크 키가 다음과 같이 설정되어있다고 생각해보자.

1
PRIMARY (fd1, fd2) -- PRIMARY도 유니크 키

파티션 키는 fd1을 사용하거나, fd2를 쓰거나, 둘 다 사용해야 한다. 그래야만 파티션 키가 유니크 값들이 무조건 같은 테이블에 있음을 확인해줄 수 있다.

파티션에서 쿼리가 발생하는 과정

우선 먼저 파티션을 구분할 수 있는 조건절이 사용되었는지 확인하고 파티션 프루닝을 시도한다. 위에서 살짝 언급했지만, 파티션 프루닝은 찾을 필요가 없는 파티션을 걸러 내는 과정이다. 그다음 일반적인 테이블을 스캔하는 과정이 발생한다.

일반적인 테이블을 스캔하는 과정은 조건절에 인덱스가 포함된 경우 인덱스를 통해 쿼리를 하고, 그렇지 않으면 테이블 풀 스캔을 하는 과정을 말한다.

따라서 쿼리를 하는 방법에서도 어떤 키를 기준으로 파티셔닝을 해야 할지 신중하게 결정해야 한다. 쿼리 패턴에 맞게 파티셔닝을 해야 파티셔닝을 한 효과를 최대화할 수 있다.

파티션에서 업데이트가 발생하는 과정

업데이트라고 썼지만 실제로 파티셔닝이 된 데이터베이스에서는 읽기, 삭제, 삽입이 포함될 수 있는 과정이다. 업데이트 동작을 수행하기 위해서는 먼저 테이블에서 해당 데이터를 찾아야 한다. 이 과정에서 위에서 말한 쿼리 과정이 수행된다. 그다음 데이터를 수정하게 되는데, 만약 업데이트한 필드가 파티션 키와 상관없는 필드인 경우엔 값만 수정하고 끝난다. 그런데 만약 파티션 키를 수정하게 되면 해당 데이터를 재배치하는 과정이 필요하다. 즉, 데이터를 삭제 후 알맞은 파티션에 삽입하는 과정이 발생한다. 이런 동작을 하므로 파티션 키는 쉽게 변하지 않는 값으로 설정하는 것이 퍼포먼스 측면에서 좋다.

파티션 프루닝

지금까지 이 글을 따라오다 보면 파티션 프루닝을 몇 차례 만날 수 있다. 파티션 프루닝은 파티셔닝의 핵심이다. 이 작업은 EXPLAIN 명령으로 확인할 수 있다.


해시 파티셔닝을 한 다음 쿼리를 EXPLAIN으로 확인한 모습. p0 파티션만 사용되고 나머지는 사용되지 않음

테이블을 분리해서 인덱스의 크기를 줄이는 것이 파티셔닝의 외적으로 드러나는 장점이지만, 사실 프루닝을 잘 할 수 있도록 쿼리를 하지 않으면 오히려 안 좋은 퍼포먼스를 발생시킨다. 따라서 무턱대고 파티션을 많이 만들어서 인덱스 사이즈를 줄이기보단 파티션 프루닝이 최적으로 발생하도록 만들고, 인덱스 서치를 한 번만 발생하도록 하는 것이 더 중요한 파티셔닝 전략이다.

방법

이 글에서는 파티셔닝 방법 4가지를 설명한다. 구체적인 내용에 대해서는 MySQL, 특히 InnoDB 스토리지 엔진을 기준으로 설명하고 있다.

Range

범위를 기반으로 데이터를 나누기 쉬운 경우 사용할 수 있는 방법이다. 로그 데이터를 예로 들어볼 수 있다. 데이터가 시간에 따라 쌓이기 때문에 필요에 따라 월 단위나 연 단위로 테이블을 나눌 수 있다.

1
2
3
4
5
6
7
8
9
10
11
CREATE TABLE example_logs (
...
reg_date DATETIME NOT NULL,
PRIMARY KEY (id, reg_date)
) PARTITION BY RANGE (YEAR(reg_date)) (
PARTITION p2017 VALUES LESS THAN (2018),
PARTITION p2018 VALUES LESS THAN (2019),
PARTITION p2019 VALUES LESS THAN (2020),
PARTITION p2020 VALUES LESS THAN (2021),
PARTITION p9999 VALUES LESS THAN MAXVALUE
);

위 SQL을 보면 파티션 키로 내부 함수가 사용된 것을 볼 수 있다. 모든 내장 함수가 가능한 것은 아니고, InnoDB인 경우에는 이 링크에 있는 내장함수들이 가능하다. 그리고 범위 마지막 부분은 MAXVALUE 키워드가 사용된 것을 확인할 수 있다. 위 SQL대로면 p9999 파티션에 2021년도 이후 로그가 쌓이고 있다고 보면 된다. 이 때 2021년도 이후 파티션을 구성하려고 하면 단순히 ADD PARTITION 키워드로는 동작하지 않는다.

1
2
3
ALTER TABLE example_logs ADD PARTITION (
PARTITION p2021 VALUES LESS THAN (2022)
); -- ERROR

맨 처음 CREATE TABLE을 한 SQL에서 알 수 있듯, 파티션으로 나눠질 때 위에서부터 차례대로 파티션 위치를 판단해 나누는 것을 알 수 있는데, ADD PARTITION을 하게 되면 이미 만들어진 파티션들 뒤에 파티션을 추가하기 때문에 마지막에 MAXVALUE를 사용하지 않는 상황이 된다. 따라서 파티션에 범위를 추가하기 위해서는 REORGANIZE를 사용해야 한다.

1
2
3
4
ALTER TABLE example_logs REORGANIZE PARTITION p9999 INTO (
PARTITION p2021 VALUES LESS THAN (2022),
PARTITION p9999 VALUES LESS THAN MAXVALUE
);

다만 REORGANIZE 작업은 기본적으로 그 전 파티션을 복사하는 작업이다. 따라서 데이터가 많은 경우 오래 걸릴 수도 있다. 이런 문제를 해결하기 위한 일반적인 패턴 중 하나로 MAXVALUE 키워드를 쓰지 않고, 미래에 사용될 범위의 파티션을 미리 만들어두는 방법이 있다. 이렇게 하면 ADD PARTITION을 통해 간단하게 범위를 늘릴 수 있다. 당연히 문제가 발생할 여지가 있다. 이 작업이 모종의 이유로 생략되거나 문제가 생겨 생성되지 못한 상태로 해당 테이블을 사용하게 되면 파티션에 들어가야 할 데이터의 INSERT 작업이 동작하지 않는다.


이렇게 만들어진 파티션은 간단하게 드랍할 수 있다.

1
ALTER TABLE example_logs DROP PARTITION p2017;

위 코드로 2017년 로그를 삭제할 수 있다. 조건절을 통해 삭제하는 것보다 데이터베이스에 생기는 부하도 적고 빠르게 데이터를 삭제할 수 있다.

List

리스트 방식은 파티션 키가 어떤 케이스에 속하는지 직접 지정해주는 방법이다. IN (...) 안에 파티션으로 선택되는 리스트를 만들어주어야 한다.

1
2
3
4
5
6
7
8
9
10
CREATE TABLE posts (
id INT NOT NULL,
title VARCHAR(50),
...
category_id INT NOT NULL
) PARTITION BY LIST(category_id) (
PARTITION fleamarket VALUES IN (1),
PARTITION town VALUES IN (2),
PARTITION etc VALUES IN (3, NULL)
);

당연히 리스트 안의 값은 겹치면 안 되고, 만약 겹치게 되면 에러를 발생시킨다. 파티션 키의 값이 지정된 코드나 값일 때 사용할 수 있다. 위 예시에서는 포스트의 카테고리에 따라 테이블을 나눠 구성한 모습이다. 또한 키 값이 오름차순이나 내림차순의 의미가 없는 경우라면 Range를 사용할 수 없으므로 List 방법이 적합한지 생각해볼 수 있다.

위에서 파티션을 추가하는 방법처럼 ADD 키워드를 통해 파티션을 추가할 수 있고, DROP PARTITION을 통해 파티션을 지울 수 있다. 또 하나의 파티션을 분리 및 병합할 때는 REORGANIZE PARTITION을 사용할 수 있다.

List, Range 파티션의 경우 Subpartition (Composite Partition)을 구성할 수 있다.

Hash

해시 함수에 의해 파티션을 결정할 수 있다.

1
2
3
4
5
6
CREATE TABLE accounts (
...
) PARTITION BY HASH(id) PARTITIONS 4 (
PARTITION p0,
...
);

PARTITIONS 4는 4개의 파티션에 의해 분할되는 것을 의미한다. 해시 작업이라고 하는 것은 쉽게 말해서 모듈러 연산하는 작업이다. 따라서 파티션 키로 사용되는 값은 정수값을 반환해줘야 한다. 파티션 이름을 지정하려면 위에서처럼 직접 이름을 정해줄 수도 있는데, 만약 정의하지 않으면 p0, p1, … 이런 식으로 지정된다.

데이터를 균일하게 파티션에 분배되어야 잘 파티셔닝 된 것이라 볼 수 있는데, 해시의 경우 아주 균일하게 파티션을 분배한 것이라 볼 수 있다. 자원을 효율적으로 사용할 수 있지만, 데이터 목적이나 유형을 고려해서 파티션을 나눈 것은 아니다. 따라서 모든 데이터에 대해 용도가 비슷하고 사용 빈도도 비슷한 큰 데이터를 파티셔닝 해야할 때 사용하기 좋다. 예를 들어서 계정 정보는 오래 전에 가입했든 최근에 가입했든 지속해서 사용하는 사람들의 정보가 계속해서 사용된다.

하지만 이 방법으로 파티션을 분할하면 파티션을 구성을 변경하는 과정의 비용이 많이 들거나 불가능하다. 예를 들어서 하나의 파티션을 더 넣는다는 것은 해시 함수를 바꿔주는 것과 같은 의미이다. 따라서 바뀐 해시 함수로 기존 데이터를 모두 재배열 해줘야 한다. 또한 해시로 나눠진 파티션을 삭제할 일도 사실상 없다. 해시 파티션을 했을 때, 각 파티션에 어떤 데이터가 있는지에 대한 의미가 없기 떄문이다. 따라서 특정 파티션을 삭제할 이유가 없고, 실제로 삭제한려고 한다 하더라도 DROP PARTITION은 에러를 발생시킨다. 그리고 병합하거나 분할하는 작업도 불가능하다. 이 과정은 그냥 파티션을 늘려주거나 줄여주는 작업을 해야 한다. 예상할 수 있겠지만 파티션을 늘이고 줄이는 작업은 아주 비싼 작업이다.

COALESCE PARTITION 1과 같은 방법으로 파티션을 줄여줄 수 있다.

1
ALTER TABLE example COALESCE PARTITIONS 1;

파티셔닝 이후의 유연성이 부족한 방법이기 때문에, 설계할 때 몇 개의 파티션이 적합할지 생각해보는 것이 중요하다. 하지만 해시로 사용하는 컬럼값이 조건절에 사용되면 아주 효율적으로 파티션 프루닝이 가능하다.

유연성이 부족한 해시 파티션 문제를 해결하기 위해서 Linear Hash를 사용할 수 있다. 그러나 사용되는 특정한 알고리즘으로 인해 데이터의 분배가 덜 균등해질 수 있다.

Key

키 파티션 방법은 해시와 거의 비슷한데 해시 함수의 모듈러를 위해 정수형 타입을 사용해야 했던 해시와는 다르게, 대부분의 타입을 파티션 키로 사용할 수 있다. 파티션 키를 MD5를 통해 해시값을 계산하고 그 값을 모듈러 연산해서 파티셔닝을 해주는 구조이다.

1
2
3
4
5
6
7
8
CREATE TABLE k1 (
id INT NOT NULL PRIMARY KEY,
name VARCHAR(20)
)
PARTITION BY KEY() -- 괄호가 비어있으면, 프라이머리 키 모든 칼럼을 사용함
PARTITIONS 2;

-- 프라이머리 키가 따로 없으면 유니크 키를 사용

해시 파티션에 비해서 더 균등하게 나눠질 수 있다. 따라서 보다 효율적이고, 파티션 키로 사용되는 필드가 정수형이 아니어도 되기 때문에 Hash 방법이 사용될 수 없는 상황에서 고려해볼 수 있다.

파티셔닝 정리

파티션 키와 파티셔닝 방법을 선택하는 기준은 데이터 접근 패턴, 어떤 유형의 데이터인지, 등 고려해볼 만한 상황이 많이 있다. 그리고 단순히 인덱스를 작게 만드는 것보다 효율적인 DML을 쓰기 위해 어떻게 설계하는 것이 좋을지도 고민해봐야 한다.

샤딩 (Sharding)

단일 서버에서 효율적으로 테이블을 나눈다고 하더라도 물리적인 한계는 반드시 존재한다. 예를 들어서 데이터베이스의 디스크 크기를 늘리기 어렵다든지, 늘릴 수 있더라도 근본적인 문제 해결 방법이 아니라든지(Network 부하, 꾸준하고 급격하게 증가하는 데이터들, 서버 자체의 부하), 이러한 이유로 결국 서버를 물리적으로 여러 대를 사용해서 해결해야 한다. 이렇게 수평적인 방식으로 파티셔닝을 하는 것을 샤딩이라고 한다.

위에서 언급한 것처럼 파티셔닝은 스케일 아웃이라고 보기 어렵다. 스케일 아웃은 결국 “수평적 확장”을 의미한다. 수평적 파티셔닝 방법이 일반적인 데이터베이스의 스케일링 방법이다. 그런데 “수평적 방식의 파티셔닝”이라는 말처럼 방법 측면에서 파티셔닝의 방법과 유사하다.


샤딩이라고 하는 것은 RDB의 기본적인 기능은 아니다. 즉, 개발자의 엔지니어링이 요구되는 부분이다. 어떤 데이터가 어떤 노드에 들어가 있는지 판단하는 것을 개발자가 엔지니어링을 통해 라우팅 처리 해줘야 한다. 이렇게 데이터를 저장하거나 가져올 때 적절한 노드를 찾아주는 흐름을 샤딩 로직 이라고 하는데, 이 로직은 애플리케이션 사이드에 존재할 수도 있고 스토리지 시스템의 미들웨어로 존재할 수도 있다. 애플리케이션 사이드에 이 로직이 있는 것을 Application-level 샤딩이라는 이름으로 보통 불린다. 이는 애플리케이션의 로직에서 라우팅 처리를 해준다는 것을 의미한다. ORM에서 이를 설정한다든지, 직접 데이터값에 따라 어떤 데이터베이스와 연결할지 선택하는 흐름 등을 예로 들 수 있다. 반대로 스토리지 시스템 미들웨어는 솔루션, 샤딩 플랫폼, 프록시 등 여러 이름으로 불리고 있다. 이 방법을 사용하면 어떤 미들웨어인지에 따라 다를 수 있지만, 일반적으로 애플리케이션에서는 데이터베이스의 라우팅 처리에 신경쓰지 않고 마치 하나의 데이터베이스와 상호작용하는 것처럼 동작하게 된다.


샤딩을 하게되면 그 전처럼 RDB를 사용하지 못 할 수 있다. 여러 RDB의 특징들에 제약이 생긴다는 것을 의미한다. 대표적으로 다음과 같은 문제들이 있다.

  • 물리적으로 다른 노드의 데이터베이스와 JOIN 연산을 수행할 수 없는 문제
  • Auto Increment가 샤드별로 달라지는 문제
  • 하나의 트랜잭션이 두 개 이상의 샤드에 접근할 수 없는 문제

이런 문제가 발생할 수 있으므로 샤딩을 설계하는 과정에서 이 문제들을 고려해서 샤딩을 진행해야 한다.

샤드 키

파티셔닝에서도 각 파티션이 어떤 데이터를 가져갈지 결정할 파티션 키가 있었던 것처럼, 분할된 노드(분리된 데이터베이스 서버)는 각각이 가져갈 데이터를 결정해야 한다. 이 기준을 샤드 키라고 부른다. 위에서 파티션 키가 변하지 않는 값으로 설정하는 것이 좋다고 했던 것과 같은 이유로 샤드 키는 변경되지 않는 값을 기준으로 설정해야 한다.

방법

샤드 키를 어떻게 설계했는지에 따라 어떻게 라우팅할지도 달라진다. 여러 방법이 있지만 여러 곳에서 소개되고 있는 두 가지 방법을 가져왔다.

Range Based Sharding


출처: 우아한 형제들 기술 블로그

특정 키의 범위에 따라 샤드에 배분해주는 방법이다. 이름에서 알 수 있듯, 위 파티셔닝 방법 중 Range 방식과 유사하다. 특징은 샤드를 추가하는 과정이 비교적 간단하고 라우팅도 간단하다. 다만 데이터 접근 패턴이나 범위별 데이터양을 고려한 방법은 아니기 때문에 컴퓨팅 자원을 불균형하게 소비하는 케이스가 발생할 수도 있다.

Modulus (Key, Hash Based) Sharding


출처: 우아한 형제들 기술 블로그

특정 키를 모듈러 연산으로 특정하는 방식이다. 이 방법의 다른 이름에서 알 수 있듯, 위에서 파티셔닝 방법 중 Hash 방식과 유사하다는 점을 알 수 있다. 특징도 Hash 방식의 특징과 유사하다. 데이터가 샤드에 균일하게 분산된다는 장점이 있지만, 샤드를 추가할 때 데이터를 재배열해야 하는 비용이 있다는 단점이 있다.


케이스 스터디

샤딩같은 경우는 아무래도 RDB의 자체적인 기능이 아니라서 케이스를 찾아보면 굉장히 다양한 방법으로 샤딩을 진행한 것을 볼 수 있다. 예시로 세 가지 케이스를 확인해보자.

Notion 샤딩

노션은 애플리케이션 레벨의 샤딩을 결정했다. Vitess, Citus와 같은 미들웨어 서비스를 알아보긴 했는데, 그 솔루션들의 동작이 불투명하다고 판단했고, 본인들 데이터를 직접 컨트롤하길 원했기 때문에 이러한 선택을 했다고 한다. 그 결정 이후에는 어떻게 데이터를 나눌지 결정하는 과정이 있었다.

  1. 어떤 데이터를 샤드해야 할까? → 노션의 블록과 FK로 연관된 모든 데이터를 같이 묶어 하나의 샤드에 포함될 수 있도록 함으로써 데이터 부정확성 문제를 방지
  2. 어떻게 데이터를 나눠야 할까? → 워크스페이스의 ID를 기준으로 샤딩 함으로써 한 워크스페이스의 데이터들이 같은 데이터베이스 안에 들어갈 수 있도록 분할

더 자세한 내용은 이 링크에서 확인할 수 있다.

LINE

LINE Manga 서비스의 서버 엔지니어링 스토리를 보면 애플리케이션 샤딩을 한 것으로 추측된다. 몇 단계를 거쳐 샤딩을 진행했는데, 4단계에서 코드를 수정해서 샤딩을 적용한다는 얘기가 나온다. 라인 망가의 경우에는 RDB 부하 문제를 초기엔 스케일 업으로 대응했으나, 근본적인 해결책이 아니라고 판단하여 샤딩을 진행했다고 한다. 검토 과정은 생략되었는데, 새로운 컬럼을 구성하고 Range Based Sharding을 진행했다고 한다.

NHN

NHN의 경우 게임 서버 얘기였는데, 여러 서버로 샤딩을 한 상태에서 애플리케이션 레벨의 샤딩을 했을 때 컨넥션과 관련한 문제가 생길 수 있었다고 한다. 애플리케이션 서버가 200대, DB에 컨넥션이 서버당 300개씩 각자 거는 상황이면, 하나의 DB마다 최대 6만 개의 컨넥션이 생기게 된다. 이 문제를 해결하기 위해 미들웨어를 두고 프록시를 사용하고 있다고 한다. 여기서 사용된 미들웨어는 ProxySQL이고, 이 솔루션을 사용하게 되면 애플리케이션 레벨에서는 하나의 데이터베이스와 통신하는 것과 같이 구성할 수 있었다고 한다. 동시 접속의 개념이 있는 게임 서버에서 컨넥션을 Demultiplexing 하는 과정에 대해 더 자세히 알아보고 싶다면 이 영상을 보자.

마치며

사실 애플리케이션의 사용자가 증가하면서 부하를 견디는 설계를 할 때 애플리케이션이 데이터베이스와 접근할 때뿐만 아니라 고려해야 할 사항이 많이 있다. 데이터베이스의 부하를 분산하는 방법으로 미들웨어를 사용한 샤딩을 결정했다면, 이 미들웨어가 애플리케이션으로부터 오는 부하를 강하게 견디는지도 확인해봐야 한다. 이런 전반적인 이야기는 Database HA (High Availability)라는 키워드로 몇 가지 더 알아봐야 한다. 그리고 Replication과 관련된 얘기도 이 글에서는 빠져있는데, 데이터베이스를 복제해 읽기 전용 슬레이브 레플리카를 만들거나, 데이터 분석용, 백업용, 등 여러 이유로 사용할 수도 있다.

Author

changhoi

Posted on

2022-02-09

Updated on

2022-02-09

Licensed under

댓글

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×