레이블이 sync인 게시물을 표시합니다. 모든 게시물 표시
레이블이 sync인 게시물을 표시합니다. 모든 게시물 표시

2014년 6월 15일 일요일

Memcached 복제 (2)

Memcached(Cache-only 모드) 복제 아키텍쳐

1편에서 언급했던 것처럼, MySQL Memcached 플러그인이 Caching과 InnoDB-only 모드로 작동하는 경우에는 Memcached를 통한 변경 내용이 InnoDB 스토리지 엔진에 적용되기 때문에 바이너리 로그에도 그 변경 내용이 기록된다. 즉 Caching과 InnoDB-only 모드에서는 자동적으로 복제 기능이 적용되는 것이다.

하지만 이 프로젝트의 목적은 InnoDB를 완전히 사용하지 않고 MySQL 서버를 Memcached 전용 서버로 사용하는 것이기 때문에, Cache-only 모드만 고려 대상인 것이다. 그런데 1편에서도 언급했듯이, 문제는 Cache-only 모드에서는 Memcached의 변경 내용이 MySQL 서버의 바이너리 로그에 기록되지 않는다는 것이다.

Cache-only 모드에서 바이너리 로그를 작동하도록 하는 것이 이 프로젝트의 목표인데, 이를 위해서는 아래 그림과 같이 마스터 MySQL의 바이너리 로그 기록과 슬레이브 MySQL의 릴레이 로그 재생(Replay) 기능을 수정했다.
(그림에서 파란색 선은 원래 MySQL 5.6이 가지고 있던 데이터의 흐름이며, 갈색 두꺼운 선이 수정된 기능을 의미한다.)



위의 그림에서 파란색 점선 화살표는, 이해를 돕기 위해서 MySQL Memcached의 Caching과 InnoDB-only 모드 작동 방식을 표시해둔 것이다.

그림에 표시된 번호별로 조금 더 상세히 살펴보도록 하자.
  1. InnoDB API에서는 Memcached를 통해서 유입된 변경 내용을 정해진 규칙(innodb_api_bk_commit_interval, daemon_memcached_r_batch_size, daemon_memcached_w_batch_size 시스템 변수)에 따라서 InnoDB 스토리지 엔진으로 전달하고, 바이너리 로그에 기록하도록 구현되어 있었다. 하지만 이 로직은 Cache-only 모드에는 해당되지 않는데, 이를 Cache-only 모드에서도 작동하도록 수정했다. Original MySQL에서는 Cache-only 모드에서는 InnoDB 테이블이 필요치 않지만, 이 기능을 위해서 Caching이나 InnoDB-only 모드와 같이 Memcached 플러그인을 위한 설정 테이블(innodb_memcache DB)과 컨테이너 테이블이 필요하다. 물론 데이터가 저장되지는 않지만, 껍데기 구조가 필요한 것이다.
  2. 슬레이브에서는 IO Thread에 의해서 만들어진 Relay log를 가져와서 다시 Memcached에 적용(SET/ADD/DELETE)해야 한다. 하지만 Original MySQL 서버에서는 Replication SQL Thread가 Memcached를 통하지 않고 즉시 InnoDB Storage Engine으로 데이터를 반영해버린다. 그래서 Replication SQL Thread가 Memcached Plugin으로 접근할 수 있는 방법이 필요한데, 이를 위해서 MySQL 서버에 libmemcached.so를 같이 빌드해서 Replication SQL Thread가 Memcached client로 작동할 수 있도록 했다.
  3. Memcached Client로 작동하는 Replication SQL Thread에서는 최대한 빠르게 Relay 로그의 내용을 Replay해야 하므로, TCP Socket 포트(11211)보다는 Unix socket을 사용하도록 했으며, Async 모드로 Memcached Plugin에 변경 내용을 적용하도록 수정했다.


