データベース
Memcached
概要
Memcachedは、メモリベースの高速キーバリュー型キャッシュシステムです。データベースの負荷軽減やWebアプリケーションの応答速度向上を目的として設計されており、シンプルで軽量な実装が特徴です。
動的Webアプリケーションにおいて、データベースクエリ結果やAPIレスポンスなどの計算コストの高いデータをメモリ上にキャッシュすることで、大幅なパフォーマンス向上を実現します。
詳細
- 開発: 2003年にBrad Fitzpatrickによって開発
- アーキテクチャ: マルチスレッド対応、イベント駆動型サーバー
- データ保存: メモリのみ(永続化なし)
- プロトコル: シンプルなテキストベースプロトコル
- 分散: サーバー側での分散機能なし(クライアント側でハッシュ分散)
- レプリケーション: なし(単純なキャッシュ用途に特化)
- プロキシ機能: 1.6以降でビルトインプロキシをサポート
- SASL認証: セキュリティ機能をサポート
- LRU: メモリ不足時の自動データ削除
- TTL: 自動期限切れ機能
メリット・デメリット
メリット
- 高速性: メモリベースによる非常に高い読み書き速度
- シンプル: 理解しやすい仕組みと軽量な実装
- 安定性: 長期間にわたる実績と安定した動作
- スケーラビリティ: 水平スケールが容易
- 多言語サポート: 豊富なクライアントライブラリ
- 低レイテンシ: ミリ秒未満の応答時間
デメリット
- 永続化なし: サーバー再起動でデータが失われる
- 単純な構造: 複雑なデータ構造は扱えない
- レプリケーションなし: 高可用性は外部で実装が必要
- メモリ制限: 使用可能メモリに依存
- 原子操作の制限: 複雑なトランザクションは不可能
主要リンク
書き方の例
セットアップ・設定
# Ubuntu/Debianでのインストール
sudo apt-get install memcached
# CentOS/RHELでのインストール
sudo yum install memcached
# サーバー起動(基本設定)
memcached -m 64 -p 11211 -u memcache -d
# 詳細設定での起動
memcached -m 512 -p 11211 -l 127.0.0.1 -d -v
基本操作(CRUD)
# Python(python-memcached)での基本操作
import memcache
# 接続設定
mc = memcache.Client(['127.0.0.1:11211'], debug=0)
# データの保存(SET)
mc.set("user:1001", {"name": "田中", "age": 30}, time=3600)
mc.set("counter", 100, time=300)
# データの取得(GET)
user_data = mc.get("user:1001")
counter = mc.get("counter")
# データの更新
mc.set("user:1001", {"name": "田中", "age": 31}, time=3600)
# データの削除
mc.delete("user:1001")
高度な操作
# 複数キーの一括操作
keys = ["user:1001", "user:1002", "user:1003"]
users = mc.get_multi(keys)
# 原子的な増減操作
mc.incr("page_views", delta=1)
mc.decr("remaining_count", delta=5)
# 条件付き保存(キーが存在しない場合のみ)
success = mc.add("lock:resource:123", "locked", time=30)
# 置換操作(キーが存在する場合のみ)
mc.replace("config:cache_timeout", 600, time=7200)
プロキシ機能(Memcached 1.6以降)
-- プロキシ設定ファイル(config.lua)
pools{
main = {
backends = {
"127.0.0.1:11214",
"127.0.0.1:11215",
}
}
}
routes{
default = route_direct{ child = "main" }
}
# プロキシモードでの起動
memcached -o proxy_config=routelib.lua,proxy_arg=config.lua -p 11212 &
実用例
// Node.js での実装例
const memcached = require('memcached');
const mc = new memcached('127.0.0.1:11211');
// キャッシュ戦略の実装
async function getUserProfile(userId) {
const cacheKey = `user_profile:${userId}`;
// キャッシュから取得を試行
return new Promise((resolve, reject) => {
mc.get(cacheKey, async (err, data) => {
if (err) return reject(err);
if (data) {
// キャッシュヒット
console.log('Cache hit');
resolve(data);
} else {
// キャッシュミス:データベースから取得
console.log('Cache miss');
const profileData = await fetchUserFromDatabase(userId);
// 1時間キャッシュに保存
mc.set(cacheKey, profileData, 3600, (setErr) => {
if (setErr) console.error('Cache set error:', setErr);
});
resolve(profileData);
}
});
});
}
ベストプラクティス
# 接続プールと例外処理
import memcache
import json
import logging
class MemcachedManager:
def __init__(self, servers=['127.0.0.1:11211']):
self.mc = memcache.Client(servers, debug=0)
self.logger = logging.getLogger(__name__)
def safe_get(self, key, default=None):
try:
return self.mc.get(key) or default
except Exception as e:
self.logger.error(f"Memcached get error: {e}")
return default
def safe_set(self, key, value, ttl=3600):
try:
return self.mc.set(key, value, time=ttl)
except Exception as e:
self.logger.error(f"Memcached set error: {e}")
return False
def cache_with_fallback(self, key, fetch_func, ttl=3600):
# キャッシュから取得
data = self.safe_get(key)
if data is not None:
return data
# フォールバック:元データを取得
try:
fresh_data = fetch_func()
self.safe_set(key, fresh_data, ttl)
return fresh_data
except Exception as e:
self.logger.error(f"Fallback fetch error: {e}")
raise
CLI操作
# 統計情報の確認
echo "stats" | nc 127.0.0.1 11211
# アイテム数の確認
echo "stats items" | nc 127.0.0.1 11211
# サーバー情報の確認
echo "stats settings" | nc 127.0.0.1 11211
# 特定のキーの削除
echo "delete user:1001" | nc 127.0.0.1 11211
# キャッシュのフラッシュ(全削除)
echo "flush_all" | nc 127.0.0.1 11211