Memcached

高性能な分散メモリオブジェクトキャッシングシステム。シンプルなキーバリューストアで軽量性に特化。マルチスレッド対応。

キャッシュ分散高性能メモリスケーラブル軽量Key-Valueインメモリ

Memcached

Memcachedは、高性能な分散メモリキャッシュシステムです。データベースの負荷軽減、動的Webアプリケーションの高速化を目的として設計され、シンプルなKey-Valueストアとして動作します。

主な特徴

高性能・軽量設計

  • インメモリ処理による超高速アクセス
  • シンプルなプロトコルによる低オーバーヘッド
  • マルチスレッドアーキテクチャ
  • 効率的なメモリ管理(Slab Allocation)

分散対応

  • クライアントサイドでの分散ハッシュ
  • 水平スケーリング対応
  • ノード追加・削除の容易さ
  • 単一障害点なしのアーキテクチャ

シンプルなKey-Valueモデル

  • 文字列キーとバイナリ値のペア
  • TTL(Time To Live)による自動期限切れ
  • LRU(Least Recently Used)による自動削除
  • アトミックな増減操作

インストール

Ubuntu/Debian

# パッケージマネージャからインストール
sudo apt update
sudo apt install memcached libmemcached-tools

# サービス開始
sudo systemctl start memcached
sudo systemctl enable memcached

CentOS/RHEL

# EPEL リポジトリの有効化
sudo dnf install epel-release
sudo dnf install memcached libmemcached

# サービス開始
sudo systemctl start memcached
sudo systemctl enable memcached

Docker

# Memcached の起動
docker run --name memcached-cache \
  -p 11211:11211 \
  -d memcached:1.6-alpine

# メモリ制限付きで起動
docker run --name memcached-cache \
  -p 11211:11211 \
  -d memcached:1.6-alpine memcached -m 64

ソースからのビルド

# 依存関係のインストール
sudo apt-get install build-essential libevent-dev

# Memcached のダウンロードとビルド
wget https://memcached.org/latest
tar -zxf memcached-x.x.x.tar.gz
cd memcached-x.x.x
./configure --prefix=/usr/local/memcached
make && make test && sudo make install

基本設定

起動オプション

# 基本的な起動
memcached -d -m 64 -p 11211 -u memcached

# 詳細オプション指定
memcached \
  -d \                    # デーモンモード
  -m 128 \               # メモリ使用量 (MB)
  -p 11211 \             # TCPポート
  -U 11211 \             # UDPポート (0で無効化)
  -l 127.0.0.1 \         # リスニングアドレス
  -u memcached \         # 実行ユーザー
  -c 1024 \              # 最大同時接続数
  -t 4 \                 # ワーカースレッド数
  -f 1.25 \              # チャンクサイズ成長因子
  -n 48 \                # 最小チャンクサイズ
  -v                     # 詳細ログ

systemd設定 (/etc/systemd/system/memcached.service)

[Unit]
Description=Memcached
After=network.target

[Service]
Type=notify
User=memcached
Group=memcached
ExecStart=/usr/bin/memcached -d -m 128 -p 11211 -u memcached -l 127.0.0.1
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target

推奨本番設定

# 高性能設定
memcached \
  -d \
  -m 512 \
  -p 11211 \
  -U 0 \                 # UDP無効化 (セキュリティ)
  -l 127.0.0.1 \
  -u memcached \
  -c 2048 \
  -t 8 \
  -o slab_reassign,slab_automove,lru_crawler,lru_maintainer \
  -o maxconns_fast,hash_algorithm=murmur3

基本操作

telnetまたはnetcatでの接続

# telnetで接続
telnet localhost 11211

# netcatで接続
nc localhost 11211

基本コマンド

# データの設定
set mykey 0 3600 5
hello
STORED

# データの取得
get mykey
VALUE mykey 0 5
hello
END

# 複数キーの取得
get key1 key2 key3

# データの削除
delete mykey
DELETED

# 全データクリア
flush_all
OK

条件付き操作

# add: キーが存在しない場合のみ設定
add newkey 0 3600 5
world
STORED

# replace: キーが存在する場合のみ置換
replace mykey 0 3600 7
updated
STORED

# append: 既存値の末尾に追加
append mykey 0 3600 6
_value
STORED

# prepend: 既存値の先頭に追加
prepend mykey 0 3600 7
prefix_
STORED

数値操作

# カウンターの設定
set counter 0 0 1
0
STORED

# インクリメント
incr counter 1
1

# デクリメント
decr counter 1
0

# 大きな増減
incr counter 100
100

統計情報とモニタリング

基本統計

# 全般的な統計
stats
STAT pid 12345
STAT uptime 3600
STAT curr_connections 5
STAT total_connections 100
STAT bytes 1024
STAT curr_items 10
STAT total_items 50
STAT evictions 2
STAT hit_ratio 0.85
END

