在PHP开发中,缓存是提升性能的利器。十年前我刚入行时,Memcached还是主流选择,但如今Redis已经占据了绝对主导地位。这不仅仅是技术潮流的变化,更是因为Redis在数据结构、持久化、集群支持等方面提供了更全面的解决方案。
上周我帮一个电商项目做性能优化,原本使用的文件缓存导致高峰期页面加载需要3秒以上。切换到Redis后,同样的商品列表页响应时间直接降到了300毫秒内。这种性能提升不是偶然的,而是Redis的架构设计决定的。
Redis最让我惊喜的是它不只是一个简单的键值存储。在处理一个社交项目时,我们需要实现以下功能:
这些如果用Memcached实现,要么需要多次查询,要么要在PHP端做复杂处理。而Redis原生支持这些数据结构,一个命令就能完成复杂操作。比如获取排行榜前10名:
php复制$redis->zRevRange('user_scores', 0, 9);
去年我们有个惨痛教训:使用Memcached存储购物车数据,结果服务器宕机导致所有临时数据丢失。Redis的RDB和AOF持久化完美解决了这个问题:
我的经验是:生产环境应该同时开启两种方式。这是我们的典型配置:
ini复制save 900 1 # 15分钟至少有1个key变化就保存
save 300 10 # 5分钟至少有10个key变化就保存
appendonly yes # 开启AOF
appendfsync everysec # 每秒同步
当单机性能遇到瓶颈时,Redis Cluster提供了完美的水平扩展方案。我们曾经用Twemproxy做分片,但遇到以下问题:
Redis Cluster原生解决了这些问题。在最近的项目中,我们部署了6个节点(3主3从),即使单个节点宕机也能自动切换。PHP客户端使用Predis时只需要这样配置:
php复制$servers = [
'tcp://192.168.1.101:6379',
'tcp://192.168.1.102:6379',
//...其他节点
];
$options = ['cluster' => 'redis'];
$redis = new Predis\Client($servers, $options);
Redis的RESP协议极其精简,相比Memcached的文本协议解析效率更高。在PHP中,我们通常使用以下两种连接方式:
php复制$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
php复制$pool = new RedisPool(function() {
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
return $redis;
}, 100); // 最大100个连接
重要提示:PHP-FPM环境下一定要使用pconnect持久连接,否则每个请求都新建连接会导致Redis崩溃
从早期的phpredis到现在的多种选择,PHP对Redis的支持已经非常成熟:
| 客户端 | 特点 | 适用场景 |
|---|---|---|
| phpredis | C扩展,性能最好 | 高并发生产环境 |
| Predis | 纯PHP实现,功能全 | 开发环境/特殊需求 |
| Laravel封装 | 简洁的ORM风格 | Laravel项目 |
我个人的选择标准:
在处理批量操作时,网络往返时间(RTT)会成为瓶颈。Redis管道可以将多个命令一次性发送:
php复制$pipe = $redis->pipeline();
for ($i = 0; $i < 1000; $i++) {
$pipe->set("key:$i", $value);
}
$pipe->exec();
实测结果:插入1000条数据
当我们需要先读取再写入时,Redis的单线程模型+Lua脚本可以完美解决竞态条件。比如实现访问计数器:
php复制$script = '
local current = redis.call("GET", KEYS[1])
if current then
return redis.call("INCR", KEYS[1])
else
return redis.call("SET", KEYS[1], 1)
end
';
$redis->eval($script, ['page_views'], 1);
Redis默认配置可能造成内存浪费,这是我的调优经验:
根据业务选择合适的数据类型
调整内存分配策略
ini复制maxmemory 4gb
maxmemory-policy allkeys-lru
ini复制hash-max-ziplist-entries 512
hash-max-ziplist-value 64
我们曾经因为大量key同时过期导致数据库瞬间过载。现在的解决方案:
php复制$expire = 3600 + rand(0, 300); // 1小时±5分钟
某个明星商品页面导致单个Redis节点CPU飙升至100%。最终通过以下方式解决:
发现一个2MB的Hash导致查询变慢,现在我们的监控方案:
bash复制redis-cli --bigkeys
# 定期扫描大Key
在最近的压力测试中(PHP 8.1 + Redis 6.2 vs Memcached 1.6):
| 测试项 | Redis | Memcached |
|---|---|---|
| SET操作QPS | 125,000 | 98,000 |
| GET操作QPS | 145,000 | 135,000 |
| 内存占用 | 较高 | 较低 |
| 数据结构 | 丰富 | 单一 |
| 持久化 | 支持 | 不支持 |
| 集群 | 原生支持 | 需要第三方工具 |
选择建议:
虽然Redis目前是PHP分布式缓存的最佳选择,但也要关注新兴技术:
我在测试环境中尝试过KeyDB,在多核服务器上确实能更好地利用CPU资源。但对于大多数PHP项目来说,原版Redis仍然是当前最稳妥的选择。