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

2014년 6월 16일 월요일

Memcached 복제 (3)

KMC 설치


KMC (Kakao Mem Cached)를 빌드하기 위해서는 MySQL 5.6 빌드시에 필요한 기본 라이브러리는 모두 설치가 되어 있어야 한다. 대표적으로 libaio-devel과 ncurses-devel 그리고 cmake 등은 설치되어 있지 않은 경우가 많으니 꼭 확인해보도록 하자. (물론 빌드하면서 에러가 출력되면 그때 그때 하나씩 설치해도 무방하다.)

그리고 KMC에서 꼭 필요한 아래 Memcached client 관련 라이브러리도 다운로드해서 설치하도록 하자.

  • libmemcached-1.0.4-1.el5.remi.x86_64.rpm
  • libmemcached-devel-1.0.4-1.el5.remi.x86_64.rpm


패키지의 이름은 무관하나 버전은 가능하면 크게 벗어나지 않도록 하자.

이제 KMC를 빌드하면 되는데, cmake를 실행할때에는 아래와 같이 몇가지 옵션들이 추가로 필요하다.

# cd mysql-5.6.14_kmc
# mkdir Release
# cd Release
# cmake .. \
'-DBUILD_CONFIG=mysql_release' \
'-DCMAKE_INSTALL_PREFIX=/usr/local/mysql' \
'-DWITH_INNODB_MEMCACHED=ON' \
'-DENABLED_LOCAL_INFILE=OFF' \
'-DHAVE_QUERY_CACHE=OFF' \
'-DOPTIMIZER_TRACE=OFF' \
'-DENABLE_DEBUG_SYNC=OFF' \
'-DENABLED_PROFILING=OFF' \
'-DWITH_ARCHIVE_STORAGE_ENGINE=OFF' \
'-DWITH_EMBEDDED_SERVER=OFF' \
'-DCOMPILATION_COMMENT=kmc' \
'-DENABLE_DTRACE=OFF'

물론 위의 모든 옵션이 꼭 필요한 것은 아니지만, 성능이나 Memcached plugin의 바이너리 로깅 기능 등을 위해서 필요하므로 혹시 변경이 필요한 경우에는 신중히 고려해서 적용하도록 하자.

cmake가 끝나면, make && make install 명령으로 KMC를 설치하면 된다. MySQL 프로그램이 설치되는 디렉토리는 INSTALL_PREFIX로 명시했던 /usr/local/mysql로 설치될 것이다.

프로그램의 설치가 완료되면, 일반적인 MySQL 서버 설치와 동일하게 $MYSQL_HOME/scripts/mysql_install_db 스크립트를 이용해서 MySQL 기본 딕셔너리 테이블들을 생성후, MySQL 서버를 시작하면 된다.

Memcached 관련 스키마 생성

MySQL 서버가 정상적으로 시작되었다면,
우선 Memcached 플러그인을 작동시키기 위해서 필요한 기본 스키마들을 생성하도록 하자. MySQL Memcached 플러그인이 작동하기 위해서 필요한 초기 스키마들은 아래 파일이 미리 준비되어 있으므로, source 명령으로 실행해주기만 하면 된다.

mysql> source $MYSQL_HOME/share/innodb_memcached_config.sql
mysql> use innodb_memcache
mysql> show tables

아래와 같은 InnoDB 테이블들이 정상적으로 준비되었는지 확인해보자.


  • cache_policies
  • config_options
  • containers

테이블이 준비되었다면, 이제 KMC를 위한 스키마들을 생성하도록 하자.

mysql> USE innodb_memcache;
mysql> INSERT INTO `containers` VALUES ('default','kmc','kmc_template','k','v','f','c','e','PRIMARY');

mysql> CREATE DATABASE kmc;
mysql> USE kmc;