그 이외의 개선 사항


  • MySQL Memcached Plugin이 TCP socket과 Unix domain socket을 동시에 지원하도록 개선
  • Memcached Plugin의 상태를 MySQL 서버의 상태 값(Global status variables)으로 모니터링할 수 있도록, 매 초단위로 Memcached의 상태 값을 MySQL 서버 상태 값으로 복사
    mysql> show global status like '%kmc%';
    +----------------------------------+--------------+
    | Variable_name                    | Value        |
    +----------------------------------+--------------+
    | Innodb_kmc_connection_structures | 2004         |
    | Innodb_kmc_curr_connections      | 2002         |
    | Innodb_kmc_curr_items            | 70638969     |
    | Innodb_kmc_pointer_size          | 64           |
    | Innodb_kmc_threads               | 8            |
    | Innodb_kmc_total_connections     | 5090         |
    | Innodb_kmc_total_items           | 778120599    |
    | Innodb_kmc_bytes                 | 18648687816  |
    | Innodb_kmc_bytes_read            | 460650586044 |
    | Innodb_kmc_bytes_written         | 497924594841 |
    | Innodb_kmc_cmd_get               | 7781206118   |
    | Innodb_kmc_cmd_set               | 778120599    |
    | Innodb_kmc_evictions             | 498675673    |
    | Innodb_kmc_get_hits              | 1043938514   |
    | Innodb_kmc_get_misses            | 6737267604   |
    | Innodb_kmc_limit_maxbytes        | 21474836480  |
    +----------------------------------+--------------+
  • Caching이나 Cache-only 모드와는 달리 InnoDB 잠금과 무관하게 작동함
    (하지만 여전히 InnoDB의 트랜잭션이나 일부 자원은 할당해서 사용함 --> 개선 예정)
  • MySQL 5.6.14의 메모리 릭과 Memcached plugin의 expiration time 설정 버그 보완
  • MySQL 서버의 각종 Disk sync 로직을 Async로 전환
  • expire_max_log_files 시스템 변수 추가
    바이너리 로그 파일의 개수를 이용한 자동 삭제 기능
  • 바이너리 로그 자동 삭제 기능의 이상 현상 모니터링용 상태 변수 Binlog_purge_failed 추가
    • 0 : 정상
    • 1 : ACTIVE 상태의 바이너리 로그를 삭제하려고 시도한 경우 설정
    • 2 : USE 상태의 바이너리 로그를 삭제하려고 시도한 경우 설정
  • kmc_connect_string 시스템 변수 추가
    Replication SQL Thread가 릴레이 로그 Replay시 접속할 Memcached 접속 정보 설정


사용 가능한 Memcached Operation

안타깝게도 KMC(Kakao Mem Cached, 복제 가능한 Memcached 서버)의 특성이나 구현 제약으로 인해서, 현재 버전에서는 일부 Memcached operation을 지원하지 않는다.

사용 가능
  • GET
  • SET
  • ADD
  • DELETE
  • REPLACE
사용 불가능
  • INCREMENT
  • DECREMENT
  • CAS

성능 고려 사항

일반적인 Original Memcached와 비교해서 GET 성능은 차이가 없다. 하지만 SET이나 ADD 그리고 DELETE와 같은 Operation은 Original Memcached와는 달리 KMC는 Memcached의 캐시에 기록후 바이너리 로그에 기록해야 한다. 물론 바이너리 로그 기록은 Sync mode로 작동하는 것은 아니지만 여전히 Original Memcached에 비해서 상당히 많은 자원을 사용하게 된다. 그래서 SET/ADD/DELETE Operation의 성능은 Original Memcached에 비해서 상당히 낮은 결과를 보일 것이다.

하지만, 일반적으로 Memcached의 특성상 SET/ADD/DELETE Operation의 수치는 그렇게 높지 않은 것이 일반적이다. 한번이라도 SET되면 그 이후부터는 SET operation이 줄어들고 GET operation이 늘어나기 때문이다. 만약 SET/ADD가  초당 10K 이상씩 실행되는 Memcached가 있다면, 이는 캐시 효율이 상당히 낮은 Memcached라고 볼 수 있다. 또한 이런 Memcached는 오히려 서비스의 처리 성능에 오버헤드만 유발하고 있다고 볼 수도 있다.

일반적인 Intel x86 장비에서 대략 초당 10K 정도 SET/ADD/DELETE operation을 한계로 고려하고 있는데, 이는 단일 서버의 한계가 아니라 복제 지연으로 인한 한계이다.

