memcached (非舊 memcache) 功能完整| 分類 | 方法 | 說明 |
|---|---|---|
| 連接 | addServer(host, port, weight) | 加入伺服器池(不立即連接) |
| connect(host, port) / pconnect() | 立即連接單台 / 持久連接 | |
| 讀寫 | set(key, value, expiration) | 存儲(覆蓋) |
| get(key) | 獲取 | |
| add(key, value, exp) | 鍵不存在才存儲(原子) | |
| replace(key, value, exp) | 鍵存在才更新 | |
| delete(key) | 刪除 | |
| 進階 | getMulti(keys) | 批量獲取,減少網絡往返 |
| setMulti(items, exp) | 批量存儲 | |
| getStats() / getVersion() | 監控統計 | |
| CAS | get(key) 返回 token → cas(token, key, value) | 樂觀鎖防併發覆蓋 |
<?php
$mc = new Memcached();
$mc->addServer('172.10.10.10', 11211); // 標準端口 11211
$mc->setOption(Memcached::OPT_BINARY_PROTOCOL, true); // 二進制協議,性能更好
$mc->setOption(Memcached::OPT_TCP_NODELAY, true); // 關閉 Nagle 算法
$mc->setOption(Memcached::OPT_NO_BLOCK, true); // 非阻塞 I/O
$mc->setOption(Memcached::OPT_CONNECT_TIMEOUT, 1000); // 1秒連接超時
$mc->setOption(Memcached::OPT_POLL_TIMEOUT, 2000); // 2秒輪詢超時
// 基礎操作
$mc->set('user:1001', ['name' => 'Alice', 'vip' => true], 3600); // 1小時過期
$data = $mc->get('user:1001');
var_dump($data);
// 多鍵批量
$profiles = $mc->getMulti(['user:1001', 'user:1002', 'user:1003']);
// CAS 樂觀鎖(庫存扣減等場景)
$casToken = null;
$stock = $mc->get('product:999:stock', null, $casToken);
if ($stock > 0) {
$mc->cas($casToken, 'product:999:stock', $stock - 1);
}
$mc = new Memcached();
// 方案 A:連接池模式(推薦生產)
$mc->addServer('10.0.0.1', 11211, 30); // 權重 30
$mc->addServer('10.0.0.2', 11211, 20);
$mc->addServer('10.0.0.3', 11211, 10);
// Client 端一致性哈希自動分片,單台故障自動切換
// 方案 B:單連接模式(僅測試/單節點)
$mc->connect('10.0.0.1', 11211);
// 若先 addServer 再 connect,會忽略池子只用連接的那台
理解協議有助於排查疑難雜症、編寫代理、或在無擴展環境下工作。
// 存儲
set key flags exptime bytes\r\n
value\r\n
// 回應:STORED / NOT_STORED / EXISTS / NOT_FOUND
// 獲取
get key1 key2 ...\r\n
// 回應:
VALUE key flags bytes\r\n
value\r\n
END
// 刪除
delete key\r\n
// 回應:DELETED / NOT_FOUND
function memcachedGet(string $host, int $port, string $key, float $timeout = 1.0): ?string {
$fp = @fsockopen($host, $port, $errno, $errstr, $timeout);
if (!$fp) {
trigger_error("Memcached 連接失敗: {$errstr} ({$errno})", E_USER_WARNING);
return null;
}
stream_set_timeout($fp, $timeout);
$cmd = "get {$key}\r\n";
fwrite($fp, $cmd);
$response = '';
while (!feof($fp)) {
$line = fgets($fp, 4096);
if ($line === false) break;
$response .= $line;
if (str_starts_with($line, 'END')) break;
}
fclose($fp);
// 解析
if (str_starts_with($response, 'END')) return null; // 不存在
// VALUE key flags bytes\r\nvalue\r\nEND
$lines = explode("\r\n", trim($response));
if (count($lines) >= 2 && str_starts_with($lines[0], 'VALUE')) {
return $lines[1]; // value 行
}
return null;
}
// 使用
$value = memcachedGet('172.10.10.10', 11211, 'mykey');
var_dump($value);
Memcached 實例,避免每請求新建連接。OPT_BINARY_PROTOCOL = true,減少解析開銷、支援 SASL 認證。setOption(Memcached::OPT_COMPRESSION, true),自動壓縮 > 100 字節值(需 zlib)。OPT_SERIALIZER = Memcached::SERIALIZER_IGBINARY,比 PHP 原生 serialize 快 2-3x、體積更小。get_hits / (get_hits + get_misses) 命中率 > 95%、evictions 激增說明內存不足、curr_connections 逼近上限需擴容。prefix:type:id:field,如 acme:user:1001:profile,便於清理與監控。| 現象 | 原因 | 解決 |
|---|---|---|
| 獲取到 false 但鍵存在 | 值為 0、空字串、false 被誤判 | 用 $mc->get($key, null, $cas) 檢查結果碼或 getResultCode() == Memcached::RES_SUCCESS |
| set 成功但 get 為空 | 鍵名包含特殊字符/空格/超過 250 字節 | 鍵名僅用 [a-zA-Z0-9_:.-],長度 < 200 |
| 集群下數據不一致 | Client 端一致性哈希環不同步 | 所有 Client 同一配置順序 addServer,或改用 Proxy (Twemproxy/Mcrouter) |
| PHP 7.4+ 編譯報錯 | 舊 memcache 擴展不支援 PHP 7+ | 必須安裝 pecl install memcached (有 d 結尾) |
Memcached 以「簡單、快速、分散式」成為 PHP 高併發架構的標配緩存層。掌握官方擴展最佳實踐(連接池、二進制協議、壓縮、igbinary),並理解底層文本協議,能讓你在排查疑難雜症、設計高可用架構時遊刃有餘。記住:緩存只是加速器,永遠不要將其作為唯一數據源,業務核心數據必須落地持久化存儲。