# アイテム統計
stats items
STAT items:1:number 5
STAT items:1:age 1234
STAT items:1:evicted 0
STAT items:1:evicted_nonzero 0
STAT items:1:evicted_time 0
END

# スラブ統計
stats slabs
STAT 1:chunk_size 96
STAT 1:chunks_per_page 10922
STAT 1:total_pages 1
STAT 1:total_chunks 10922
STAT 1:used_chunks 5
STAT 1:free_chunks 10917
STAT 1:get_hits 25
STAT 1:cmd_set 10
END

詳細統計

# 設定値確認
stats settings
STAT maxbytes 67108864
STAT maxconns 1024
STAT tcpport 11211
STAT udpport 11211
STAT verbosity 0
STAT num_threads 4
STAT growth_factor 1.25
STAT chunk_size 48
END

# サイズ分布確認(開発時のみ)
stats sizes
STAT 96 5
STAT 120 2
STAT 152 1
END

クライアント実装例

Python (pymemcache)

from pymemcache.client.base import Client

# 単一サーバー接続
client = Client(('localhost', 11211))

# データ操作
client.set('key', 'value', expire=3600)
result = client.get('key')
client.delete('key')

# 数値操作
client.set('counter', 0)
client.incr('counter', 1)
client.decr('counter', 1)

# バッチ操作
client.set_many({'key1': 'value1', 'key2': 'value2'}, expire=3600)
values = client.get_many(['key1', 'key2'])

Python (分散設定)

from pymemcache.client.hash import HashClient

# 複数サーバー設定
servers = [
    ('server1', 11211),
    ('server2', 11211),
    ('server3', 11211)
]

client = HashClient(servers)

# 自動的に適切なサーバーに分散
client.set('user:1000', user_data)
client.set('session:abc123', session_data)

PHP

<?php
$memcached = new Memcached();

// サーバー追加
$memcached->addServer('localhost', 11211);
$memcached->addServer('server2', 11211);

// データ操作
$memcached->set('key', 'value', 3600);
$value = $memcached->get('key');
$memcached->delete('key');

// バッチ操作
$items = [
    'key1' => 'value1',
    'key2' => 'value2'
];
$memcached->setMulti($items, 3600);
$values = $memcached->getMulti(['key1', 'key2']);
?>

Node.js (memjs)

const memjs = require('memjs');

// クライアント作成
const client = memjs.Client.create('localhost:11211');

// データ操作
await client.set('key', 'value', {expires: 3600});
const value = await client.get('key');
await client.delete('key');

// 条件付き操作
await client.add('newkey', 'value');
await client.replace('existingkey', 'newvalue');

パフォーマンス最適化

メモリチューニング

# スラブクラス確認
memcached -vv
# slab class   1: chunk size        80 perslab   13107
# slab class   2: chunk size       104 perslab   10082
# ...

# 成長因子調整(デフォルト: 1.25)
memcached -f 1.1  # より細かいスラブクラス
memcached -f 2.0  # より粗いスラブクラス

# 最小チャンクサイズ調整
memcached -n 64   # 64バイトから開始

接続・スレッド最適化

# スレッド数最適化(CPUコア数と同じ)
memcached -t $(nproc)

# 接続数上限調整
memcached -c 4096

# 高速接続処理
memcached -o maxconns_fast

高度な最適化

# LRU機能有効化
memcached -o lru_crawler,lru_maintainer

# スラブ自動管理
memcached -o slab_reassign,slab_automove

# 高性能ハッシュアルゴリズム
memcached -o hash_algorithm=murmur3

# 組み合わせ設定
memcached -o slab_reassign,slab_automove,lru_crawler,lru_maintainer,maxconns_fast,hash_algorithm=murmur3

分散・クラスタリング

クライアントサイド分散

import hashlib
from pymemcache.client.base import Client

class DistributedMemcache:
    def __init__(self, servers):
        self.servers = [Client(server) for server in servers]
        self.server_count = len(self.servers)
    
    def _get_server(self, key):
        hash_value = int(hashlib.md5(key.encode()).hexdigest(), 16)
        return self.servers[hash_value % self.server_count]
    
    def get(self, key):
        server = self._get_server(key)
        return server.get(key)
    
    def set(self, key, value, expire=0):
        server = self._get_server(key)
        return server.set(key, value, expire)

# 使用例
servers = [('server1', 11211), ('server2', 11211), ('server3', 11211)]
cache = DistributedMemcache(servers)
cache.set('user:1000', user_data)

一貫性ハッシュ実装

import bisect
import hashlib

