memcached
memcached 는 자주 쓰는 값을 메모리에 담아 두는 서버입니다. 키 하나에 값 하나를 맡기고 같은 키로 다시 꺼냅니다. 데이터베이스에 같은 것을 다시 묻지 않으려고 그 앞에 세웁니다.
상세
공식 사이트는 memcached 를 고성능 분산 메모리 객체 캐싱 시스템이라고 적습니다. 성격은 범용이라고 밝히면서도, 원래 겨냥한 자리는 데이터베이스 부하를 덜어 동적 웹 애플리케이션의 속도를 올리는 것이었다고 덧붙입니다. 캐싱을 맡는 물건이라 앞에는 애플리케이션이 있고 뒤에는 데이터베이스가 있습니다.
서버를 고르는 클라이언트
여러 대를 띄워도 서버끼리는 연결되지 않습니다. 공식 튜토리얼은 memcached 세 대의 주소와 포트 번호를 클라이언트 쪽 배열에 적어 넣는 장면을 보여 줍니다. 그리고 그 memcached 들은 아무와도 말하고 있지 않으며 데이터도 갖고 있지 않다고 적습니다. 어느 서버에 무엇이 들어 있는지를 아는 쪽은 클라이언트입니다.
flowchart TD
C["클라이언트"] -->|키로 고른다| S1["memcached 1"]
C -->|키로 고른다| S2["memcached 2"]
C -->|키로 고른다| S3["memcached 3"]
S1 -.->|말하지 않는다| S2
S2 -.->|말하지 않는다| S3
같은 튜토리얼에서 사람이 세 대에 차례로 접속해 같은 키를 물어봅니다. 첫째와 둘째는 답하지 않고 셋째만 값을 뱉습니다. 몇 번을 되풀이해도 키 하나는 memcached 한 대에서만 발견됩니다. 클라이언트가 키를 보고 서버를 고르기 때문입니다.
한 덩이로 보이는 메모리
공식 사이트는 이 배치를 두고 모든 서버가 같은 가상 메모리 풀을 들여다보는 것이라고 적습니다. 그래서 어떤 아이템은 웹 클러스터 전체에서 언제나 같은 자리에 저장되고 언제나 같은 자리에서 꺼내집니다.
같은 문서가 수치로 견줍니다. 웹 서버 쉰 대가 저마다 64메가바이트씩 들고 각자 캐시를 쓰면 쓸 수 있는 캐시 크기는 그대로 64메가바이트입니다. 같은 쉰 대를 하나의 풀로 묶으면 3.2기가바이트가 됩니다. 웹 서버의 메모리를 캐시로 써야 하는 것은 아니라는 단서도 붙습니다. memcached 사용자 가운데 상당수는 memcached 서버 노릇만 하도록 만든 전용 장비를 둔다고 적습니다.
포기한 것
디스크
저장소 README 는 memcached 가 네트워크 입출력은 논블로킹으로 하지만 디스크는 하지 않는다고 적습니다. 괄호를 열어 덧붙인 말이 더 셉니다. 디스크로 가는 일은 절대 없어야 하며, 가는 순간 이 물건의 요점을 통째로 잃은 것이라고 스스로 적습니다.
디스크를 안 쓰기로 하고 얻은 것은 요청 경로에 디스크가 없다는 것입니다. 대신 담아 둔 것은 프로세스 바깥에 남지 않습니다. 공식 튜토리얼은 memcached 한 대를 내렸다가 다시 올린 장면을 적습니다. 다시 올리고 몇 분이 지나서야 데이터베이스 부하가 원래 자리로 내려갔습니다. 캐시가 비어 있는 상태에서 다시 채워진 것입니다.
값 안을 들여다보는 일
값은 서버에게 해석되지 않는 바이트 덩어리입니다. 프로토콜 명세는 비정형 데이터에 나타날 수 있는 문자에 아무 제한이 없다고 적습니다. 다만 그 데이터를 읽는 쪽은 앞선 텍스트 줄에서 전송되는 데이터 블록의 정확한 길이를 언제나 알게 된다고 못 박습니다. 같은 절이 서버는 비정형 데이터의 바이트 순서 문제를 신경 쓰지 않으며 그런 것이 있다는 것도 알지 못한다고 적습니다.
자료형을 두지 않고 얻은 것은 서버가 값의 형식에 대해 아무 약속도 하지 않아도 된다는 것입니다. 서버가 아는 것은 길이 하나입니다.
키와 아이템 크기의 상한
프로토콜 명세는 키 길이 상한이 현재 250자로 잡혀 있다고 적습니다. 물론 보통 클라이언트라면 그렇게 긴 키를 쓸 일이 없을 것이라는 단서가 괄호로 붙습니다. 키에는 제어문자나 공백이 들어가면 안 됩니다.
아이템 크기 쪽은 man page 가 값으로 적습니다. -I, --max-item-size 의 기본값은 1m 이고 최소는
1k, 최대는 1G 입니다. 담을 수 있는 것의 크기가 설정값 하나로 박혀 있는 셈입니다.
복제와 장애 승격
복제본이 없고 승격할 대기 서버도 없습니다. 공식 튜토리얼은 memcached 한 대를 올려 둔 장비가 낡아서 업그레이드하려고 통째로 내리는 장면을 적습니다. 내리자 데이터베이스 부하가 1에서 2로 올랐습니다. 튜토리얼의 인물은 그래도 아직 견딜 만하며 나머지 memcached 들은 여전히 트래픽을 받고 있다고 말합니다. 다시 켜자 몇 분 뒤 부하가 1로 돌아왔습니다.
서버끼리 상태를 맞출 일을 없앤 대가가 이 자리에 있습니다. 한 대가 빠지면 그 대가 들고 있던 키는 전부 캐시 미스가 됩니다. 튜토리얼은 그것을 두고 쓸 수 없으면 요청 몇 개가 미스가 될 뿐이며 나를 죽일 만큼은 아니라고 적습니다.
접근 제어
접근 제어를 서버 안에 두지 않았습니다. man page 의 -l, --listen 항목이 그것을 스스로 적습니다.
리슨 주소를 묶는 것 말고는 설치를 지킬 다른 방법이 없다고 적습니다. 그래서 고려해야 할 옵션이라고
덧붙입니다. 내부망이나 방화벽 뒤의 네트워크 인터페이스에 묶는 것을 권합니다. 이 옵션의 기본값은
INADDR_ANY 입니다.
인증이 아예 없는 것은 아닙니다. 켜는 자리가 따로 있습니다. 프로토콜 명세에 「Authentication」 절이
있습니다. 사용자 이름과 암호를 토큰으로 보내는 인증입니다. 명세는 이것을 선택 사항이라고 적습니다.
-Y 옵션에 달려 있습니다. 보내는 방법은 아무 키나 붙인 가짜 set 명령입니다. 키와 플래그와
만료시간은 인증에서 무시됩니다. 바이트 수는 사용자 이름과 암호 페이로드의 길이입니다. 성공하면
STORED 가 옵니다. 그 뒤로는 아무 명령이나 정상으로 동작합니다. 어떤 이유로든 실패하면
CLIENT_ERROR 가 옵니다. man page 에는 -S, --enable-sasl 도 있습니다. SASL(Simple
Authentication and Security Layer, 단순 인증·보안 계층) 인증을 켜는 옵션입니다. memcached 를 SASL
지원을 켜서 컴파일했을 때만 뜻이 있는 옵션이라고 적혀 있습니다.
인증을 기본에서 뺀 자리에 남은 것은 짧은 연결 절차입니다. 프로토콜 명세는 클라이언트가 서버가 듣는 포트에 붙어 명령을 보내고 응답을 읽고 연결을 닫는다고 적습니다. 세션을 끝내려고 따로 보낼 명령도 없다고 덧붙입니다. 붙는 쪽을 가리는 일은 서버 밖으로 나갑니다 — 리슨 주소, 그리고 그 앞의 방화벽입니다.
예시
MediaWiki 와 위키백과
MediaWiki 공식 매뉴얼에는 memcached 를 붙이는 문서가 따로 있습니다. memcached 를 MediaWiki 가 값을 캐싱하는 데 쓸 수 있는 인메모리 객체 저장소라고 적습니다. 고른 이유를 둘로 적습니다. 비싼 계산을 다시 할 필요를 줄이는 것, 그리고 데이터베이스 서버의 부하를 줄이는 것입니다.
같은 문서가 안 고르는 자리도 적어 둡니다. 서버 한 대가 굴리는 작은 웹사이트라면 memcached 를 설치하는 것이 수고할 값어치가 없을 수 있다고 적습니다. 그런 경우에는 PHP 가 가진 저장소를 주 객체 저장소로 쓰는 것을 생각해 보라고 덧붙입니다. 위키백과 같은 큰 웹사이트, 그리고 웹 서버 여러 대에 얹힌 위키 일반에서는 memcached 가 MediaWiki 객체 캐시로 흔한 선택이라고 적습니다.
작게 시작한다면 웹 서버에 memcached 한 대만 띄우라며 명령 한 줄을 듭니다.
memcached -d -l 127.0.0.1 -p 11211 -m 64
문서가 그 뜻을 괄호로 풀어 적습니다. 데몬 모드입니다. 루프백 인터페이스로만 닿습니다. 포트는
11211 입니다. 메모리는 64메가바이트까지 씁니다. 그 다음 LocalSettings.php 에 이렇게 적으라고
합니다.
$wgMainCacheType = CACHE_MEMCACHED;
$wgParserCacheType = CACHE_MEMCACHED; // optional
$wgMessageCacheType = CACHE_MEMCACHED; // optional
$wgMemCachedServers = [ '127.0.0.1:11211' ];
여러 대를 쓰려면 $wgMemCachedServers 배열에 항목을 더 넣으면 된다고 적습니다.
같은 문서에 「Security」 문단이 따로 있습니다. MediaWiki 매뉴얼은 memcached 에 보안도 인증도 없다고
적습니다. 그러니 서버를 방화벽 뒤에 알맞게 두고, memcached 서버가 쓰는 포트가 공개적으로 닿지
않게 하라고 적습니다. 위에서 든 실행 명령이 루프백 주소를 -l 로 박아 둔 것도 같은 문단과 짝을
이룹니다.
Django
Django 공식 문서의 캐시 프레임워크 장이 memcached 를 캐시 백엔드로 싣습니다. 전적으로 메모리 기반인 캐시 서버라고 적습니다. 원래 LiveJournal.com 의 높은 부하를 감당하려고 만들어졌다가 Danga Interactive 가 오픈소스로 풀었다고 덧붙입니다. 고른 이유로 적는 것은 데이터가 전부 메모리에 바로 담기므로 데이터베이스나 파일 시스템을 쓰는 부담이 없다는 것입니다.
여러 대를 한 캐시로 묶는 성질도 이유로 듭니다. 장비 여러 대에서 memcached 데몬을 띄우면 프로그램이
그 장비 무리를 캐시 하나로 다룹니다. 캐시 값을 장비마다 복제할 필요가 없다고 적습니다. 그러려면
LOCATION 에 서버 주소를 전부 적습니다.
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache",
"LOCATION": [
"172.19.26.240:11211",
"172.19.26.242:11211",
],
}
}
주소 두 줄이 각각 memcached 한 대입니다. 172.19.26.240 과 172.19.26.242 이고 포트는 둘 다 11211 입니다.
같은 문서가 대가도 함께 적습니다. 캐시한 데이터가 메모리에 담기므로 서버가 죽으면 그 데이터를 잃습니다. 메모리는 영구 데이터 저장을 겨냥한 것이 아니니 메모리 기반 캐싱을 유일한 데이터 저장소로 삼지 말라고 적습니다.
Ruby on Rails
Rails 공식 가이드는 캐시 저장소 하나로 ActiveSupport::Cache::MemCacheStore 를 싣습니다. Danga 의
memcached 서버를 써서 애플리케이션에 중앙 집중식 캐시를 준다고 적습니다. 기본으로는 함께 딸려 오는
dalli 젬을 쓴다고 덧붙입니다. 고른 이유로 적는 것은 공유 캐시 클러스터 하나를 매우 높은 성능과
이중화로 줄 수 있다는 것입니다. 가이드는 이것이 현재 프로덕션 웹사이트에서 가장 인기 있는 캐시
저장소라고 적습니다.
캐시를 초기화할 때 클러스터에 있는 memcached 서버 주소를 전부 적으라고 합니다.
config.cache_store = :mem_cache_store, "cache-1.example.com", "cache-2.example.com"
주소도 환경 변수 MEMCACHE_SERVERS 도 안 주면 memcached 가 로컬호스트의 기본 포트 127.0.0.1:11211
에서 돌고 있다고 가정합니다. 다만 규모가 큰 사이트에는 이상적인 구성이 아니라고 적습니다.
운영
옵션과 기본값
man page 가 옵션마다 기본값을 적어 둡니다.
| 옵션 | 기본값 | man page 가 적는 것 |
|---|---|---|
-m, --memory-limit |
64메가바이트 | 객체 저장에 쓸 메모리 상한 |
-c, --conn-limit |
1024 | 동시 연결 최대 수 |
-t, --threads |
4 | 들어오는 요청을 처리할 스레드 수 |
-I, --max-item-size |
1m |
아이템 크기 상한. 최소 1k, 최대 1G |
-f, --slab-growth-factor |
1.25 | 아이템이 담기는 메모리 청크 크기를 계산할 때 쓰는 배수 |
-b, --listen-backlog |
1024 | 백로그 큐 한도 |
-p, --port |
11211 | 듣는 포트. 0 이면 끕니다 |
-m 에는 단서가 붙습니다. 공식 위키 ConfiguringServer 는 이 값이 전역 메모리 상한이 아니라는
점을 주의 깊게 보라고 적습니다. memcached 는 알려준 것보다 메모리를 조금 더 씁니다. 그리고 64메가바이트
미만으로 잡아도 최소치로 64메가바이트까지는 쓸 수 있다고 적습니다. 값이 실제로 먹었는지는
limit_maxbytes 를 확인하면 됩니다. 공식 위키 ServerMaint 가 -m 인자가 통했는지 확인하는 방법으로
이 지표를 듭니다.
보는 지표
stats 가 내주는 범용 지표 가운데 프로토콜 명세가 뜻을 적어 둔 것들입니다.
| 지표 | 명세가 적는 뜻 |
|---|---|
curr_connections |
열려 있는 연결 수 |
max_connections |
동시 연결 최대 수 |
get_hits |
요청됐고 실제로 있었던 키의 수 |
get_misses |
요청됐는데 없었던 아이템의 수 |
evictions |
새 아이템에게 메모리를 내주려고 캐시에서 치운 유효한 아이템 수 |
reclaimed |
만료된 항목의 메모리를 써서 항목을 저장한 횟수 |
limit_maxbytes |
이 서버가 저장에 쓸 수 있는 바이트 수 |
threads |
요청한 워커 스레드 수 |
ServerMaint 는 curr_connections 가 최대 연결 설정인 -c 에 너무 가까워지지 않는지 지켜보라고
적습니다.
슬랩이 굳는 자리
주 저장 공간은 기본으로 1메가바이트 페이지들로 쪼개집니다. 각 페이지는 필요에 따라 슬랩 클래스에 배정되고, 그 클래스에 정해진 크기의 청크로 잘립니다. 한 번 클래스에 배정된 페이지는 절대 옮겨지지 않습니다. UserInternals 가 별표까지 붙여 못 박는 대목입니다.
빈 청크도 없고 해당 슬랩 클래스에 빈 페이지도 없으면 memcached 는 LRU(Least Recently Used, 가장 오래 안 쓰인 것) 꼬리를 봅니다. 꼬리 쪽 마지막 몇 개를 뒤져 이미 만료되어 재사용해도 되는 아이템을 찾습니다. 만료된 것을 못 찾으면 아직 만료되지 않은 아이템 하나를 축출합니다.
flowchart TD
A["새 아이템이 들어온다"] --> B{"슬랩 클래스에 빈 청크가 있나"}
B -->|있다| C["청크에 담는다"]
B -->|없다| D{"빈 페이지가 있나"}
D -->|있다| E["페이지를 그 클래스에 배정한다"]
D -->|없다| F["LRU 꼬리에서 만료된 아이템을 찾는다"]
F --> G["못 찾으면 아직 안 만료된 아이템을 축출한다"]
페이지가 옮겨지지 않는다는 성질 때문에 한쪽 클래스만 계속 축출되는 상황이 생깁니다. ServerMaint 가
그것을 가려내는 순서를 적어 둡니다. 먼저 전역 evictions 지표를 봅니다. 오르기 시작하면
stats items 안의 evicted 와 evicted_nonzero 를 봅니다. 그 다음 stats slabs 의 total_pages
를 읽어 앞의 축출 수치와 겹쳐 봅니다. 축출이 가장 많은 슬랩이 페이지를 가장 많이 쥔 슬랩과 맞아
떨어지면 정말로 메모리가 모자란 것일 수 있습니다. 잘 맞아떨어지지 않으면 memcached 를 재시작해
메모리를 다시 배정해야 합니다. 다만 1.4.25 이후 판에는 재시작 없이 이 상황을 고치는, 잘 조율된
자동 기능이 들어 있다고 같은 문서가 덧붙입니다.
손으로 만지는 손잡이도 프로토콜 명세에 있습니다. slabs reassign 은 이미 돌고 있는 인스턴스가 한도에
닿은 뒤 메모리를 다시 나누는 데 쓰는 명령입니다. 서버가 뜬 뒤 자동으로 배정된 것과 다르게 메모리를
깔고 싶을 때를 위한 자리입니다. slabs automove 는 슬랩 클래스 사이에서 메모리를 언제 옮길지 스스로
정하는 백그라운드 스레드를 켭니다. 두 명령에는 이 글을 쓰는 시점 기준으로 바뀔 수 있는 명령이라는
주의가 붙어 있습니다.
관련 항목
이것이 속하는 상위 분류
이 캐시 앞뒤에 서는 대상
cache-aside · 데이터베이스 · 세션
담아 둔 것을 다루는 규칙
TTL · 축출 · LRU · 슬랩 할당 · 해시테이블 · 일관성 해싱
터지는 것과 그 이름
캐시 스탬피드 · 썬더링 허드 · 낡은 데이터 · 캐시 미스
이것을 대신할 수 있는 다른 캐시 구현체
이것을 굴리는 데 쓰는 기술
접근을 여닫는 통로
TCP · 네트워크 인터페이스 · 방화벽
이것이 지키지 않는 성질
이것을 실제로 구현·채택한 제품
다른 이름: Memcached