Files
rxthinkcmf-wiki/RXThinkCMF_Laravel13_EleVue/.vitepress/dist/assets/zh_architecture_cache.md.BcDBCO52.lean.js
T
2026-10-25 18:52:36 +08:00

43 lines
14 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import{_ as a,o as e,c as i,a2 as n}from"./chunks/framework.B-QBKwc1.js";const k=JSON.parse('{"title":"缓存架构设计","description":"项目采用两级缓存架构:请求级缓存(内存)+ 持久缓存(Cache 门面,默认 Redis),大幅减少数据库查询;数据变更时由 Observer 自动失效对应缓存,保证一致性。","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 &quot;4.8 缓存架构设计&quot;">​</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 &quot;两级缓存架构&quot;">​</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 &quot;使用两级缓存的服务&quot;">​</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 = &#39;dict_&#39;</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 &quot;缓存一致性:Observer 自动失效&quot;">​</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>缓存写入发生在&quot;读取回源&quot;时,失效只做 <code>forget</code> 不做主动重建——<strong>删除缓存比更新缓存更安全</strong>,避免并发下旧数据回写。</p></div><h2 id="缓存驱动配置" tabindex="-1">缓存驱动配置 <a class="header-anchor" href="#缓存驱动配置" aria-label="Permalink to &quot;缓存驱动配置&quot;">​</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 &quot;缓存清除&quot;">​</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;">&#39;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">gender</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</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 &quot;Redis 使用全景&quot;">​</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;">&quot;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">try { \\Illuminate\\Support\\Facades\\Redis::setex(&#39;test:key&#39;, 10, &#39;ok&#39;); echo &#39;OK: &#39;.\\Illuminate\\Support\\Facades\\Redis::get(&#39;test:key&#39;); } 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-&gt;getMessage(); }</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&quot;</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};