# ShopXO 缓存速查手册

## 核心结论

| 命题 | 结论 |
|------|------|
| 数据库查询自动缓存？ | ❌ 否，必须手动显式包裹 |
| 哪些查询被缓存？ | 仅代码中用 `MyCache()` / `cache()` 包装的 |
| `Db::name('x')->find()` 自动走 Redis？ | ❌ 直击 DB，无中间层 |
| 缓存驱动切换 | 由后台 `common_data_is_use_redis_cache` 控制（0=file, 1=redis） |
| Redis 参数来源 | 从数据库配置表读取（host/port/password） |

---

## 缓存调用方式

### ShopXO 自有的 `MyCache()`

```php
// 读取（为空触发重新查询）
$data = MyCache($key);

// 写入（第三参数 = 秒数 expire）
MyCache($key, $data, 3600);

// 删除
MyCache($key, null);
```

### ThinkPHP 原生 `cache()`

```php
cache($key, $value, $expire_seconds);
cache($key, null);  // 删除
```

---

## vr_ticket 插件缓存策略

### 缓存场景

| 数据 | 缓存时间 | 原因 |
|------|---------|------|
| 座位模板（vr_seat_templates） | 86400s（1天） | 静态数据，变更少 |
| 商品座位图渲染数据 | 3600s（1小时） | 商品信息不频繁改 |
| 核销记录列表 | 300s（5分钟） | 需要一定实时性 |
| 观演人信息 | ❌ 不缓存 | 隐私数据 |
| 座位实时余量 | ❌ 不缓存 | 强一致性要求，走 DB |

### 缓存 Key 规范

```
vrticket:seat_template_{id}      # 单个模板
vrticket:seat_templates_list     # 模板列表
vrticket:goods_seat_map_{goods_id}  # 商品座位图
vrticket:verifications_list      # 核销记录列表
```

### 数据变更时主动失效

```php
// 模板更新后
cache('vrticket:seat_template_' . $id, null);
cache('vrticket:seat_templates_list', null);

// 核销记录新增后
cache('vrticket:verifications_list', null);
```

---

## 配置说明（config/cache.php）

```php
'default' => (MyFileConfig('common_data_is_use_redis_cache','',0,true) == 1) ? 'redis' : 'file',

'stores' => [
    'redis' => [
        'host'     => MyFileConfig('common_cache_data_redis_host','','127.0.0.1',true),
        'port'     => MyFileConfig('common_cache_data_redis_port','',6379,true),
        'password' => MyFileConfig('common_cache_data_redis_password','','',true),
        'expire'   => intval(MyFileConfig('common_cache_data_redis_expire','',0,true)),
        'prefix'   => MyFileConfig('common_cache_data_redis_prefix','','redis_shopxo',true),
    ],
]
```

- 全局 `expire` = 0 表示永久（受实际 per-key expire 覆盖）
- ShopXO 后台可配置 `common_data_is_use_cache` 全局开关（debug 模式下自动 bypass）

---

## 防坑提示

1. **Debug 模式绕过缓存**：`MyEnv('app_debug')` 为 true 时所有 `MyCache()` 直接 miss
2. **全局开关**：`MyC('common_data_is_use_cache') != 1` 时全部走 DB
3. **超卖风险**：座位状态查询禁止缓存，必须实时查 DB + `FOR UPDATE SKIP LOCKED`
4. **MyCache 静态复用**：同一请求内同一 key 只调一次底层 `cache()`（in-request 内存缓存）

---

## 快速上手模板

```php
// 读缓存
$key = 'vrticket:seat_template_' . $templateId;
$data = MyCache($key);
if ($data === null) {
    $data = Db::name('SeatTemplate')->where(['id'=>$templateId])->find();
    MyCache($key, $data, 86400);
}

// 写操作后清缓存
cache('vrticket:seat_template_' . $templateId, null);
```