KMC 기타 제약 사항

  • Memcached 컨테이너 테이블의 이름은 "kmc_" 키워드로 시작해야 한다.
  • CAS와 INCR 그리고 DECR 명령 사용 불가
  • KMC 내부적으로 Memcached 프로토콜에서 flags 필드의 상위 2바이트를 사용함
    • Memcached의 flags중에서 하위 2바이트만 사용한다면 문제 없음

KMC 다운로드




** 마지막 편에서는 MySQL Memcached를 설치하고, 사용하는 방법 그리고 추천하는 설정에 대해서 간단히 소개하도록 하겠다.

2014년 6월 10일 화요일

Memcached 복제 (1)

개요

모든 응용 프로그램은 영구적으로 저장되어야 하는 데이터를 가지게 마련이며,
이런 데이터들은 MySQL 서버와 같은 RDBMS나 NoSQL류의 DBMS에 저장되고 있다.
하지만 웹 기반의 응용 프로그램에서는 매우 빈번하게 데이터를 DBMS로부터 가져와서 가공하고 그 결과를 클라이언트로 전송해야 하는 요건을 가지고 있는데,
이렇게 많은 요청들을 매번 DBMS에서 가져간다는 것은 상당히 부담스러운 작업중 하나이다.
MySQL과 같은 RDBMS에서는 레코드 하나를 가져가기 위해서도 내부적으로 많은 복잡한 과정들을 거쳐야 하며,
때로는 메모리에 상주되지 않은 데이터를 위해서 고 비용의 디스크 읽기를 거쳐야 할 수도 있다. 
이는 HBase나 Cassandra와 같은 NoSQL류의 데이터베이스에서도 마찬가지이다. 때로는 NoSQL류의 데이터베이스는 데이터 읽기 작업이 RDBMS보다 훨씬 고비용인 경우가 많다.

그래서 이렇게 자주 사용되는 데이터는 빠르게 접근할 수 있도록 Memcached나 Redis와 같은 메모리 기반의 캐시 솔루션을 도입해서 구현하는 것이 매우 일반적인 형태이다.
Memcached와 Redis 모두 많은 개발자들로부터 관심을 받고 있는 캐시 솔루션이지만, 여전히 이 솔루션들은 각자의 단점들을 가지고 있다.

(이 글은 Memcached와 Redis중 어떤 솔루션이 더 나은지를 비교하는 것이 목적이 아니므로, 관심 대상이 아닌 Redis에 대한 언급은 생략하도록 하겠다.)
각 단점중에서 Memcached가 가진 단점 중 대표적인 것은 Memcached의 데이터가 복제되지 않는다는 것이다. (물론 그 이외에도 많은 단점들이 있을 것이다.)
최근에는 많은 회사들이 Memcached 서버의 데이터 복제나 여러 Memcached 서버간의 데이터 동기화에 대한 요건을 가지고 있으며, 페이스북의 경우에는 MySQL 서버의 바이너리 로그를 분석해서 Memcached로 변경 내용을 Relay해주는 기능들을 구현해서 사용중이기도 하다.
카카오에서도 Memcached의 이런 단점을 보완하기 위한 방법을 고려하던중, Memcached 플러그인이 내장된 MySQL 5.6 버전이 릴리즈되었다.

MySQL 5.6  Memcached 플러그인

우선 본격적인 이야기에 앞서서, MySQL 5.6의 Memcached 플러그인에 대한 기본적인 내용을 조금 살펴보도록 하자.
MySQL 5.6에 내장된 Memcached 플러그인은 아래와 같이 3가지 캐시 모드로 작동할 수 있다.

  1. Innodb-only
  2. Caching
  3. Cache-only


