前置需求
- 安裝 Redis 服務
- PHP 安裝 redis 擴充套件:
pecl install redis
- 啟用擴充:
extension=redis.so
Redis 快取類別實作
class RedisCache implements CacheInterface {
private $redis;
private $config;
private $connected = false;
public function __construct(array $config = []) {
if (!class_exists('Redis')) {
throw new Exception('Redis 擴充套件未安裝');
}
$this->redis = new Redis();
$this->config = array_merge([
'host' => '127.0.0.1',
'port' => 6379,
'database' => 0,
'password' => null,
'timeout' => 2.5,
'persistent' => true,
'prefix' => 'app:cache:',
'serializer' => Redis::SERIALIZER_PHP, // 或 SERIALIZER_IGBINARY
], $config);
$this->connect();
}
private function connect() {
if ($this->connected) return;
$method = $this->config['persistent'] ? 'pconnect' : 'connect';
$result = $this->redis->$method(
$this->config['host'],
$this->config['port'],
$this->config['timeout']
);
if (!$result) {
throw new Exception("Redis 連線失敗:{$this->config['host']}:{$this->config['port']}");
}
if ($this->config['password']) {
$this->redis->auth($this->config['password']);
}
$this->redis->select($this->config['database']);
$this->redis->setOption(Redis::OPT_SERIALIZER, $this->config['serializer']);
$this->redis->setOption(Redis::OPT_PREFIX, $this->config['prefix']);
$this->connected = true;
}
public function set($key, $value, $expire = 0) {
$this->connect();
if ($expire > 0) {
return $this->redis->setex($key, $expire, $value);
}
return $this->redis->set($key, $value);
}
public function get($key) {
$this->connect();
return $this->redis->get($key);
}
public function delete($key) {
$this->connect();
return $this->redis->del($key);
}
public function flush() {
$this->connect();
// 僅清除當前資料庫且帶前綴的鍵
$keys = $this->redis->keys($this->config['prefix'] . '*');
if ($keys) {
return $this->redis->del($keys);
}
return true;
}
// === Redis 進階功能 ===
/** 列表操作 */
public function lPush($key, $value) { $this->connect(); return $this->redis->lPush($key, $value); }
public function rPop($key) { $this->connect(); return $this->redis->rPop($key); }
public function lRange($key, $start, $end) { $this->connect(); return $this->redis->lRange($key, $start, $end); }
/** 集合操作 */
public function sAdd($key, $member) { $this->connect(); return $this->redis->sAdd($key, $member); }
public function sMembers($key) { $this->connect(); return $this->redis->sMembers($key); }
public function sIsMember($key, $member) { $this->connect(); return $this->redis->sIsMember($key, $member); }
/** 有序集合 */
public function zAdd($key, $score, $member) { $this->connect(); return $this->redis->zAdd($key, $score, $member); }
public function zRange($key, $start, $end, $withScores = false) { $this->connect(); return $this->redis->zRange($key, $start, $end, $withScores); }
/** 原子計數器 */
public function incr($key) { $this->connect(); return $this->redis->incr($key); }
public function decr($key) { $this->connect(); return $this->redis->decr($key); }
public function incrBy($key, $value) { $this->connect(); return $this->redis->incrBy($key, $value); }
/** 分散式鎖 */
public function lock($key, $expire = 10) {
$this->connect();
$lockKey = 'lock:' . $key;
return $this->redis->set($lockKey, 1, ['nx', 'ex' => $expire]);
}
public function unlock($key) {
$this->connect();
$lockKey = 'lock:' . $key;
// Lua 腳本保證原子性:僅刪除自己設定的鎖
$script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
return $this->redis->eval($script, 1, $lockKey, 1);
}
/** 管線操作(批量指令) */
public function pipeline(callable $callback) {
$this->connect();
$this->redis->multi(Redis::PIPELINE);
$callback($this->redis);
return $this->redis->exec();
}
}
使用範例
$redis = new RedisCache([
'host' => '127.0.0.1',
'prefix' => 'myapp:',
'serializer' => Redis::SERIALIZER_IGBINARY, // 需安裝 igbinary
]);
// 基本快取
$redis->set('config:site_name', '我的網站', 86400);
$name = $redis->get('config:site_name');
// 熱門文章排行(有序集合)
$redis->zAdd('rank:articles', 100, 'article:1');
$redis->zAdd('rank:articles', 85, 'article:2');
$topArticles = $redis->zRange('rank:articles', 0, 9, true); // 前 10 名
// 分散式鎖防止快取擊穿
$lockKey = 'rebuild:homepage_cache';
if ($redis->lock($lockKey, 30)) {
try {
// 重建快取邏輯
$html = renderHomepage();
$redis->set('page:home', $html, 3600);
} finally {
$redis->unlock($lockKey);
}
}
// 管線批量寫入
$redis->pipeline(function($r) {
for ($i = 1; $i <= 1000; $i++) {
$r->set("batch:$i", "value_$i");
}
});
Redis vs Memcached 選擇指南
| 特性 | Redis | Memcached |
| 資料結構 | 字串、列表、集合、有序集合、雜湊、位圖、HyperLogLog | 簡單 Key-Value |
| 持久化 | RDB/AOF 可選 | 不支援 |
| 複寫/叢集 | 主從複寫、Redis Cluster、Sentinel | Client 端一致性雜湊 |
| 記憶體管理 | 自有分配器、支援過期驅逐策略 | Slab Allocator、LRU |
| 適用場景 | 複雜資料結構、排行榜、分散式鎖、快取、Session、佇列 | 純快取、簡單 Key-Value、大量小物件 |