AWS RDS의 타임존 변경하기

[markdown]

최근까지 N모사의 클라우드 서비스를 사용하다가, 혼자서 서버를 관리해야하는 부담 때문에 AWS로 넘어왔다. 그러면서 기존에 구상해 두었던, 서버 아키텍쳐를 AWS로 다 옮기는 중에 있는데, 그중에 하나는 Mysql-cluster를 RDS로 옮기기로 한 결정이었다. 그래서 RDS 인스턴스를 만들어보고 테스트 중에 있는데, 아니 타임존이 UTC로 되어 있어서 시간이 내가 의도한대로 나오지 않는 것이었다!

이걸 고치는 법을 아마존에서 공식적으로 지원하지는 않는 것으로 보이고(오라클은 최근에 지원이 되기 시작했다!) 뭔가 다른 방법으로 해야될 것 같아서 찾아보니 DB프로시져를 이용하는 방법이 있었다.

간단히 단계를 살펴보자면 아래와 같다.

1. 스토어드 프로시져의 작성
2. Parameter Group의 작성
3. RDS에 Parameter Group을 적용
4. RDS 인스턴스 재시작

이 작업을 하면서 주의해야될게 하나 있는데 `rdsadmin` 이라는 유저는 AWS쪽에서 DB인스턴스 관리용으로 사용되는 녀석으로 타임존을 건드리면 이상하게 동작할 것 같으니 이녀석의 타임존은 안건드려야 한다는 것이다.

이제 스토어드 프로시져부터 만들어보자

### 1. 스토어드 프로시져 만들기

프로시져 명을 `mysql.store_time_zone`이라고 했는데, `mysql`에는 실제로 사용할 DB의 이름을 넣도록 하자. 귀찮으면 그냥 둬도 상관은 없다.

“`sql
DELIMITER |
CREATE PROCEDURE mysql.`store_time_zone`()
IF NOT (POSITION(‘rdsadmin@’ IN CURRENT_USER()) = 1) THEN
SET SESSION time_zone = ‘Asia/Seoul’;
END IF |

DELIMITER ;
“`

### 2. Parameter Group 만들기

RDS에서는 MySQL의 파라메터를 `Parameter Group`으로 그룹단위로 관리하고 있다. mysql의 세션이 최초 접속시에 위에서 만들어둔 프로시져를 실행하도록 `init_connect`파라메터를 설정하기 위한 `Parameter Group`을 만들자.

RDS의 Parameter Groups에 들어가서 `Create DB Parameter Group`을 클릭한다.

