基礎 CRUD 操作
<?php
$mem = new Memcached();
$mem->addServer('192.168.0.200', 11211); // 支持多台自動分片
// 存儲 (key, value, expiration 秒)
$mem->set('key1', 'First Value', 60);
$mem->set('key2', ['aaa','bbb','ccc','ddd'], 60);
// 讀取
$val1 = $mem->get('key1');
$val2 = $mem->get('key2');
// 替換 (僅鍵存在時成功)
$mem->replace('key1', 'Replaced Value', 60);
// 刪除
$mem->delete('key1');
// 原子增減 (僅限數值)
$mem->set('counter', 0);
$mem->increment('counter'); // 1
$mem->decrement('counter'); // 0
// 追加/前置 (字符串)
$mem->set('str', 'Hello');
$mem->append('str', ' World'); // 'Hello World'
$mem->prepend('str', 'Say '); // 'Say Hello World'
// 獲取多鍵
$multi = $mem->getMulti(['key1', 'key2', 'counter']);
// 刷新所有 (慎用!)
$mem->flush();
// 關閉連接
$mem->close();
?>
Memcached 類 vs Memcache 類
| 特性 | Memcached (推薦) | Memcache (舊) |
| 類名 | Memcached | Memcache |
| 二進制協議 | ✅ 支持 | ❌ 僅文本協議 |
| 多鍵獲取 | getMulti() | 不支持 |
| CAS 操作 | ✅ cas() | ❌ |
| 持久連接 | ✅ addServer(..., true) | ✅ |
| 壓縮 | ✅ setOption(OPT_COMPRESSION, true) | ✅ |
| PHP 7+ 支持 | 完美 | 不再維護 |
分布式架構設計
一致性哈希分片 (客戶端自動)
Memcached 客戶端內建一致性哈希 (Ketama 算法),添加多台 Server 即可自動分片:
$mem = new Memcached();
$mem->addServer('10.0.0.1', 11211, 33); // 權重 33
$mem->addServer('10.0.0.2', 11211, 33);
$mem->addServer('10.0.0.3', 11211, 34);
- 鍵自動路由到對應節點
- 節點增減只影響少量鍵重新分佈
- 業務層無感知
業務鍵設計規範
// 格式: {業務域}:{實體類型}:{ID}:{屬性}
// 示例:
$userProfile = "user:profile:10001"; // 用戶基本信息
$userSession = "user:session:s_abc123"; // Session
$productInfo = "product:info:SKU_20240101"; // 商品詳情
$categoryTree = "category:tree:level1"; // 分類樹
$rateLimit = "ratelimit:ip:192.168.1.1"; // 限流計數器
- 使用冒號分隔,便於正則匹配、手動清理
- 統一前綴區分環境:
prod:... / dev:...
- 避免過長鍵 (>250 字節),影響性能
緩存穿透/雪崩/擊穿解決方案
| 問題 | 現象 | Memcached 方案 |
| 緩存穿透 | 查詢不存在的 Key,每次直穿 DB | 緩存空對象 (set key '' 300s) + 布隆過濾器預檢 |
| 緩存雪崩 | 大量 Key 同期過期,請求洪峰打 DB | TTL 加隨機值 (300+rand(0,60)) + 多級緩存 (本地 APCu + Memcached) |
| 緩存擊穿 | 熱點 Key 失效瞬間大量併發重建 | 互斥鎖重建 (Memcached add 鎖) 或 永不過期 + 異步更新 |
互斥鎖重建熱點 Key 示例
<?php
function getWithLock($key, $ttl, callable $rebuilder) {
global $mem;
$val = $mem->get($key);
if ($val !== false) return $val;
// 嘗試獲取鎖 (add 只有鍵不存在才成功)
$lockKey = "lock:$key";
if ($mem->add($lockKey, 1, 10)) { // 鎖過期 10s 防死鎖
try {
$val = $rebuilder(); // 重建數據 (查 DB、計算等)
$mem->set($key, $val, $ttl);
} finally {
$mem->delete($lockKey);
}
return $val;
} else {
// 等待持有鎖的進程重建完成
usleep(50000); // 50ms
return getWithLock($key, $ttl, $rebuilder); // 遞歸重試
}
}
// 使用
$hotData = getWithLock('homepage:banner', 3600, function() {
return $db->query("SELECT * FROM banner WHERE status=1 ORDER BY sort");
});
?>
安全加固
1. 內網隔離 (最關鍵)
- Memcached **無認證機制**,任何能連接端口的客戶端都可讀寫數據
- **必須**部署在內網,綁定內網 IP:
memcached -l 10.0.0.1 -p 11211
- Web 服務器通過內網 IP 訪問,禁止公網暴露
2. 防火牆限制訪問來源
# iptables 示例:只允許 Web 服務器集群訪問
iptables -A INPUT -p tcp -s 10.0.1.0/24 --dport 11211 -j ACCEPT
iptables -A INPUT -p tcp -s 10.0.2.0/24 --dport 11211 -j ACCEPT
iptables -A INPUT -p tcp --dport 11211 -j DROP
3. 禁用 UDP (如不需要)
memcached -U 0 # 關閉 UDP 端口,僅保留 TCP
4. 監控關鍵指標
$stats = $mem->getStats();
$serverStat = $mem->getServerStatus('10.0.0.1', 11211);
print_r($stats);
/*
關注指標:
- curr_items: 當前項目數
- bytes: 佔用內存
- limit_maxbytes: 最大內存
- get_hits / get_misses: 命中率 = hits/(hits+misses)
- evictions: 驅逐次數 (內存不足被動刪除)
- curr_connections: 當前連接數
*/
?>
性能調優參數 (啟動參數)
memcached -d -m 2048 -u memcached -l 10.0.0.1 -p 11211 -c 4096 -t 4 -R 20 -B binary
# -m 2048: 最大內存 2GB
# -u memcached: 運行用戶 (非 root)
# -c 4096: 最大並發連接
# -t 4: 工作線程數 (CPU 核心數)
# -R 20: 最大請求處理速率限制 (每事件循環)
# -B binary: 二進制協議 (更高效)
遷移建議:Redis 作為更強替代者
| 場景 | 選 Memcached | 選 Redis |
| 純緩存、Key-String、超大內存、極高 QPS | ✅ 更輕量、多線程、內存利用率高 | 單線程可能成瓶頸 |
| 需要持久化、複雜數據結構、發布訂閲、Lua 腳本 | ❌ | ✅ 原生支持 |
| Session 共享、分布式鎖、排行榜、延遲隊列 | ❌ 難以實現 | ✅ 一行代碼 |
| 現有架構已深度綁定 Memcached | ✅ 維護成本低 | 遷移成本高 |
新項目**首選 Redis**,老項目 Memcached 運行穩定則無需強行遷移。