애초에 MySQL Memcached 플러그인은 InnoDB의 NoSQL 인터페이스로 도입된 솔루션인 관계로, InnoDB 스토리지 엔진과 매우 밀접하게 연결되어 있다.

  • 첫 번째 Innodb-only 모드에서는 Memcached 플러그인이 자체적인 캐시 메모리 공간을 가지지 않고, InnoDB 스토리지 엔진에 Memcached 프로토콜을 이용해서 데이터를 저장하고 읽어가기 위한 방법을 제공하는 것이다.즉 Memcached 프로토콜을 이용해서 SET/DELETE 그리고 GET 오퍼레이션이 실행되면, MySQL Memcached 플러그인은 그 명령을 InnoDB 스토리지 엔진으로 그대로 전달해서 실제 내부적으로는 InnoDB 테이블의 데이터를 변경하거나 조회해서 클라이언트로 반환하게 되는 것이다.



  • 두 번째 Caching 모드에서는 InnoDB 버퍼 풀과는 별도로 MySQL Memcached 플러그인이 자체적인 캐시 메모리 공간을 가지는 방식이다. 이 모드에서는 데이터를 ADD/SET/DELETE 오퍼레이션이 수행되면, MySQL Memcached 플러그인의 캐시 메모리 공간에 데이터를 변경함과 동시에 변경 내용을 InnoDB 테이블에도 같이 적용하게 된다.물론 Memcached 플러그인에 적용된 변경 사항이 InnoDB 테이블에 즉시 적용될지 또는 모아서 배치 형태로 처리될지는 MySQL 서버의 옵션을 통해서 조절할 수 있다.그리고 GET 오퍼레이션이 수행되면, Memcached 플러그인의 캐시 메모리에 존재하는 데이터라면 Memcached 플러그인 레벨에서 처리가 완료되고 그렇지 않다면 Memcached 플러그인이 InnoDB 테이블을 조회해서 클라이언트로 반환하게 된다.이 캐시 모드는 마치 MySQL 서버와 Memcached 서버를 하나로 묶어둔 것처럼 작동하는 방식이다.



  • 세 번째 Cache-only 모드는 MySQL Memcached 플러그인이 주 역할을 수행하며 InnoDB 스토리지 엔진은 전혀 처리에 관여하지 않게 된다.물론 Memcached의 캐시 메모리 공간에만 데이터가 기록되기 때문에 MySQL 서버가 재시작되면 캐시 메모리의 데이터는 모두 사라지게 될 것이다.


첫 번째와 두 번째 캐시 모드에서는 모두 Memcached 플러그인을 통해서 최종적으로는 InnoDB 스토리지 엔진으로 데이터가 전달되기 때문에 Memcached 플러그인을 통해서 유입된 데이터라 하더라도 바이너리 로그에 기록되어서 슬레이브 MySQL 서버로 전달된다.
하지만 순수한 Memcached 서버와 같은 방식으로 작동하는 세 번째 모드는 Memcached를 통한 데이터 유입이 InnoDB 스토리지 엔진으로 전달되지 않기 때문에 바이너리 로그에 기록되지 않는다.
MySQL Memcached 플러그인이 제공하는 3가지 캐시 모드중에서 첫번째와 두번째는가 각각의 목적과 용도에 맞게 훌륭하게 사용될 수 있을 것으로 보지만, 사실 3번째 Cache-only 모드는 왠지 MySQL 서버에서 필요로 할 기능처럼은 보이지 않는다. 
그런데 여기에서 제일 쓸모가 없을 것으로 보이는 세 번째 캐시 모드에서 Memcached 서버의 데이터 복제에 대한 가능성을 찾을 수 있었다.

만약 MySQL Memcached 플러그인의 Cache-only 모드에서, Memcached 플러그인을 통해서 유입된 데이터가 InnoDB 스토리지 엔진으로는 전달되지 않지만 바이너리 로그에는 기록된다면 어떻게 될까 ?

이 기능이 보완된다면, MySQL 서버를 이용해서 Memcached 서버의 데이터 복제나 동기화가 구현되는 것이다.
결국 이는 MySQL 서버의 복제 기능을 Memcached 서버에 결합하는 효과를 얻을 수 있게 되는 것이다.

물론, 바이너리 로그 기록 하나만 수정해서 모든 것이 해결되는 것은 아닐 것이다. 언급하지 않은 작은 문제들과 제약 사항들이 도사리고 있을 것이다.
하지만 MySQL 서버의 안정적인 복제 방식을 Memcached 서버에 도입할 수 있게 된다면 더 없이 좋은 솔루션이 될 것이다. 그것도 많은 시간을 투자하지 않고서 ...

(2편에서 계속 ...)