43 lines
14 KiB
JavaScript
43 lines
14 KiB
JavaScript
import{_ as a,o as e,c as i,a2 as n}from"./chunks/framework.BbmSSMjL.js";const k=JSON.parse('{"title":"4.8 缓存架构设计","description":"","frontmatter":{},"headers":[],"relativePath":"zh/architecture/cache.md","filePath":"zh/architecture/cache.md"}'),t={name:"zh/architecture/cache.md"};function p(l,s,d,c,h,r){return e(),i("div",null,[...s[0]||(s[0]=[n(`<h1 id="_4-8-缓存架构设计" tabindex="-1">4.8 缓存架构设计 <a class="header-anchor" href="#_4-8-缓存架构设计" aria-label="Permalink to "4.8 缓存架构设计""></a></h1><div class="tip custom-block"><p class="custom-block-title">概述</p><p>项目采用两级缓存架构:请求级缓存(内存)+ 持久缓存(Cache 门面,默认 Redis),大幅减少数据库查询;数据变更时由 Observer 自动失效对应缓存,保证一致性。</p></div><h2 id="两级缓存架构" tabindex="-1">两级缓存架构 <a class="header-anchor" href="#两级缓存架构" aria-label="Permalink to "两级缓存架构""></a></h2><div class="language-text vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">text</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span>┌─────────────────────────────────────────────────────────┐</span></span>
|
||
<span class="line"><span>│ 第一级:请求级缓存(static 变量) │</span></span>
|
||
<span class="line"><span>│ │</span></span>
|
||
<span class="line"><span>│ 生命周期:单次 HTTP 请求 │</span></span>
|
||
<span class="line"><span>│ 存储位置:PHP 内存(static $cache) │</span></span>
|
||
<span class="line"><span>│ 速度:极快(纳秒级) │</span></span>
|
||
<span class="line"><span>│ │</span></span>
|
||
<span class="line"><span>│ 同一请求内多次查询同一数据,只查一次库或缓存 │</span></span>
|
||
<span class="line"><span>└──────────────────────────┬──────────────────────────────┘</span></span>
|
||
<span class="line"><span> │ 未命中</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span>┌─────────────────────────────────────────────────────────┐</span></span>
|
||
<span class="line"><span>│ 第二级:持久缓存(cache() 门面 / Redis) │</span></span>
|
||
<span class="line"><span>│ │</span></span>
|
||
<span class="line"><span>│ 生命周期:跨请求复用 │</span></span>
|
||
<span class="line"><span>│ 存储位置:Redis(CACHE_STORE=redis,必需组件) │</span></span>
|
||
<span class="line"><span>│ 速度:快(毫秒级) │</span></span>
|
||
<span class="line"><span>│ 有效期:1 小时(3600 秒) │</span></span>
|
||
<span class="line"><span>│ │</span></span>
|
||
<span class="line"><span>│ 跨请求复用,减少数据库压力 │</span></span>
|
||
<span class="line"><span>└──────────────────────────┬──────────────────────────────┘</span></span>
|
||
<span class="line"><span> │ 未命中</span></span>
|
||
<span class="line"><span> ▼</span></span>
|
||
<span class="line"><span>┌─────────────────────────────────────────────────────────┐</span></span>
|
||
<span class="line"><span>│ 数据库查询 │</span></span>
|
||
<span class="line"><span>│ │</span></span>
|
||
<span class="line"><span>│ 查询后同时写入两级缓存 │</span></span>
|
||
<span class="line"><span>└─────────────────────────────────────────────────────────┘</span></span></code></pre></div><h2 id="使用两级缓存的服务" tabindex="-1">使用两级缓存的服务 <a class="header-anchor" href="#使用两级缓存的服务" aria-label="Permalink to "使用两级缓存的服务""></a></h2><table tabindex="0"><thead><tr><th>服务</th><th>缓存键格式</th><th>说明</th></tr></thead><tbody><tr><td><code>DictCacheService</code></td><td><code>dict_{code}</code>(前缀 <code>dict_</code>)</td><td>字典项映射(<code>CACHE_PREFIX = 'dict_'</code>,TTL 见 <code>CACHE_TTL</code>)</td></tr><tr><td><code>MenuService</code> 等</td><td>权限/菜单缓存</td><td>角色权限列表(配合 Observer 失效)</td></tr></tbody></table><h2 id="缓存一致性-Observer-自动失效" tabindex="-1">缓存一致性:Observer 自动失效 <a class="header-anchor" href="#缓存一致性-Observer-自动失效" aria-label="Permalink to "缓存一致性:Observer 自动失效""></a></h2><p>数据变更时由 Eloquent Observer 精确失效相关缓存(注册于 <code>AppServiceProvider</code>):</p><table tabindex="0"><thead><tr><th>Observer</th><th>监听模型</th><th>触发动作</th></tr></thead><tbody><tr><td><code>DictItemObserver</code></td><td>字典项</td><td>清除所属字典缓存(<code>dict_{code}</code>)</td></tr><tr><td><code>MenuObserver</code></td><td>菜单</td><td>清除菜单/权限相关缓存</td></tr><tr><td><code>RoleMenuObserver</code></td><td>角色-菜单关系</td><td>清除角色权限缓存</td></tr></tbody></table><div class="language-text vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">text</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span>字典项变更 → DictItemObserver → DictCacheService::clearCache(code) → 下次读取回源重建</span></span>
|
||
<span class="line"><span>菜单变更 → MenuObserver → 权限缓存失效 → 前端刷新菜单后生效</span></span></code></pre></div><div class="tip custom-block"><p class="custom-block-title">设计要点</p><p>缓存写入发生在"读取回源"时,失效只做 <code>forget</code> 不做主动重建——<strong>删除缓存比更新缓存更安全</strong>,避免并发下旧数据回写。</p></div><h2 id="缓存驱动配置" tabindex="-1">缓存驱动配置 <a class="header-anchor" href="#缓存驱动配置" aria-label="Permalink to "缓存驱动配置""></a></h2><p><code>.env</code> 配置:</p><div class="language-ini vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ini</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"># Redis 缓存(项目默认,必需)</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">CACHE_STORE</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">=</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;">redis</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">REDIS_CLIENT</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">=</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;">phpredis</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">REDIS_HOST</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">=</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;">127.0.0.1</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">REDIS_PORT</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">=</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;">6379</span></span>
|
||
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">REDIS_PASSWORD</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">=</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;">null </span><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"># 设置了 requirepass 时必须同步</span></span></code></pre></div><div class="warning custom-block"><p class="custom-block-title">不要改回 database/file 驱动</p><ul><li><code>CACHE_STORE=database</code>:数据库脚本<strong>不含</strong> Laravel <code>cache</code> 表,会直接报表不存在;</li><li><code>file</code> 驱动:多进程/多服务器环境下无法共享,且本项目 JWT 会话、权限缓存均依赖 Redis;</li><li>会话 / 缓存 / 队列三者统一使用 Redis(<code>SESSION_DRIVER</code> / <code>CACHE_STORE</code> / <code>QUEUE_CONNECTION</code>)。</li></ul></div><h2 id="缓存清除" tabindex="-1">缓存清除 <a class="header-anchor" href="#缓存清除" aria-label="Permalink to "缓存清除""></a></h2><div class="language-php vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">php</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;">// 清除指定字典缓存(Observer 已自动调用,此处为手动兜底)</span></span>
|
||
<span class="line"><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#998418;--shiki-light-font-style:inherit;">DictCacheService</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">::</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">clearCache</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">gender</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">'</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">);</span></span>
|
||
<span class="line"></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;">// 清除所有字典缓存</span></span>
|
||
<span class="line"><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#998418;--shiki-light-font-style:inherit;">DictCacheService</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">::</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">clearAllCache</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">();</span></span></code></pre></div><p>对应接口与命令:</p><div class="language-text vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">text</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span>GET /api/dict/refreshCache 刷新字典缓存(需认证)</span></span>
|
||
<span class="line"><span>php artisan dict:clear-cache 命令行清除字典缓存</span></span>
|
||
<span class="line"><span>php artisan config:clear 清除配置缓存(修改 .env 后)</span></span>
|
||
<span class="line"><span>php artisan cache:clear 清除全部应用缓存</span></span></code></pre></div><h2 id="Redis-使用全景" tabindex="-1">Redis 使用全景 <a class="header-anchor" href="#Redis-使用全景" aria-label="Permalink to "Redis 使用全景""></a></h2><table tabindex="0"><thead><tr><th>用途</th><th>使用方</th><th>说明</th></tr></thead><tbody><tr><td>JWT 双令牌会话</td><td><code>JwtService</code></td><td>refresh_token 存 Redis(支持踢下线、改密全端失效、一次性消费)</td></tr><tr><td>字典缓存</td><td><code>DictCacheService</code></td><td>两级缓存第二级</td></tr><tr><td>权限/菜单缓存</td><td><code>Authenticate</code> 中间件 + Observer</td><td>角色权限列表</td></tr><tr><td>验证码</td><td><code>CaptchaService</code></td><td>图形验证码存取 + IP 限流</td></tr><tr><td>会话/队列</td><td>Laravel 驱动</td><td><code>SESSION_DRIVER</code> / <code>QUEUE_CONNECTION</code></td></tr></tbody></table><p>连接验证:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">php</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;"> artisan</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;"> tinker</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#A65E2B;"> --execute=</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">"</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">try { \\Illuminate\\Support\\Facades\\Redis::setex('test:key', 10, 'ok'); echo 'OK: '.\\Illuminate\\Support\\Facades\\Redis::get('test:key'); } catch (\\Throwable </span><span style="--shiki-dark:#F286C4;--shiki-light:#A65E2B;">\\$</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">e) { echo </span><span style="--shiki-dark:#F286C4;--shiki-light:#A65E2B;">\\$</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">e->getMessage(); }</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">"</span></span>
|
||
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"># 输出 OK: ok 即连接正常</span></span></code></pre></div>`,23)])])}const g=a(t,[["render",p]]);export{k as __pageData,g as default};
|