![그림1](http://gyus.me/wp-content/uploads/2014/10/1-1-1.png)

`Yes, Create`를 클릭!

![그림2](http://gyus.me/wp-content/uploads/2014/10/2-1-1.png)

store-time-zone을 선택후 `Edit Parameters`를 클릭하자.

엄청 많은 파라메터들이 있는데 `init_connect` 파라메터를 찾아서 아래의 `CALL` 명령어를 입력하자

“`sql
CALL mysql.store_time_zone
“`

![그림3](http://gyus.me/wp-content/uploads/2014/10/31-1-1.png)

설정이 끝났으면 위로 올라와서 `Save Changes`를 클릭해서 설정을 저장한다.

![그림4](http://gyus.me/wp-content/uploads/2014/10/4-1-1.png)

### 3. RDS에 파라메터 그룹을 설정하기

![그림5](http://gyus.me/wp-content/uploads/2014/10/5-1-1.png)

RDS instance메뉴로 가서 DB Instance를 선택한다음 마우스 오론쪽을 클릭해서 `Modify` 메뉴로 들어가자.

`Parameter Group`을 위에서 작성한 그룹으로 변경하고 `Apply Immediately`를 체크하고 변경하자.
![그림6](http://gyus.me/wp-content/uploads/2014/10/6-1-1.png)

### 4. DB인스턴스의 재시작

적용후 DB인스턴스를 재시작해주면 된다.

잘 적용 되었는지 DB에 접속해서 확인을 해보자. 아래와 같이 쿼리를 날리면 아마 바뀌어진 타임존으로 시간이 나올 것이다.

적용전/적용후를 알 수가 없지만, 이 글을 포스팅한 시간이 2014년 10월 10일 오후 5시 50분경이니 잘나오는거라고 보시면 된다. ^^;

“`sql
mysql > select now();
“`

![그림7](http://gyus.me/wp-content/uploads/2014/10/7-1-1.png)

### Tip 커맨드라인으로 변경하기
** ※ `AWS RDS API`는 사용가능 한 상태임을 가정한다. **

앞서 본것 처럼 웹콘솔에 들어가서 작업을 해도 되지만, API로 하는 방법도 있다.
단계는 동일한데 단순히 API를 쓴다는 것만 다르다.

– 프로시져를 만든다.
– 파라메터 그룹을 만든다.
– RDB인스턴스의 파라메터 그룹 설정을 변경한다.
– RDB인스턴스 재시작

`프로시져는 위에서 만들어 봤으므로 생략하겠다`

#### 파라메터 그룹 만들기

아래의 커맨드로 그룹을 쉽게 만들 수 있다.

“`shell
$ rds-create-db-parameter-group store-time-zone –db-parameter-group-family mysql5.6 –description “change time zone” –region “ap-northeast-1”
“`

#### 파라메터 그룹 설정 적용하기

아래의 커맨드로 만든 그룹에 대한 설정을 하고 적용을 바로 할 수 있다.

“`shell
$ rds-modify-db-parameter-group PARAMGROUP –parameters “name=init_connect, value=’CALL mysql.store_time_zone’, method=immediate”
“`

#### 재시작도 해보자

**※ rdb 인스턴스 명을 알고 있어야 한다.**

“`shell
$ rds-reboot-db-instance {rdb instance 명}
“`

[/markdown]

확장성 전략 입문: 데이터베이스 샤딩

http://blog.maxindelicato.com/2008/12/scalability-strategies-primer-database-sharding.html

위의 링크의 문서를 간략하게 정리한 자료입니다.

확장성 전략 입문서 : 데이터베이스 샤딩

데이터 베이스 확장성

기본 용어 정리

  • 성능 – 한 시스템에서 트랜젝션 하나를 서비스할 수 있게 하는 스피드
  • 확장성 – 전체 시스템의 부하증가시에도 서비스의 품질을 유지시키는 시스템의 기능
  • 수직 확장 – 시스템을 수직으로 확장하는 것은 각각의 컴포넌트의 자원을 늘려서 확장성을 증가시키는 것
  • 수평 확장 – 수평으로 확장하는 것은 각각의 컴포넌트들을 늘려서 확장성을 증가시키는 것
  • 수직 데이터셋 파티셔닝 – 데이터 세트(테이블)을 테이블의 컬럼 단위로 쪼개는것
  • 수평 데이터셋 파티셔닝 – 데이터 세트(테이블)을 테이블의 행단위로 쪼개는 것
  • 시스템 – 소프트웨어와 하드웨어가 상호 연결된 컴포넌트들의 집합
  • 샤드 – 데이터베이스의 인스턴스 한개. 전체 데이터 세트의 부분집합. 각각의 샤드에 있는 데이터는 유니크하지만, 같은 스키마를 공유한다.

당신의 데이터를 파티셔닝하라

왜 우리는 데이터를 파티셔닝 해야하는가? 그리고 그것은 우리 어플리케이션의 확장성에 어떻게 도움이 되나?

가능한한 최고 사양의 머신을 구해서 수직적으로 확장을 할 수는 있겠지만, 결국에는 벽에 부딪힌 자신을 발견할 것이다. 하드웨어를 수직적으로 확장하는 것은 한계가 있다.

알고리즘적 색인(Algorithmic Lookups)

날짜 시간 파티셔닝

파티셔닝 날짜와 시간으로 데이터를 파티셔닝 하는 가장 단순한 알고리즘적 방법

Row Count 파티셔닝

날짜와 시간 파티셔닝의 또다른 대안은 Row Count파티셔닝이다. Row Count파티셔닝은 날짜 시간 파티셔닝과 기본적인 개념을 같이한다.

“우리의 시스템에는 하나의 DB서버가 있다. 그 데이터베이스는 지금 사용량 초과이고 가끔 실행되는 오랫동안 실행되는 쿼리로 인해 장애가 발생직전의 상태이다. 데이터셋의 거의 가득 차오른 용량이 주된 이슈이다. 좀 더 상세히 말하자면, 거의 2천 5백만개의 송장이 5년이란 기간동안 더해졌다.”

위의 주어진 시나리오대로라면, 우리는 아래와 같이 말할 수 있다.

  • 낮은 볼륨의 쓰기/읽기
  • 리소스 집중적인 읽기 – 오래 실행되는 쿼리는 CPU와 Disk 리소스를 집중적으로 사용한다.
  • 큰 용량의 데이터 – 큰 볼륨의 데이터를 다루고 있다.
  • 데이터의 균등한 분배 – 데이터 셋이 크지만 5년동안 거의 1년에 500만개가 증가했다. 어느정도 증가할지는 장담할 수 없다.

더 이상 예측 가능한 데이터셋을 다루어야 한다고 고민할 필요는 없다. 역사적인 관례로 보면 – 장담할 수 있는건 아니지만 – 데이터셋은 1년에 500만개정도가 계속해서 증가했다. 보통의 서버는 하나의 테이블에 500만개의 읽기 처리를 별다른 노력없이 할 수 있다. 그러므로 우리는 간단히 2천5백만개의 레코드를 분리된 각각의 5개의 데이터 샤드로 재배치 할 수 있다.

몇년이 지난후, 우리는 또다른 샤드를 우리 시스템에 추가 할 수도 있다. 우리는 균등한 데이터의 분배에 대한 유지 관리를 걱정하지 않아도 된다.

새로운 데이터를 어디에 넣을지 결정하는 것은 간단하다. 간단히 우리의 어플리케이션에 설정을 할수 있고 우리의 샤드는 현재 활성상태의 샤드가 무엇인지 찾아낸다. 액티브 샤드가 최대 레코드의 수용치에 이르렀을 때에, 어플리케이션에는 간단히 준비된 다음 샤드에 데이터를 넣도록 설정을 추가하거나 변경하면 된다.

이제 유연성면에서는 약간 어려워지지만, 작년의 데이터가 어디있는지 결정하기 위해 럭셔리 해질 필요는 없는데, 일년전의 데이터를 쿼리할 경우에 모든 샤드를 검색하면 된다. 이건 생각보다 나쁘진 않다. 우리 시스템은 이미 병렬화(parallelized)되었기 때문이다. 이제 하나의 서버에서 찾고자 하는 데이터가 어디 있는지 찾는 대신에 각각의 서버에 찾고자 하는 쿼리를 날리고 결과들을 합치면 된다.

마스터 인덱스 색인

마스터 인덱스 색인을 사용하는 것은 알고리즘적 색인을 사용하는 것과 거의 비슷하게 간단하지만, 특정 데이터 셋이 연속적으로 정렬되어 있지 않은 경우에 필요하다.

도메인 파티셔닝

좀 더 복잡한 파티셔닝 방법중 도메인 파티셔닝은 최고의 유연성을 준다.

파티셔닝의 규모를 결정하는 것은 해당 데이터 베이스의 스키마의 성격에 강하게 의존한다.

특정 사이즈로 파티셔닝이 쉽도록 잘 정리된 스키마는 아래의 성질들이 있다.

  • 스키마들의 테이블이 상호 관계가 없이 자연적으로 원칙적으로 격리되어 있다. 예를 들면, 대부분의 테이블은 유저테이블과 관계가 있다. 그리고 포함된 열들은 특히 단순한 한명의 유저의 데이터이다.
  • 스키마의 가장 중요한 테이블은 많은 조인을 필요로 하지 않아야 한다. 예를 들면, 유저테이블을 파티셔닝하고자 할 때, 여러유저에 대한 쿼리가 각각의 다른 샤드에 대한 조인이 필요 없어야 한다.
  • 위성테이블의 수, 포함된 데이터, 충분히 작은 사이즈의 1단 계층의 테이블, 그리고 계속 작은 사이즈를 유지하는것, 그리고 성능의 병목지점이 되지 않는것.

도메인 파티셔닝은 두개의 메인 서버타입으로 구성된다. 하나의 인덱스 샤드 그리고 하나이상의 도메인 샤드.

인덱스 샤드는 검색 메커니즘을 서비스한다. 최소로, 데이터셋의 파티셔닝된 규모와 그 도메인 샤드의 위치를 포함한다. 처음에 인덱스 샤드에 쿼리를 날려서 어느 도메인 서버에 데이터가 있는 지 얻어오고 도메인 샤드에 대한 다른 컨넥션이 저장된 데이터를 요청한다.

인덱스샤드에 과부하가 걸리는 거 아니냐고 궁금해 할 수 있을 것 같은데, 약간의 과부하가 걸리긴 하지만, 전체시스템에 영향을 자주, 많이 줄 정도는 아니다. 인덱스 샤드의 데이터는 검색하는 데이터에 비해 작은 용량이기 때문에 데이터 전체를 메모리에 올리기도 한다.

“우리의 시스템에는 하나의 DB서버가 있다. 그 데이터베이스는 지금 사용량 초과이고 가끔 실행되는 오랫동안 실행되는 쿼리로 인해 장애가 발생직전의 상태이다. 데이터셋의 거의 가득 차오른 용량이 주된 이슈이다. 좀 더 자세히 말하면, 거의 500만개의 비선형적인 자료를 지난 2년동안 쌓아왔다. 각가의 유저는 user테이블, user_profile테이블, user_blog테이블, user_blog_engty테이블로 되어있다. user_profile 테이블의 각각의 열은 user테이블과 관련되어 있다. 각각의 user_blog테이블의 열은 user테이블과 관련되어 있다. 각각의 user_blog_entry테이블의 열은 user_blog의 열과 관련이 있다.”

위의 시나리오를 보고 파티셔닝 규모를 정해보자. 직접적이든 간접적이든 user라는 하난의 엔트리에 모든 데이터가 관련이 있는것을 알고 있다. 그리고 시스템이 매우 커지는 주된 요인이 유저가 추가되는 것이라는 것을 알고 있다. 유저는 유저테이블에 저장되는 것을 알고 있기 때문에 우리는 확신을 가지고 유저 테이블을 여러개의 샤드로 파티셔닝함으로써 부하를 분산시킬 수 있다고 말 할 수 있다.

** 인덱스 샤드 스키마 정의하기 **

우리가 파티셔닝 해야되는 건 user라는 건 알았는데, 각각의 user를 검색을 위해, 유저에 대한 유니크한 식별자가 필요하다. 이건 간단하게 userId 키 컬럼을 조합함으로써 만들 수 있다.userId키를 가지고 있는 것은 중요하지만, 검색에 매우 유용하지많은 않다. 대부분의 유저들은 username으로 검색을 하기때문에 그들의 username으로 인증하는 것은 중요하다. 유저의 인증에는 username과 password둘다 필요하다.

인덱스 샤드를 위한 명백하게 가장 필요한 데이터들은 아래의 컬럼이 필요하다.

  • shardId – 샤드를 위한 식별자
  • connectionString – 연결된 샤들에 대한 컨넥션 스트링
  • status – 샤드의 상태를 나타내는데 사용
  • createdDate – 데이터가 해당 샤드에 추가된 시간
  • userId – 유저에 대한 유일한 식별자
  • username
  • password

** 핫스팟과 샤드의 재조정 다루기 **

불가피하게도 확장성 전략에는 데이터가 하나의 서버에 몰리는 것에 의해 발생하는 성능 저하를 방지하기 위해 재분배가 필요하다.