mysql> DROP TABLE IF EXISTS `kmc_template`;
mysql> CREATE TABLE `kmc_template` (
  `k` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '',
  `v` mediumblob,
  `f` int(11) NOT NULL DEFAULT '0',
  `c` bigint(20) unsigned NOT NULL DEFAULT '0',
  `e` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`k`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 STATS_PERSISTENT=0;

mysql> INSERT INTO `kmc_template` VALUES ('1','DO-NOT-REMOVE',0,0,0);

KMC를 위한 my.cnf 설정

이제 마지막으로 Memcached를 위한 설정들을 my.cnf 파일에 적용하고 Memcached 서비스가 가능토록 MySQL 서버를 기동해보도록 하자. (MySQL 서버가 기동중이라면 셧다운하도록 하자.)

...
innodb_data_file_path = ibdata1:64M:autoextend
...
innodb_log_file_size = 32M
innodb_log_files_in_group = 2
innodb_log_buffer_size = 16M

## Memcached -----------------------------------------------------------------------------
daemon_memcached_option = '-m 20480 -p 11211 -c 80000 -t 8 -s /tmp/memcached.sock'
innodb_api_enable_binlog = 1
innodb_api_trx_level=0 ## READ-UNCOMMITTED
innodb_api_bk_commit_interval=1
daemon_memcached_r_batch_size=1
daemon_memcached_w_batch_size=1
innodb_api_enable_mdl=OFF

## Replication:Binary log
server-id  = 1
## Replication query is always idempotent
slave_exec_mode = IDEMPOTENT
## Memcached connection string :: with socket file
kmc_connect_string = '--SOCKET="/tmp/memcached.sock" --BINARY-PROTOCOL --NOREPLY --TCP-NODELAY --TCP-KEEPALIVE'
binlog-checksum = NONE
sync_binlog = 0
master_info_repository = FILE
relay_log_info_repository = FILE
sync_master_info    = 0
sync_relay_log      = 0
sync_relay_log_info = 0
slave_checkpoint_group = 100000
slave_checkpoint_period = 1000
max_binlog_size = 100M
expire_logs_days=1
expire_max_log_files = 15
binlog-format = ROW

대 부분 기본적으로 설정되어 있는 옵션들도 있지만, 안정성과 성능을 위해서 설정되어 있는 것이므로, 큰 문제가 없다면 최소한 이 정도의 옵션은 my.cnf에 포함시키도록 하자.

daemon_memcached_option 옵션은 Memcached 쓰레드가 기동되면서 사용하는 옵션이므로 신중히 설정하도록 하자. 그리고 kmc_connect_string는 MySQL Slave의 SQL Thread가 바이너리 로그를 읽어서 Replay시에 접속하는 Memcached 서버의 정보이므로, 로컬 Memcached 서버의 접속 정보를 설정하면 된다. 또한 InnoDB는 거의 데이터 저장소로 사용되지 않으므로, innodb의 데이터 파일과 리두 로그의 크기는 최소화해서 설정해주는 것이 좋다.

이제 MySQL 서버를 기동하고, Memcached의 기능을 테스트해보자.
물론, KMC의 복제를 연결하는 방법과 복제 상태를 확인하는 방법은 기존 MySQL 서버와 동일하다.

KMC 테스트

MySQL 서버가 기동되면, 아래와 같이 간단히 Memcached의 기능을 테스트를 수행해볼 수 있다.

[root@matt001 ~]# telnet localhost 11211
Trying 127.0.0.1...
Connected to localhost.localdomain (127.0.0.1).
Escape character is '^]'.

set matt 0 0 9
Seong Uck
STORED

get matt
VALUE matt 0 9
Seong Uck
END

delete matt
DELETED

get matt
END

quit
Connection closed by foreign host.

만약 Memcached로 접속하는 socket file (/tmp/memcached.sock)이 정상적으로 작동하는지 확인하고자 한다면 아래와 같이 netcat 유틸리티를 이용하면 된다.

[root@matt001 ~]# nc -U /tmp/memcached.sock
set matt 0 0 9
SEONG UCK
STORED

get matt
VALUE matt 0 9
SEONG UCK
END

quit
[root@matt001 ~]#

물론 복제된 데이터를 확인하고자 한다면, 마스터에서 SET을 실행하고 슬레이브 Memcached로 접속해서 GET으로 데이터를 확인해보면 된다. 또한 바이너리 로그를 mysqlbinlog 유틸리티로 열어서 내용을 확인해보는 것이 좋은 방법일 것이다.



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를 설치하고, 사용하는 방법 그리고 추천하는 설정에 대해서 간단히 소개하도록 하겠다.