class ConsistentHash:
    def __init__(self, servers, replicas=150):
        self.replicas = replicas
        self.ring = {}
        self.sorted_keys = []
        
        for server in servers:
            self.add_server(server)
    
    def _hash(self, key):
        return int(hashlib.md5(key.encode()).hexdigest(), 16)
    
    def add_server(self, server):
        for i in range(self.replicas):
            key = self._hash(f"{server}:{i}")
            self.ring[key] = server
            bisect.insort(self.sorted_keys, key)
    
    def remove_server(self, server):
        for i in range(self.replicas):
            key = self._hash(f"{server}:{i}")
            del self.ring[key]
            self.sorted_keys.remove(key)
    
    def get_server(self, key):
        if not self.ring:
            return None
        
        hash_key = self._hash(key)
        idx = bisect.bisect_right(self.sorted_keys, hash_key)
        if idx == len(self.sorted_keys):
            idx = 0
        return self.ring[self.sorted_keys[idx]]

セキュリティ

ネットワークセキュリティ

# ローカルホストのみリスニング
memcached -l 127.0.0.1

# 特定IPのみリスニング
memcached -l 192.168.1.100

# UDP無効化(DDoS対策)
memcached -U 0

# ファイアウォール設定
sudo ufw allow from 192.168.1.0/24 to any port 11211
sudo ufw deny 11211

iptables設定

# 特定サブネットからのみアクセス許可
sudo iptables -A INPUT -p tcp --dport 11211 -s 192.168.1.0/24 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 11211 -j DROP

# レート制限
sudo iptables -A INPUT -p tcp --dport 11211 -m limit --limit 25/minute --limit-burst 100 -j ACCEPT

監視とメンテナンス

監視スクリプト例

#!/bin/bash
# memcached-monitor.sh

MEMCACHED_HOST="localhost"
MEMCACHED_PORT="11211"

# 接続テスト
if ! echo "version" | nc -w1 $MEMCACHED_HOST $MEMCACHED_PORT > /dev/null 2>&1; then
    echo "ERROR: Cannot connect to Memcached"
    exit 1
fi

# 統計取得
STATS=$(echo "stats" | nc -w1 $MEMCACHED_HOST $MEMCACHED_PORT)

# ヒット率計算
GET_HITS=$(echo "$STATS" | grep "STAT get_hits" | awk '{print $3}')
GET_MISSES=$(echo "$STATS" | grep "STAT get_misses" | awk '{print $3}')
TOTAL_GETS=$((GET_HITS + GET_MISSES))

if [ $TOTAL_GETS -gt 0 ]; then
    HIT_RATE=$(echo "scale=2; $GET_HITS * 100 / $TOTAL_GETS" | bc)
    echo "Hit Rate: ${HIT_RATE}%"
fi

# メモリ使用率
BYTES=$(echo "$STATS" | grep "STAT bytes " | awk '{print $3}')
LIMIT_MAXBYTES=$(echo "$STATS" | grep "STAT limit_maxbytes" | awk '{print $3}')
MEMORY_USAGE=$(echo "scale=2; $BYTES * 100 / $LIMIT_MAXBYTES" | bc)
echo "Memory Usage: ${MEMORY_USAGE}%"

# エビクション確認
EVICTIONS=$(echo "$STATS" | grep "STAT evictions" | awk '{print $3}')
echo "Evictions: $EVICTIONS"

自動復旧設定

#!/bin/bash
# memcached-watchdog.sh

MEMCACHED_HOST="localhost"
MEMCACHED_PORT="11211"
MAX_RETRIES=3
RETRY_COUNT=0

while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
    if echo "version" | timeout 5 nc $MEMCACHED_HOST $MEMCACHED_PORT > /dev/null 2>&1; then
        echo "Memcached is running"
        exit 0
    fi
    
    echo "Memcached is not responding, attempting restart..."
    sudo systemctl restart memcached
    sleep 10
    RETRY_COUNT=$((RETRY_COUNT + 1))
done

echo "Failed to restart Memcached after $MAX_RETRIES attempts"
exit 1

ベストプラクティス

キー設計

# 名前空間の使用
user_key = f"user:{user_id}"
session_key = f"session:{session_id}"
cache_key = f"cache:article:{article_id}"

# バージョニング
cache_key = f"v2:user:{user_id}:profile"

# 期限設定の工夫
SHORT_TTL = 300    # 5分
MEDIUM_TTL = 3600  # 1時間
LONG_TTL = 86400   # 24時間

エラーハンドリング

def safe_cache_get(client, key, default=None):
    try:
        result = client.get(key)
        return result if result is not None else default
    except Exception as e:
        logger.warning(f"Cache get failed for key {key}: {e}")
        return default

def safe_cache_set(client, key, value, expire=3600):
    try:
        return client.set(key, value, expire)
    except Exception as e:
        logger.warning(f"Cache set failed for key {key}: {e}")
        return False

プールド接続

from pymemcache.client.base import PooledClient

# 接続プール使用
client = PooledClient(
    ('localhost', 11211),
    max_pool_size=100,
    connect_timeout=5,
    timeout=2
)

Memcachedは、そのシンプルさと高性能により、Webアプリケーションのキャッシング層として広く採用されています。適切な設定と運用により、システム全体のパフォーマンスを大幅に向上させることができます。