架构设计
- 资源插件化:
_initMemcached() 在 Bootstrap 中创建 Zend_Cache 实例,注入 Zend_Registry 全局访问。
- 双后端支持:Core 前端 + Memcached 后端,配置生命周期、压缩、序列化。
- 标签清理:利用
Zend_Cache 标签机制实现分组失效(如 clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, ['user_list']))。
Bootstrap.php 集成代码
<?php
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
protected function _initMemcached()
{
$frontendOptions = [
'lifetime' => 1800, // 30 分钟默认 TTL
'automatic_serialization' => true, // 自动 serialize/unserialize
'automatic_cleaning_factor' => 10, // 10% 概率触发清理
'write_control' => true, // 校验写入
];
$backendOptions = [
'servers' => [
['host' => '127.0.0.1', 'port' => 11211, 'weight' => 100],
// 多台从库可添加 weight 实现负载均衡
],
'compression' => false, // Memcached 自带压缩,关闭避免双重
'compatibility' => false, // 关闭兼容模式,启用二进制协议
];
// 创建缓存实例
$cache = Zend_Cache::factory('Core', 'Memcached', $frontendOptions, $backendOptions);
// 注册到全局注册表
Zend_Registry::set('cache', $cache);
// 可选:设置默认标签前缀
$cache->setOption('cache_id_prefix', 'myapp_');
return $cache;
}
}
Controller/Service 中使用
<?php
class IndexController extends Zend_Controller_Action
{
public function indexAction()
{
$cache = Zend_Registry::get('cache');
// 场景 1:友情链接列表,标签 'link_list'
if (!$links = $cache->load('link_list')) {
$model = new Application_Model_Link();
$links = $model->fetchAll(['status' => 1], 'sort ASC');
$cache->save($links, 'link_list', ['link_list']); // 绑定标签
}
$this->view->links = $links;
// 场景 2:用户信息,键 'user_1001',标签 'user_1001'
$userId = 1001;
$cacheKey = "user_{$userId}";
if (!$user = $cache->load($cacheKey)) {
$user = (new Application_Model_User())->find($userId);
$cache->save($user, $cacheKey, ["user_{$userId}"]);
}
$this->view->user = $user;
}
// 后台更新链接后,清理标签
public function adminUpdateLinkAction()
{
// ... 更新逻辑 ...
$cache = Zend_Registry::get('cache');
$cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, ['link_list']);
$this->_helper->json(['status' => 'ok']);
}
}
进阶:缓存键命名规范与防穿透
<?php
class CacheKey
{
// 统一格式:模块:实体:动作:标识
public static function user(int $id): string { return "user:info:$id"; }
public static function userList(array $filters): string {
ksort($filters);
return 'user:list:' . md5(http_build_query($filters));
}
public static function article(int $id): string { return "article:detail:$id"; }
}
// 使用
$key = CacheKey::user(1001);
if (!$user = $cache->load($key)) {
// 防缓存穿透:设置空值占位,短 TTL
$cache->save(null, $key, [], 60); // 60 秒空值缓存
$user = $model->find(1001);
if ($user) $cache->save($user, $key, ['user_1001'], 1800);
}
监控与运维
| 指标 | 获取方式 | 告警阈值 |
| 命中率 | stats get_hits / (get_hits + get_misses) | < 80% |
| 内存使用率 | stats bytes / limit_maxbytes | > 85% |
| 驱逐数 | stats evictions | 持续增长 |
| 连接数 | stats curr_connections | > 80% maxconns |
# 监控脚本片段
$mem = new Memcached();
$mem->addServer('127.0.0.1', 11211);
$stats = $mem->getStats();
$hitRate = $stats['get_hits'] / ($stats['get_hits'] + $stats['get_misses']);
常见问题排查
| 现象 | 原因 | 解决 |
| save/load 返回 false | Memcached 服务未启动/防火墙/主机名解析失败 | telnet 11211 测试连通性,检查 /etc/hosts |
| 数据不一致(主从不同步) | 多台 Memcached 无复制机制 | 仅作缓存层,接受短暂不一致;关键数据落 Redis/DB |
| 大 Value 存储失败 | 超过 1MB 限制(默认) | 调大 -I 启动参数,或拆分/压缩 |
| PHP 扩展版本不匹配 | pecl/memcached vs pecl/memcache 混淆 | 统一用 Memcached (libmemcached),php -m | grep memcached 验证 |
从 ZF1 迁移到 Laminas/现代框架的等价实现
<?php
// Laminas / Mezzio / Symfony 等现代框架:ServiceManager 工厂
use Laminas\Cache\StorageFactory;
use Laminas\Cache\Storage\Adapter\Memcached;
use Laminas\Cache\Storage\Plugin\Serializer;
$cache = StorageFactory::factory([
'adapter' => [
'name' => Memcached::class,
'options' => [
'servers' => [['127.0.0.1', 11211]],
'liboptions' => [
Memcached::OPT_BINARY_PROTOCOL => true,
Memcached::OPT_COMPRESSION => false,
],
],
],
'plugins' => [
'serializer' => Serializer::class,
],
]);
// 容器注册
$container->setService('cache', $cache);