数组缓存预热是将高频低变的只读数据(如状态码、字典、配置)批量加载至内存数组,启动时一次性构建并全局复用;适合小规模单机场景,需控制数据量、避免大对象、提供手动刷新机制。
用数组实现缓存预热,核心是“提前把高频、低变的数据装进内存数组,等请求来时直接取,不查库”。它轻量、零依赖、适合小规模或单机场景(比如后台管理页的配置项、系统常量、静态商品分类),不追求分布式一致性,但要讲清楚怎么装、怎么取、怎么更新。
明确哪些数据适合数组缓存预热
不是所有数据都适合——数组缓存本质是进程内常驻内存,重启即丢,所以只预热:
- 访问频率高、更新频率极低的只读数据(如:支付状态码映射表、省份城市字典、网站开关配置)
- 总量可控(几百到几千条),不会撑爆内存
- 无用户维度隔离需求(即所有请求共用同一份数据)
- 不需要过期淘汰机制,或可接受手动/启动时全量刷新
用PHP数组实现批量加载与预热
以加载“商品分类树”为例,从数据库一次查出全部分类,转成嵌套数组并挂载为全局/静态变量:
// config/category_cache.php
function preloadCategoryTree(): array
{
// 模拟批量查询(实际应走PDO/ORM)
$rows = [
['id' => 1, 'name' => '手机', 'parent_id' => 0],
['id' => 2, 'name' => '电脑', 'parent_id' => 0],
['id' => 3, 'name' => 'iPhone', 'parent_id' => 1],
['id' => 4, 'name' => '华为', 'parent_id' => 1],
['id' => 5, 'name' => 'MacBook', 'parent_id' => 2],
];
// 构建树形结构(简单版)
$tree = [];
$map = [];
foreach ($rows as $row) {
$map[$row['id']] = $row + ['children' => []];
}
foreach ($rows as $row) {
if ($row['parent_id'] == 0) {
$tree[] = &$map[$row['id']];
} else {
$map[$row['parent_id']]['children'][] = &$map[$row['id']];
}
}
return $tree;
}
// 启动时执行(如在框架 boot 或入口文件中)
$CATEGORY_TREE = preloadCategoryTree();
后续业务代码中直接使用:$CATEGORY_TREE,无需重复查库。
封装成可复用的轻量缓存类
避免满屏全局变量,封装一个简单数组缓存管理器:
class SimpleArrayCache
{
private static array $data = [];
public static function set(string $key, mixed $value): void
{
self::$data[$key] = $value;
}
public static function get(string $key, mixed $default = null): mixed
{
return self::$data[$key] ?? $default;
}
public static function has(string $key): bool
{
return isset(self::$data[$key]);
}
// 批量预热入口
public static function warmUp(array $preloadMap): void
{
foreach ($preloadMap as $key => $loader) {
if (is_callable($loader)) {
self::set($key, $loader());
}
}
}
}
// 预热调用(放在应用初始化处)
SimpleArrayCache::warmUp([
'admin_roles' => fn() => [['id'=>1,'name'=>'超级管理员']],
'site_config' => fn() => ['title'=>'我的商城','icp'=>'京ICP备xxx'],
'pay_status' => fn() => [0=>'待支付', 1=>'已付款', 2=>'已退款'],
]);
这样既保持了数组的简洁性,又具备扩展性和可维护性。
注意边界与实战提醒
数组缓存预热虽简单,但容易踩坑:
- 不要缓存大对象(如含二进制、长文本、资源句柄),会显著拖慢启动和GC
- 避免在循环中反复
array_merge或array_push大量数据,改用预分配或引用赋值 - 若数据需更新,提供
refresh()方法+信号触发(如监听Redis Pub/Sub消息),而不是靠定时轮询 - 上线前务必压测:启动耗时、内存占用、首次请求响应时间,确认没有成为瓶颈
对中小项目来说,这种基于数组的预热方式够用、可控、易调试,比引入Redis还快一步落地。










