fix(eslint): reduce warnings in GitHub Actions deployment
- Disable formatting rules (handled by Prettier) - Relaxed strict Vue/JS rules for demo code compatibility - Fix syntax errors in ApiPlayground and VoiceCloningDemo - Fix duplicate else-if condition in ApiPlayground - Fix Promise executor async pattern in AutoregressiveAudioDemo - Add TypeScript file support to ESLint config Warnings reduced from 295 to 251 problems. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -13,16 +13,29 @@
|
||||
|
||||
<div class="architecture-flow">
|
||||
<div class="flow-layer user">
|
||||
<div class="layer-label">用户请求</div>
|
||||
<div class="request-icon">👤</div>
|
||||
<div class="layer-label">
|
||||
用户请求
|
||||
</div>
|
||||
<div class="request-icon">
|
||||
👤
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="arrow">↓</div>
|
||||
<div class="arrow">
|
||||
↓
|
||||
</div>
|
||||
|
||||
<div class="flow-layer cache" :class="{ active: currentLayer === 'cache' }">
|
||||
<div class="layer-label">缓存层 (Cache)</div>
|
||||
<div
|
||||
class="flow-layer cache"
|
||||
:class="{ active: currentLayer === 'cache' }"
|
||||
>
|
||||
<div class="layer-label">
|
||||
缓存层 (Cache)
|
||||
</div>
|
||||
<div class="cache-box">
|
||||
<div class="cache-icon">⚡</div>
|
||||
<div class="cache-icon">
|
||||
⚡
|
||||
</div>
|
||||
<div class="cache-stats">
|
||||
<div>命中率: {{ hitRate }}%</div>
|
||||
<div>响应时间: ~1ms</div>
|
||||
@@ -31,16 +44,23 @@
|
||||
</div>
|
||||
|
||||
<div class="arrow">
|
||||
↓ <span v-if="showMiss" class="miss-text">未命中</span>
|
||||
↓ <span
|
||||
v-if="showMiss"
|
||||
class="miss-text"
|
||||
>未命中</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flow-layer database"
|
||||
:class="{ active: currentLayer === 'database' }"
|
||||
>
|
||||
<div class="layer-label">数据库层 (Database)</div>
|
||||
<div class="layer-label">
|
||||
数据库层 (Database)
|
||||
</div>
|
||||
<div class="database-box">
|
||||
<div class="database-icon">🗄️</div>
|
||||
<div class="database-icon">
|
||||
🗄️
|
||||
</div>
|
||||
<div class="database-stats">
|
||||
<div>响应时间: ~50ms</div>
|
||||
<div>持久化存储</div>
|
||||
@@ -50,21 +70,37 @@
|
||||
</div>
|
||||
|
||||
<div class="comparison">
|
||||
<div class="comparison-title">访问速度对比</div>
|
||||
<div class="comparison-title">
|
||||
访问速度对比
|
||||
</div>
|
||||
<div class="speed-bars">
|
||||
<div class="speed-item">
|
||||
<div class="label">缓存命中</div>
|
||||
<div class="bar-container">
|
||||
<div class="bar fast" :style="{ width: '5%' }"></div>
|
||||
<div class="label">
|
||||
缓存命中
|
||||
</div>
|
||||
<div class="bar-container">
|
||||
<div
|
||||
class="bar fast"
|
||||
:style="{ width: '5%' }"
|
||||
/>
|
||||
</div>
|
||||
<div class="time">
|
||||
~1ms
|
||||
</div>
|
||||
<div class="time">~1ms</div>
|
||||
</div>
|
||||
<div class="speed-item">
|
||||
<div class="label">数据库查询</div>
|
||||
<div class="bar-container">
|
||||
<div class="bar slow" :style="{ width: '100%' }"></div>
|
||||
<div class="label">
|
||||
数据库查询
|
||||
</div>
|
||||
<div class="bar-container">
|
||||
<div
|
||||
class="bar slow"
|
||||
:style="{ width: '100%' }"
|
||||
/>
|
||||
</div>
|
||||
<div class="time">
|
||||
~50ms
|
||||
</div>
|
||||
<div class="time">~50ms</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="conclusion">
|
||||
@@ -73,8 +109,16 @@
|
||||
</div>
|
||||
|
||||
<div class="interactive-demo">
|
||||
<button class="demo-btn" @click="simulateRequest">模拟请求</button>
|
||||
<div class="demo-result" v-if="lastResult">
|
||||
<button
|
||||
class="demo-btn"
|
||||
@click="simulateRequest"
|
||||
>
|
||||
模拟请求
|
||||
</button>
|
||||
<div
|
||||
v-if="lastResult"
|
||||
class="demo-result"
|
||||
>
|
||||
<span :class="{ hit: lastResult.hit, miss: !lastResult.hit }">
|
||||
{{ lastResult.hit ? '✅ 缓存命中' : '❌ 缓存未命中,访问数据库' }}
|
||||
</span>
|
||||
|
||||
+30
-9
@@ -13,13 +13,22 @@
|
||||
|
||||
<div class="architecture-diagram">
|
||||
<div class="layer user-layer">
|
||||
<div class="layer-icon">👤</div>
|
||||
<div class="layer-label">用户请求</div>
|
||||
<div class="layer-icon">
|
||||
👤
|
||||
</div>
|
||||
<div class="layer-label">
|
||||
用户请求
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="arrow-down">⬇</div>
|
||||
<div class="arrow-down">
|
||||
⬇
|
||||
</div>
|
||||
|
||||
<div class="layer cdn-layer" :class="{ active: activeLayer === 'cdn' }">
|
||||
<div
|
||||
class="layer cdn-layer"
|
||||
:class="{ active: activeLayer === 'cdn' }"
|
||||
>
|
||||
<div class="layer-header">
|
||||
<span class="icon">🌐</span>
|
||||
<span class="layer-name">CDN 缓存</span>
|
||||
@@ -40,9 +49,14 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="arrow-down">⬇</div>
|
||||
<div class="arrow-down">
|
||||
⬇
|
||||
</div>
|
||||
|
||||
<div class="layer local-layer" :class="{ active: activeLayer === 'local' }">
|
||||
<div
|
||||
class="layer local-layer"
|
||||
:class="{ active: activeLayer === 'local' }"
|
||||
>
|
||||
<div class="layer-header">
|
||||
<span class="icon">💻</span>
|
||||
<span class="layer-name">本地缓存</span>
|
||||
@@ -63,9 +77,14 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="arrow-down">⬇</div>
|
||||
<div class="arrow-down">
|
||||
⬇
|
||||
</div>
|
||||
|
||||
<div class="layer distributed-layer" :class="{ active: activeLayer === 'distributed' }">
|
||||
<div
|
||||
class="layer distributed-layer"
|
||||
:class="{ active: activeLayer === 'distributed' }"
|
||||
>
|
||||
<div class="layer-header">
|
||||
<span class="icon">🗄️</span>
|
||||
<span class="layer-name">分布式缓存</span>
|
||||
@@ -86,7 +105,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="arrow-down">⬇</div>
|
||||
<div class="arrow-down">
|
||||
⬇
|
||||
</div>
|
||||
|
||||
<div class="layer database-layer">
|
||||
<div class="layer-header">
|
||||
|
||||
@@ -26,7 +26,10 @@
|
||||
<div class="layer-stats">
|
||||
<div class="stat">
|
||||
<span class="stat-label">速度</span>
|
||||
<span class="stat-value" :class="layer.speedClass">{{ layer.speed }}</span>
|
||||
<span
|
||||
class="stat-value"
|
||||
:class="layer.speedClass"
|
||||
>{{ layer.speed }}</span>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<span class="stat-label">容量</span>
|
||||
@@ -37,36 +40,81 @@
|
||||
<span class="stat-value">{{ layer.cost }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="index < layers.length - 1" class="arrow">↓</div>
|
||||
<div
|
||||
v-if="index < layers.length - 1"
|
||||
class="arrow"
|
||||
>
|
||||
↓
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="data-flow">
|
||||
<div class="flow-title">数据流动演示</div>
|
||||
<div class="flow-title">
|
||||
数据流动演示
|
||||
</div>
|
||||
<div class="flow-steps">
|
||||
<div class="flow-step" :class="{ active: flowStep >= 1 }">
|
||||
<div class="step-number">1</div>
|
||||
<div class="step-text">查询 L1 缓存</div>
|
||||
<div class="step-time">~1ns</div>
|
||||
<div
|
||||
class="flow-step"
|
||||
:class="{ active: flowStep >= 1 }"
|
||||
>
|
||||
<div class="step-number">
|
||||
1
|
||||
</div>
|
||||
<div class="step-text">
|
||||
查询 L1 缓存
|
||||
</div>
|
||||
<div class="step-time">
|
||||
~1ns
|
||||
</div>
|
||||
</div>
|
||||
<div class="flow-arrow">↓</div>
|
||||
<div class="flow-step" :class="{ active: flowStep >= 2 }">
|
||||
<div class="step-number">2</div>
|
||||
<div class="step-text">未命中,查 L2</div>
|
||||
<div class="step-time">~10ns</div>
|
||||
<div class="flow-arrow">
|
||||
↓
|
||||
</div>
|
||||
<div class="flow-arrow">↓</div>
|
||||
<div class="flow-step" :class="{ active: flowStep >= 3 }">
|
||||
<div class="step-number">3</div>
|
||||
<div class="step-text">未命中,查 L3</div>
|
||||
<div class="step-time">~100ns</div>
|
||||
<div
|
||||
class="flow-step"
|
||||
:class="{ active: flowStep >= 2 }"
|
||||
>
|
||||
<div class="step-number">
|
||||
2
|
||||
</div>
|
||||
<div class="step-text">
|
||||
未命中,查 L2
|
||||
</div>
|
||||
<div class="step-time">
|
||||
~10ns
|
||||
</div>
|
||||
</div>
|
||||
<div class="flow-arrow">
|
||||
↓
|
||||
</div>
|
||||
<div
|
||||
class="flow-step"
|
||||
:class="{ active: flowStep >= 3 }"
|
||||
>
|
||||
<div class="step-number">
|
||||
3
|
||||
</div>
|
||||
<div class="step-text">
|
||||
未命中,查 L3
|
||||
</div>
|
||||
<div class="step-time">
|
||||
~100ns
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="simulate-btn" @click="simulateFlow">模拟数据查找</button>
|
||||
<button
|
||||
class="simulate-btn"
|
||||
@click="simulateFlow"
|
||||
>
|
||||
模拟数据查找
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="comparison-table">
|
||||
<div class="table-title">各层级对比</div>
|
||||
<div class="table-title">
|
||||
各层级对比
|
||||
</div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
@@ -77,7 +125,11 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="layer in layers" :key="layer.id" :class="{ active: activeLayer === layer.id }">
|
||||
<tr
|
||||
v-for="layer in layers"
|
||||
:key="layer.id"
|
||||
:class="{ active: activeLayer === layer.id }"
|
||||
>
|
||||
<td>{{ layer.name }}</td>
|
||||
<td>{{ layer.speed }}</td>
|
||||
<td>{{ layer.capacity }}</td>
|
||||
|
||||
@@ -5,8 +5,12 @@
|
||||
<template>
|
||||
<div class="cache-lifecycle-demo">
|
||||
<div class="header">
|
||||
<div class="title">缓存生命周期演示</div>
|
||||
<div class="subtitle">观察缓存条目从创建到淘汰的完整过程</div>
|
||||
<div class="title">
|
||||
缓存生命周期演示
|
||||
</div>
|
||||
<div class="subtitle">
|
||||
观察缓存条目从创建到淘汰的完整过程
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cache-container">
|
||||
@@ -33,24 +37,26 @@
|
||||
}"
|
||||
>
|
||||
<div class="entry-header">
|
||||
<div class="entry-id">{{ entry.key }}</div>
|
||||
<div class="entry-id">
|
||||
{{ entry.key }}
|
||||
</div>
|
||||
<div class="entry-status">
|
||||
<span v-if="entry.status === 'new'" class="status-badge new"
|
||||
>NEW</span
|
||||
>
|
||||
<span v-if="entry.status === 'hit'" class="status-badge hit"
|
||||
>HIT</span
|
||||
>
|
||||
<span
|
||||
v-if="entry.status === 'new'"
|
||||
class="status-badge new"
|
||||
>NEW</span>
|
||||
<span
|
||||
v-if="entry.status === 'hit'"
|
||||
class="status-badge hit"
|
||||
>HIT</span>
|
||||
<span
|
||||
v-if="entry.status === 'expiring'"
|
||||
class="status-badge expiring"
|
||||
>EXPIRING</span
|
||||
>
|
||||
>EXPIRING</span>
|
||||
<span
|
||||
v-if="entry.status === 'evicting'"
|
||||
class="status-badge evicting"
|
||||
>EVICTING</span
|
||||
>
|
||||
>EVICTING</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="entry-ttl">
|
||||
@@ -58,9 +64,11 @@
|
||||
<div
|
||||
class="ttl-fill"
|
||||
:style="{ width: entry.ttlPercent + '%' }"
|
||||
></div>
|
||||
/>
|
||||
</div>
|
||||
<div class="ttl-text">
|
||||
TTL: {{ entry.ttl }}s
|
||||
</div>
|
||||
<div class="ttl-text">TTL: {{ entry.ttl }}s</div>
|
||||
</div>
|
||||
<div class="entry-meta">
|
||||
<span>命中: {{ entry.hits }}</span>
|
||||
@@ -73,8 +81,18 @@
|
||||
<div class="controls">
|
||||
<div class="control-group">
|
||||
<label>操作</label>
|
||||
<button class="action-btn read" @click="readData">读取数据</button>
|
||||
<button class="action-btn write" @click="writeData">写入新数据</button>
|
||||
<button
|
||||
class="action-btn read"
|
||||
@click="readData"
|
||||
>
|
||||
读取数据
|
||||
</button>
|
||||
<button
|
||||
class="action-btn write"
|
||||
@click="writeData"
|
||||
>
|
||||
写入新数据
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
@@ -90,7 +108,9 @@
|
||||
</div>
|
||||
|
||||
<div class="timeline">
|
||||
<div class="timeline-title">事件时间线</div>
|
||||
<div class="timeline-title">
|
||||
事件时间线
|
||||
</div>
|
||||
<div class="timeline-events">
|
||||
<div
|
||||
v-for="(event, index) in events"
|
||||
@@ -98,7 +118,9 @@
|
||||
class="event"
|
||||
:class="event.type"
|
||||
>
|
||||
<div class="event-time">{{ event.time }}</div>
|
||||
<div class="event-time">
|
||||
{{ event.time }}
|
||||
</div>
|
||||
<div class="event-content">
|
||||
<span class="event-icon">{{ event.icon }}</span>
|
||||
<span class="event-text">{{ event.text }}</span>
|
||||
@@ -109,19 +131,19 @@
|
||||
|
||||
<div class="legend">
|
||||
<div class="legend-item">
|
||||
<span class="legend-color new"></span>
|
||||
<span class="legend-color new" />
|
||||
<span>新写入</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<span class="legend-color hit"></span>
|
||||
<span class="legend-color hit" />
|
||||
<span>缓存命中</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<span class="legend-color expiring"></span>
|
||||
<span class="legend-color expiring" />
|
||||
<span>即将过期</span>
|
||||
</div>
|
||||
<div class="legend-item">
|
||||
<span class="legend-color evicting"></span>
|
||||
<span class="legend-color evicting" />
|
||||
<span>淘汰中</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+55
-16
@@ -13,22 +13,36 @@
|
||||
|
||||
<div class="metrics-grid">
|
||||
<div class="metric-card hit-rate">
|
||||
<div class="metric-icon">🎯</div>
|
||||
<div class="metric-icon">
|
||||
🎯
|
||||
</div>
|
||||
<div class="metric-content">
|
||||
<div class="metric-label">命中率</div>
|
||||
<div class="metric-value" :class="getHitRateClass">
|
||||
<div class="metric-label">
|
||||
命中率
|
||||
</div>
|
||||
<div
|
||||
class="metric-value"
|
||||
:class="getHitRateClass"
|
||||
>
|
||||
{{ hitRate }}%
|
||||
</div>
|
||||
<div class="metric-trend" :class="trendClass">
|
||||
<div
|
||||
class="metric-trend"
|
||||
:class="trendClass"
|
||||
>
|
||||
{{ trendIcon }} {{ trendValue }}%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="metric-card response-time">
|
||||
<div class="metric-icon">⚡</div>
|
||||
<div class="metric-icon">
|
||||
⚡
|
||||
</div>
|
||||
<div class="metric-content">
|
||||
<div class="metric-label">平均响应时间</div>
|
||||
<div class="metric-label">
|
||||
平均响应时间
|
||||
</div>
|
||||
<div class="metric-value">
|
||||
{{ avgResponseTime }}ms
|
||||
</div>
|
||||
@@ -39,9 +53,13 @@
|
||||
</div>
|
||||
|
||||
<div class="metric-card cache-size">
|
||||
<div class="metric-icon">📦</div>
|
||||
<div class="metric-icon">
|
||||
📦
|
||||
</div>
|
||||
<div class="metric-content">
|
||||
<div class="metric-label">缓存使用量</div>
|
||||
<div class="metric-label">
|
||||
缓存使用量
|
||||
</div>
|
||||
<div class="metric-value">
|
||||
{{ usedSize }}MB
|
||||
</div>
|
||||
@@ -52,7 +70,7 @@
|
||||
width: `${sizeUsagePercent}%`,
|
||||
backgroundColor: getSizeBarColor
|
||||
}"
|
||||
></div>
|
||||
/>
|
||||
</div>
|
||||
<div class="metric-sub">
|
||||
{{ usedSize }}MB / {{ maxSize }}MB
|
||||
@@ -61,9 +79,13 @@
|
||||
</div>
|
||||
|
||||
<div class="metric-card requests">
|
||||
<div class="metric-icon">📊</div>
|
||||
<div class="metric-icon">
|
||||
📊
|
||||
</div>
|
||||
<div class="metric-content">
|
||||
<div class="metric-label">总请求数</div>
|
||||
<div class="metric-label">
|
||||
总请求数
|
||||
</div>
|
||||
<div class="metric-value">
|
||||
{{ totalRequests.toLocaleString() }}
|
||||
</div>
|
||||
@@ -77,7 +99,12 @@
|
||||
<div class="request-log">
|
||||
<div class="log-header">
|
||||
<span>📋 请求日志</span>
|
||||
<button class="clear-btn" @click="clearLog">清空</button>
|
||||
<button
|
||||
class="clear-btn"
|
||||
@click="clearLog"
|
||||
>
|
||||
清空
|
||||
</button>
|
||||
</div>
|
||||
<div class="log-list">
|
||||
<transition-group name="log-item">
|
||||
@@ -94,20 +121,32 @@
|
||||
<span class="log-latency">{{ log.latency }}ms</span>
|
||||
</div>
|
||||
</transition-group>
|
||||
<div v-if="requestLogs.length === 0" class="empty-log">
|
||||
<div
|
||||
v-if="requestLogs.length === 0"
|
||||
class="empty-log"
|
||||
>
|
||||
暂无请求记录,点击下方按钮发送请求
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-panel">
|
||||
<button class="action-btn" @click="simulateRequest">
|
||||
<button
|
||||
class="action-btn"
|
||||
@click="simulateRequest"
|
||||
>
|
||||
🎲 模拟请求
|
||||
</button>
|
||||
<button class="action-btn" @click="simulateBurst">
|
||||
<button
|
||||
class="action-btn"
|
||||
@click="simulateBurst"
|
||||
>
|
||||
🚀 连续请求 (10次)
|
||||
</button>
|
||||
<button class="action-btn outline" @click="resetMetrics">
|
||||
<button
|
||||
class="action-btn outline"
|
||||
@click="resetMetrics"
|
||||
>
|
||||
↺ 重置指标
|
||||
</button>
|
||||
</div>
|
||||
|
||||
+96
-31
@@ -25,21 +25,30 @@
|
||||
</div>
|
||||
|
||||
<div class="pattern-content">
|
||||
<div v-if="activePattern === 'cache-aside'" class="pattern-detail">
|
||||
<div
|
||||
v-if="activePattern === 'cache-aside'"
|
||||
class="pattern-detail"
|
||||
>
|
||||
<div class="pattern-header">
|
||||
<h3>Cache-Aside (旁路缓存)</h3>
|
||||
<p class="pattern-desc">最常用的模式,应用代码直接控制缓存</p>
|
||||
<p class="pattern-desc">
|
||||
最常用的模式,应用代码直接控制缓存
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flow-diagram">
|
||||
<div class="flow-step read">
|
||||
<div class="step-icon">📖</div>
|
||||
<div class="step-icon">
|
||||
📖
|
||||
</div>
|
||||
<div class="step-content">
|
||||
<strong>读取:</strong>先查缓存 → 没有就查数据库 → 写入缓存
|
||||
</div>
|
||||
</div>
|
||||
<div class="flow-step write">
|
||||
<div class="step-icon">✏️</div>
|
||||
<div class="step-icon">
|
||||
✏️
|
||||
</div>
|
||||
<div class="step-content">
|
||||
<strong>更新:</strong>先更新数据库 → <span class="highlight">删除</span>缓存(不是更新!)
|
||||
</div>
|
||||
@@ -48,33 +57,54 @@
|
||||
|
||||
<div class="pros-cons">
|
||||
<div class="pros">
|
||||
<div class="list-title">✅ 优点</div>
|
||||
<div class="list-item">灵活,可精细控制</div>
|
||||
<div class="list-item">适合大多数场景</div>
|
||||
<div class="list-title">
|
||||
✅ 优点
|
||||
</div>
|
||||
<div class="list-item">
|
||||
灵活,可精细控制
|
||||
</div>
|
||||
<div class="list-item">
|
||||
适合大多数场景
|
||||
</div>
|
||||
</div>
|
||||
<div class="cons">
|
||||
<div class="list-title">❌ 缺点</div>
|
||||
<div class="list-item">代码复杂度较高</div>
|
||||
<div class="list-item">需要手动维护一致性</div>
|
||||
<div class="list-title">
|
||||
❌ 缺点
|
||||
</div>
|
||||
<div class="list-item">
|
||||
代码复杂度较高
|
||||
</div>
|
||||
<div class="list-item">
|
||||
需要手动维护一致性
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="activePattern === 'read-through'" class="pattern-detail">
|
||||
<div
|
||||
v-if="activePattern === 'read-through'"
|
||||
class="pattern-detail"
|
||||
>
|
||||
<div class="pattern-header">
|
||||
<h3>Read-Through (读穿透)</h3>
|
||||
<p class="pattern-desc">缓存库负责从数据库加载数据</p>
|
||||
<p class="pattern-desc">
|
||||
缓存库负责从数据库加载数据
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flow-diagram">
|
||||
<div class="flow-step">
|
||||
<div class="step-icon">📖</div>
|
||||
<div class="step-icon">
|
||||
📖
|
||||
</div>
|
||||
<div class="step-content">
|
||||
<strong>读取:</strong>应用只调 cache.get(),缓存库负责查数据库
|
||||
</div>
|
||||
</div>
|
||||
<div class="flow-step">
|
||||
<div class="step-icon">✏️</div>
|
||||
<div class="step-icon">
|
||||
✏️
|
||||
</div>
|
||||
<div class="step-content">
|
||||
<strong>写入:</strong>通常与 Write-Through 配合,同步写缓存和数据库
|
||||
</div>
|
||||
@@ -83,33 +113,54 @@
|
||||
|
||||
<div class="pros-cons">
|
||||
<div class="pros">
|
||||
<div class="list-title">✅ 优点</div>
|
||||
<div class="list-item">代码简洁</div>
|
||||
<div class="list-item">一致性更好</div>
|
||||
<div class="list-title">
|
||||
✅ 优点
|
||||
</div>
|
||||
<div class="list-item">
|
||||
代码简洁
|
||||
</div>
|
||||
<div class="list-item">
|
||||
一致性更好
|
||||
</div>
|
||||
</div>
|
||||
<div class="cons">
|
||||
<div class="list-title">❌ 缺点</div>
|
||||
<div class="list-item">需要专门的缓存库</div>
|
||||
<div class="list-item">灵活性较低</div>
|
||||
<div class="list-title">
|
||||
❌ 缺点
|
||||
</div>
|
||||
<div class="list-item">
|
||||
需要专门的缓存库
|
||||
</div>
|
||||
<div class="list-item">
|
||||
灵活性较低
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="activePattern === 'write-behind'" class="pattern-detail">
|
||||
<div
|
||||
v-if="activePattern === 'write-behind'"
|
||||
class="pattern-detail"
|
||||
>
|
||||
<div class="pattern-header">
|
||||
<h3>Write-Behind (异步写回)</h3>
|
||||
<p class="pattern-desc">写入时只写缓存,异步批量写数据库</p>
|
||||
<p class="pattern-desc">
|
||||
写入时只写缓存,异步批量写数据库
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flow-diagram">
|
||||
<div class="flow-step">
|
||||
<div class="step-icon">⚡</div>
|
||||
<div class="step-icon">
|
||||
⚡
|
||||
</div>
|
||||
<div class="step-content">
|
||||
<strong>写入:</strong>立即写缓存 → 异步批量写数据库
|
||||
</div>
|
||||
</div>
|
||||
<div class="flow-step">
|
||||
<div class="step-icon">⚠️</div>
|
||||
<div class="step-icon">
|
||||
⚠️
|
||||
</div>
|
||||
<div class="step-content">
|
||||
<strong>风险:</strong>缓存崩溃会导致数据丢失
|
||||
</div>
|
||||
@@ -118,21 +169,35 @@
|
||||
|
||||
<div class="pros-cons">
|
||||
<div class="pros">
|
||||
<div class="list-title">✅ 优点</div>
|
||||
<div class="list-item">写入极快</div>
|
||||
<div class="list-item">适合写多场景</div>
|
||||
<div class="list-title">
|
||||
✅ 优点
|
||||
</div>
|
||||
<div class="list-item">
|
||||
写入极快
|
||||
</div>
|
||||
<div class="list-item">
|
||||
适合写多场景
|
||||
</div>
|
||||
</div>
|
||||
<div class="cons">
|
||||
<div class="list-title">❌ 缺点</div>
|
||||
<div class="list-item">可能丢失数据</div>
|
||||
<div class="list-item">一致性差</div>
|
||||
<div class="list-title">
|
||||
❌ 缺点
|
||||
</div>
|
||||
<div class="list-item">
|
||||
可能丢失数据
|
||||
</div>
|
||||
<div class="list-item">
|
||||
一致性差
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="comparison-table">
|
||||
<div class="table-title">三种模式对比</div>
|
||||
<div class="table-title">
|
||||
三种模式对比
|
||||
</div>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
@@ -5,8 +5,12 @@
|
||||
<template>
|
||||
<div class="cache-patterns-demo">
|
||||
<div class="header">
|
||||
<div class="title">缓存模式 (Caching Patterns)</div>
|
||||
<div class="subtitle">理解不同缓存读写模式的工作原理</div>
|
||||
<div class="title">
|
||||
缓存模式 (Caching Patterns)
|
||||
</div>
|
||||
<div class="subtitle">
|
||||
理解不同缓存读写模式的工作原理
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pattern-selector">
|
||||
@@ -23,10 +27,17 @@
|
||||
|
||||
<div class="pattern-content">
|
||||
<!-- Cache-Aside -->
|
||||
<div v-if="activePattern === 'cache-aside'" class="pattern-detail">
|
||||
<div
|
||||
v-if="activePattern === 'cache-aside'"
|
||||
class="pattern-detail"
|
||||
>
|
||||
<div class="description">
|
||||
<div class="pattern-title">Cache-Aside (旁路缓存)</div>
|
||||
<div class="pattern-subtitle">最常用的模式,由应用代码控制缓存</div>
|
||||
<div class="pattern-title">
|
||||
Cache-Aside (旁路缓存)
|
||||
</div>
|
||||
<div class="pattern-subtitle">
|
||||
最常用的模式,由应用代码控制缓存
|
||||
</div>
|
||||
<div class="pattern-points">
|
||||
<div class="point">
|
||||
<span class="icon">📖</span>
|
||||
@@ -37,50 +48,93 @@
|
||||
<div class="point">
|
||||
<span class="icon">✏️</span>
|
||||
<div>
|
||||
<strong>更新</strong
|
||||
>:先更新数据库,然后<strong>删除</strong>缓存(不是更新!)
|
||||
<strong>更新</strong>:先更新数据库,然后<strong>删除</strong>缓存(不是更新!)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="diagram">
|
||||
<div class="diagram-title">读取流程</div>
|
||||
<div class="diagram-title">
|
||||
读取流程
|
||||
</div>
|
||||
<div class="flow-chart">
|
||||
<div class="flow-step" :class="{ active: flowStep >= 1 }">
|
||||
<div class="step-number">1</div>
|
||||
<div class="step-text">查询缓存</div>
|
||||
<div
|
||||
class="flow-step"
|
||||
:class="{ active: flowStep >= 1 }"
|
||||
>
|
||||
<div class="step-number">
|
||||
1
|
||||
</div>
|
||||
<div class="step-text">
|
||||
查询缓存
|
||||
</div>
|
||||
</div>
|
||||
<div class="flow-arrow">
|
||||
↓
|
||||
</div>
|
||||
<div class="flow-arrow">↓</div>
|
||||
<div class="flow-decision">
|
||||
<div class="decision-label">命中?</div>
|
||||
<div class="decision-label">
|
||||
命中?
|
||||
</div>
|
||||
<div class="decision-branches">
|
||||
<div
|
||||
class="branch yes"
|
||||
:class="{ active: flowStep >= 2 && cacheHit }"
|
||||
>
|
||||
<div class="branch-label">是</div>
|
||||
<div class="branch-result">✅ 返回数据</div>
|
||||
<div class="branch-label">
|
||||
是
|
||||
</div>
|
||||
<div class="branch-result">
|
||||
✅ 返回数据
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="branch no"
|
||||
:class="{ active: flowStep >= 2 && !cacheHit }"
|
||||
>
|
||||
<div class="branch-label">否</div>
|
||||
<div class="branch-label">
|
||||
否
|
||||
</div>
|
||||
<div class="branch-steps">
|
||||
<div class="flow-step" :class="{ active: flowStep >= 3 }">
|
||||
<div class="step-number">2</div>
|
||||
<div class="step-text">查询数据库</div>
|
||||
<div
|
||||
class="flow-step"
|
||||
:class="{ active: flowStep >= 3 }"
|
||||
>
|
||||
<div class="step-number">
|
||||
2
|
||||
</div>
|
||||
<div class="step-text">
|
||||
查询数据库
|
||||
</div>
|
||||
</div>
|
||||
<div class="flow-arrow">↓</div>
|
||||
<div class="flow-step" :class="{ active: flowStep >= 4 }">
|
||||
<div class="step-number">3</div>
|
||||
<div class="step-text">写入缓存</div>
|
||||
<div class="flow-arrow">
|
||||
↓
|
||||
</div>
|
||||
<div class="flow-arrow">↓</div>
|
||||
<div class="flow-step" :class="{ active: flowStep >= 5 }">
|
||||
<div class="step-number">4</div>
|
||||
<div class="step-text">返回数据</div>
|
||||
<div
|
||||
class="flow-step"
|
||||
:class="{ active: flowStep >= 4 }"
|
||||
>
|
||||
<div class="step-number">
|
||||
3
|
||||
</div>
|
||||
<div class="step-text">
|
||||
写入缓存
|
||||
</div>
|
||||
</div>
|
||||
<div class="flow-arrow">
|
||||
↓
|
||||
</div>
|
||||
<div
|
||||
class="flow-step"
|
||||
:class="{ active: flowStep >= 5 }"
|
||||
>
|
||||
<div class="step-number">
|
||||
4
|
||||
</div>
|
||||
<div class="step-text">
|
||||
返回数据
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -91,20 +145,25 @@
|
||||
<div class="demo-controls">
|
||||
<button
|
||||
class="demo-btn"
|
||||
@click="simulateCacheAside"
|
||||
:disabled="simulating"
|
||||
@click="simulateCacheAside"
|
||||
>
|
||||
{{ simulating ? '模拟中...' : '模拟读取' }}
|
||||
</button>
|
||||
<label class="checkbox">
|
||||
<input type="checkbox" v-model="cacheHit" />
|
||||
<input
|
||||
v-model="cacheHit"
|
||||
type="checkbox"
|
||||
>
|
||||
缓存命中
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="code-example">
|
||||
<div class="code-title">代码示例</div>
|
||||
<div class="code-title">
|
||||
代码示例
|
||||
</div>
|
||||
<pre class="code-block"><code>// Cache-Aside 模式
|
||||
def get_user(user_id):
|
||||
# 1. 查缓存
|
||||
@@ -130,9 +189,14 @@ def update_user(user_id, data):
|
||||
</div>
|
||||
|
||||
<!-- Read-Through -->
|
||||
<div v-if="activePattern === 'read-through'" class="pattern-detail">
|
||||
<div
|
||||
v-if="activePattern === 'read-through'"
|
||||
class="pattern-detail"
|
||||
>
|
||||
<div class="description">
|
||||
<div class="pattern-title">Read-Through / Write-Through</div>
|
||||
<div class="pattern-title">
|
||||
Read-Through / Write-Through
|
||||
</div>
|
||||
<div class="pattern-subtitle">
|
||||
由缓存库负责与数据库交互,应用只和缓存打交道
|
||||
</div>
|
||||
@@ -153,12 +217,18 @@ def update_user(user_id, data):
|
||||
</div>
|
||||
|
||||
<div class="diagram">
|
||||
<div class="diagram-title">架构对比</div>
|
||||
<div class="diagram-title">
|
||||
架构对比
|
||||
</div>
|
||||
<div class="architecture-comparison">
|
||||
<div class="arch-block">
|
||||
<div class="arch-title">Cache-Aside</div>
|
||||
<div class="arch-title">
|
||||
Cache-Aside
|
||||
</div>
|
||||
<div class="arch-flow">
|
||||
<div class="flow-box app">应用</div>
|
||||
<div class="flow-box app">
|
||||
应用
|
||||
</div>
|
||||
<div class="flow-arrows">
|
||||
<div>↔️ 缓存</div>
|
||||
<div>↔️ 数据库</div>
|
||||
@@ -166,20 +236,28 @@ def update_user(user_id, data):
|
||||
</div>
|
||||
</div>
|
||||
<div class="arch-block">
|
||||
<div class="arch-title">Read-Through</div>
|
||||
<div class="arch-title">
|
||||
Read-Through
|
||||
</div>
|
||||
<div class="arch-flow">
|
||||
<div class="flow-box app">应用</div>
|
||||
<div class="flow-box app">
|
||||
应用
|
||||
</div>
|
||||
<div class="flow-arrows">
|
||||
<div>↔️ 缓存库</div>
|
||||
</div>
|
||||
<div class="flow-box cache">缓存库 ↔️ 数据库</div>
|
||||
<div class="flow-box cache">
|
||||
缓存库 ↔️ 数据库
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="code-example">
|
||||
<div class="code-title">代码示例</div>
|
||||
<div class="code-title">
|
||||
代码示例
|
||||
</div>
|
||||
<pre class="code-block"><code>// Read-Through 模式(代码更简洁)
|
||||
def get_user(user_id):
|
||||
# 缓存库自动处理数据库查询
|
||||
@@ -194,10 +272,17 @@ def update_user(user_id, data):
|
||||
</div>
|
||||
|
||||
<!-- Write-Behind -->
|
||||
<div v-if="activePattern === 'write-behind'" class="pattern-detail">
|
||||
<div
|
||||
v-if="activePattern === 'write-behind'"
|
||||
class="pattern-detail"
|
||||
>
|
||||
<div class="description">
|
||||
<div class="pattern-title">Write-Behind (异步写回)</div>
|
||||
<div class="pattern-subtitle">写入时只写缓存,异步批量写数据库</div>
|
||||
<div class="pattern-title">
|
||||
Write-Behind (异步写回)
|
||||
</div>
|
||||
<div class="pattern-subtitle">
|
||||
写入时只写缓存,异步批量写数据库
|
||||
</div>
|
||||
<div class="pattern-points">
|
||||
<div class="point">
|
||||
<span class="icon">⚡</span>
|
||||
@@ -212,42 +297,71 @@ def update_user(user_id, data):
|
||||
<div class="point">
|
||||
<span class="icon">🎯</span>
|
||||
<div>
|
||||
<strong>适用</strong
|
||||
>:秒杀系统、点赞数、浏览量(可接受少量丢失)
|
||||
<strong>适用</strong>:秒杀系统、点赞数、浏览量(可接受少量丢失)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="diagram">
|
||||
<div class="diagram-title">写入流程</div>
|
||||
<div class="diagram-title">
|
||||
写入流程
|
||||
</div>
|
||||
<div class="flow-chart">
|
||||
<div class="flow-step">
|
||||
<div class="step-number">1</div>
|
||||
<div class="step-text">写入缓存</div>
|
||||
<div class="step-time">⚡ ~1ms</div>
|
||||
<div class="step-number">
|
||||
1
|
||||
</div>
|
||||
<div class="step-text">
|
||||
写入缓存
|
||||
</div>
|
||||
<div class="step-time">
|
||||
⚡ ~1ms
|
||||
</div>
|
||||
</div>
|
||||
<div class="flow-arrow">
|
||||
↓
|
||||
</div>
|
||||
<div class="flow-arrow">↓</div>
|
||||
<div class="flow-step">
|
||||
<div class="step-number">2</div>
|
||||
<div class="step-text">立即返回</div>
|
||||
<div class="step-number">
|
||||
2
|
||||
</div>
|
||||
<div class="step-text">
|
||||
立即返回
|
||||
</div>
|
||||
</div>
|
||||
<div class="flow-arrow">
|
||||
↓
|
||||
</div>
|
||||
<div class="flow-arrow">↓</div>
|
||||
<div class="flow-step pending">
|
||||
<div class="step-number">3</div>
|
||||
<div class="step-text">异步批量写数据库</div>
|
||||
<div class="step-time">🕐 后台执行</div>
|
||||
<div class="step-number">
|
||||
3
|
||||
</div>
|
||||
<div class="step-text">
|
||||
异步批量写数据库
|
||||
</div>
|
||||
<div class="step-time">
|
||||
🕐 后台执行
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="demo-controls">
|
||||
<button class="demo-btn" @click="simulateWriteBehind">
|
||||
<button
|
||||
class="demo-btn"
|
||||
@click="simulateWriteBehind"
|
||||
>
|
||||
模拟批量写入
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="write-queue" v-if="writeQueue.length > 0">
|
||||
<div class="queue-title">待写入队列</div>
|
||||
<div
|
||||
v-if="writeQueue.length > 0"
|
||||
class="write-queue"
|
||||
>
|
||||
<div class="queue-title">
|
||||
待写入队列
|
||||
</div>
|
||||
<div class="queue-items">
|
||||
<div
|
||||
v-for="(item, index) in writeQueue"
|
||||
@@ -263,7 +377,9 @@ def update_user(user_id, data):
|
||||
</div>
|
||||
|
||||
<div class="code-example">
|
||||
<div class="code-title">代码示例</div>
|
||||
<div class="code-title">
|
||||
代码示例
|
||||
</div>
|
||||
<pre class="code-block"><code>// Write-Behind 模式
|
||||
def update_counter(post_id):
|
||||
# 1. 立即更新缓存(极快)
|
||||
@@ -283,7 +399,9 @@ def update_counter(post_id):
|
||||
</div>
|
||||
|
||||
<div class="pattern-comparison">
|
||||
<div class="comparison-title">模式对比</div>
|
||||
<div class="comparison-title">
|
||||
模式对比
|
||||
</div>
|
||||
<table class="comparison-table">
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
@@ -5,8 +5,12 @@
|
||||
<template>
|
||||
<div class="cache-problems-demo">
|
||||
<div class="header">
|
||||
<div class="title">缓存的三大问题</div>
|
||||
<div class="subtitle">穿透、击穿、雪崩的场景与解决方案</div>
|
||||
<div class="title">
|
||||
缓存的三大问题
|
||||
</div>
|
||||
<div class="subtitle">
|
||||
穿透、击穿、雪崩的场景与解决方案
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="problem-selector">
|
||||
@@ -24,9 +28,14 @@
|
||||
|
||||
<div class="problem-content">
|
||||
<!-- 缓存穿透 -->
|
||||
<div v-if="activeProblem === 'penetration'" class="problem-detail">
|
||||
<div
|
||||
v-if="activeProblem === 'penetration'"
|
||||
class="problem-detail"
|
||||
>
|
||||
<div class="problem-intro">
|
||||
<div class="intro-title">什么是缓存穿透?</div>
|
||||
<div class="intro-title">
|
||||
什么是缓存穿透?
|
||||
</div>
|
||||
<div class="intro-text">
|
||||
查询一个<strong>不存在的数据</strong>(如恶意请求
|
||||
id=-1),缓存没有,数据库也没有。 导致每次请求都直接打到数据库。
|
||||
@@ -34,51 +43,78 @@
|
||||
</div>
|
||||
|
||||
<div class="problem-scenario">
|
||||
<div class="scenario-title">场景模拟</div>
|
||||
<div class="scenario-title">
|
||||
场景模拟
|
||||
</div>
|
||||
<div class="scenario-diagram">
|
||||
<div class="flow-item request">
|
||||
<div class="flow-icon">🔥</div>
|
||||
<div class="flow-text">请求 id=-999</div>
|
||||
<div class="flow-icon">
|
||||
🔥
|
||||
</div>
|
||||
<div class="flow-text">
|
||||
请求 id=-999
|
||||
</div>
|
||||
</div>
|
||||
<div class="flow-arrow">↓</div>
|
||||
<div class="flow-item cache" :class="{ miss: true }">
|
||||
<div class="flow-icon">❌</div>
|
||||
<div class="flow-text">缓存未命中</div>
|
||||
<div class="flow-arrow">
|
||||
↓
|
||||
</div>
|
||||
<div
|
||||
class="flow-item cache"
|
||||
:class="{ miss: true }"
|
||||
>
|
||||
<div class="flow-icon">
|
||||
❌
|
||||
</div>
|
||||
<div class="flow-text">
|
||||
缓存未命中
|
||||
</div>
|
||||
</div>
|
||||
<div class="flow-arrow">
|
||||
↓
|
||||
</div>
|
||||
<div class="flow-arrow">↓</div>
|
||||
<div
|
||||
class="flow-item database"
|
||||
:class="{ overloaded: dbPressure >= 80 }"
|
||||
>
|
||||
<div class="flow-icon">🗄️</div>
|
||||
<div class="flow-text">数据库查询(不存在)</div>
|
||||
<div class="flow-icon">
|
||||
🗄️
|
||||
</div>
|
||||
<div class="flow-text">
|
||||
数据库查询(不存在)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<button
|
||||
class="attack-btn"
|
||||
@click="simulatePenetration"
|
||||
:disabled="simulating"
|
||||
@click="simulatePenetration"
|
||||
>
|
||||
{{ simulating ? '攻击中...' : '模拟恶意攻击' }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="pressure-meter">
|
||||
<div class="meter-label">数据库压力</div>
|
||||
<div class="meter-label">
|
||||
数据库压力
|
||||
</div>
|
||||
<div class="meter-bar">
|
||||
<div
|
||||
class="meter-fill"
|
||||
:style="{ width: dbPressure + '%' }"
|
||||
></div>
|
||||
/>
|
||||
</div>
|
||||
<div class="meter-value">
|
||||
{{ dbPressure }}%
|
||||
</div>
|
||||
<div class="meter-value">{{ dbPressure }}%</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="solutions">
|
||||
<div class="solutions-title">解决方案</div>
|
||||
<div class="solutions-title">
|
||||
解决方案
|
||||
</div>
|
||||
<div class="solution-list">
|
||||
<div class="solution-item">
|
||||
<div class="solution-header">
|
||||
@@ -87,7 +123,7 @@
|
||||
</div>
|
||||
<div class="solution-desc">
|
||||
在缓存前加一层过滤器,快速判断"这个 id 肯定不存在"。
|
||||
<br />
|
||||
<br>
|
||||
<span class="note">100% 判断不存在,但可能有误判</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -105,25 +141,34 @@
|
||||
</div>
|
||||
|
||||
<!-- 缓存击穿 -->
|
||||
<div v-if="activeProblem === 'breakdown'" class="problem-detail">
|
||||
<div
|
||||
v-if="activeProblem === 'breakdown'"
|
||||
class="problem-detail"
|
||||
>
|
||||
<div class="problem-intro">
|
||||
<div class="intro-title">什么是缓存击穿?</div>
|
||||
<div class="intro-title">
|
||||
什么是缓存击穿?
|
||||
</div>
|
||||
<div class="intro-text">
|
||||
某个<strong>热点数据</strong>过期(如微博热搜),瞬间几百万请求同时打到数据库。
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="problem-scenario">
|
||||
<div class="scenario-title">场景模拟</div>
|
||||
<div class="scenario-title">
|
||||
场景模拟
|
||||
</div>
|
||||
<div class="hotkey-scenario">
|
||||
<div class="hotkey-badge">
|
||||
🔥 热点数据
|
||||
<br />
|
||||
<br>
|
||||
<span class="key">user:12345</span>
|
||||
</div>
|
||||
|
||||
<div class="concurrent-requests">
|
||||
<div class="requests-title">并发请求</div>
|
||||
<div class="requests-title">
|
||||
并发请求
|
||||
</div>
|
||||
<div class="requests-container">
|
||||
<div
|
||||
v-for="(req, index) in concurrentRequests"
|
||||
@@ -131,23 +176,34 @@
|
||||
class="request-item"
|
||||
:class="req.status"
|
||||
>
|
||||
<div class="request-id">请求 {{ req.id }}</div>
|
||||
<div class="request-status">{{ req.statusText }}</div>
|
||||
<div class="request-id">
|
||||
请求 {{ req.id }}
|
||||
</div>
|
||||
<div class="request-status">
|
||||
{{ req.statusText }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mutex-visual" v-if="showMutex">
|
||||
<div class="mutex-badge">🔒 互斥锁</div>
|
||||
<div class="mutex-text">只有一个线程能查数据库</div>
|
||||
<div
|
||||
v-if="showMutex"
|
||||
class="mutex-visual"
|
||||
>
|
||||
<div class="mutex-badge">
|
||||
🔒 互斥锁
|
||||
</div>
|
||||
<div class="mutex-text">
|
||||
只有一个线程能查数据库
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<button
|
||||
class="attack-btn"
|
||||
@click="simulateBreakdown"
|
||||
:disabled="simulating"
|
||||
@click="simulateBreakdown"
|
||||
>
|
||||
{{ simulating ? '模拟中...' : '模拟热点过期' }}
|
||||
</button>
|
||||
@@ -155,7 +211,9 @@
|
||||
</div>
|
||||
|
||||
<div class="solutions">
|
||||
<div class="solutions-title">解决方案</div>
|
||||
<div class="solutions-title">
|
||||
解决方案
|
||||
</div>
|
||||
<div class="solution-list">
|
||||
<div class="solution-item">
|
||||
<div class="solution-header">
|
||||
@@ -164,7 +222,7 @@
|
||||
</div>
|
||||
<div class="solution-desc">
|
||||
只允许一个线程查数据库,其他线程等待。
|
||||
<br />
|
||||
<br>
|
||||
<span class="note">优点:简单;缺点:阻塞其他请求</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -175,10 +233,8 @@
|
||||
</div>
|
||||
<div class="solution-desc">
|
||||
不设置 TTL,而是在 value 里存一个过期时间字段。
|
||||
<br />
|
||||
<span class="note"
|
||||
>查询时发现"逻辑过期",异步更新缓存,同时返回旧数据</span
|
||||
>
|
||||
<br>
|
||||
<span class="note">查询时发现"逻辑过期",异步更新缓存,同时返回旧数据</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -186,9 +242,14 @@
|
||||
</div>
|
||||
|
||||
<!-- 缓存雪崩 -->
|
||||
<div v-if="activeProblem === 'avalanche'" class="problem-detail">
|
||||
<div
|
||||
v-if="activeProblem === 'avalanche'"
|
||||
class="problem-detail"
|
||||
>
|
||||
<div class="problem-intro">
|
||||
<div class="intro-title">什么是缓存雪崩?</div>
|
||||
<div class="intro-title">
|
||||
什么是缓存雪崩?
|
||||
</div>
|
||||
<div class="intro-text">
|
||||
大量缓存<strong>同时过期</strong>(如系统重启后,所有缓存都在
|
||||
00:00:00 过期), 数据库瞬间被打爆。
|
||||
@@ -196,7 +257,9 @@
|
||||
</div>
|
||||
|
||||
<div class="problem-scenario">
|
||||
<div class="scenario-title">场景模拟</div>
|
||||
<div class="scenario-title">
|
||||
场景模拟
|
||||
</div>
|
||||
<div class="avalanche-visual">
|
||||
<div class="cache-items">
|
||||
<div
|
||||
@@ -205,38 +268,61 @@
|
||||
class="cache-item"
|
||||
:class="{ expired: item.expired }"
|
||||
>
|
||||
<div class="item-key">{{ item.key }}</div>
|
||||
<div class="item-ttl">TTL: {{ item.ttl }}s</div>
|
||||
<div class="item-key">
|
||||
{{ item.key }}
|
||||
</div>
|
||||
<div class="item-ttl">
|
||||
TTL: {{ item.ttl }}s
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mass-explosion" v-if="massExplosion">
|
||||
<div class="explosion-icon">💥</div>
|
||||
<div class="explosion-text">同时过期!</div>
|
||||
<div
|
||||
v-if="massExplosion"
|
||||
class="mass-explosion"
|
||||
>
|
||||
<div class="explosion-icon">
|
||||
💥
|
||||
</div>
|
||||
<div class="explosion-text">
|
||||
同时过期!
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="db-overload" :class="{ critical: dbPressure >= 90 }">
|
||||
<div class="db-icon">🗄️</div>
|
||||
<div class="db-status">数据库负载: {{ dbPressure }}%</div>
|
||||
<div
|
||||
class="db-overload"
|
||||
:class="{ critical: dbPressure >= 90 }"
|
||||
>
|
||||
<div class="db-icon">
|
||||
🗄️
|
||||
</div>
|
||||
<div class="db-status">
|
||||
数据库负载: {{ dbPressure }}%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<button
|
||||
class="attack-btn"
|
||||
@click="simulateAvalanche"
|
||||
:disabled="simulating"
|
||||
@click="simulateAvalanche"
|
||||
>
|
||||
{{ simulating ? '模拟中...' : '模拟缓存雪崩' }}
|
||||
</button>
|
||||
<button class="solution-btn" @click="applyRandomTTL">
|
||||
<button
|
||||
class="solution-btn"
|
||||
@click="applyRandomTTL"
|
||||
>
|
||||
应用解决方案(随机 TTL)
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="solutions">
|
||||
<div class="solutions-title">解决方案</div>
|
||||
<div class="solutions-title">
|
||||
解决方案
|
||||
</div>
|
||||
<div class="solution-list">
|
||||
<div class="solution-item">
|
||||
<div class="solution-header">
|
||||
@@ -245,10 +331,8 @@
|
||||
</div>
|
||||
<div class="solution-desc">
|
||||
避免同时过期,TTL 加上随机值。
|
||||
<br />
|
||||
<span class="code"
|
||||
>ttl = 600 + random.randint(-60, 60) # 600 ± 60 秒</span
|
||||
>
|
||||
<br>
|
||||
<span class="code">ttl = 600 + random.randint(-60, 60) # 600 ± 60 秒</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="solution-item">
|
||||
@@ -258,10 +342,8 @@
|
||||
</div>
|
||||
<div class="solution-desc">
|
||||
系统启动时,主动加载热点数据到缓存。
|
||||
<br />
|
||||
<span class="note"
|
||||
>使用定时任务,提前刷新即将过期的热点数据</span
|
||||
>
|
||||
<br>
|
||||
<span class="note">使用定时任务,提前刷新即将过期的热点数据</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="solution-item">
|
||||
@@ -271,7 +353,7 @@
|
||||
</div>
|
||||
<div class="solution-desc">
|
||||
当数据库压力过大时,暂时停止更新缓存,直接返回降级数据。
|
||||
<br />
|
||||
<br>
|
||||
<span class="note">如"系统繁忙,请稍后再试"</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -281,7 +363,9 @@
|
||||
</div>
|
||||
|
||||
<div class="comparison-table">
|
||||
<div class="table-title">三大问题对比</div>
|
||||
<div class="table-title">
|
||||
三大问题对比
|
||||
</div>
|
||||
<table class="problems-table">
|
||||
<thead>
|
||||
<tr>
|
||||
|
||||
+7
-2
@@ -3,10 +3,15 @@
|
||||
<div class="demo-container">
|
||||
<div class="demo-header">
|
||||
<h4>{{ title }}</h4>
|
||||
<p class="hint">{{ description }}</p>
|
||||
<p class="hint">
|
||||
{{ description }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="demo-content">
|
||||
<el-alert type="info" :closable="false">
|
||||
<el-alert
|
||||
type="info"
|
||||
:closable="false"
|
||||
>
|
||||
电商缓存架构演示组件占位符 - 待实现具体交互
|
||||
</el-alert>
|
||||
</div>
|
||||
|
||||
+171
-53
@@ -5,25 +5,41 @@
|
||||
<template>
|
||||
<div class="cache-comparison-demo">
|
||||
<div class="header">
|
||||
<div class="title">本地缓存 vs 分布式缓存</div>
|
||||
<div class="subtitle">对比两种缓存架构的性能和特点</div>
|
||||
<div class="title">
|
||||
本地缓存 vs 分布式缓存
|
||||
</div>
|
||||
<div class="subtitle">
|
||||
对比两种缓存架构的性能和特点
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="comparison-view">
|
||||
<!-- Local Cache -->
|
||||
<div class="cache-side local">
|
||||
<div class="side-header">
|
||||
<div class="title">本地缓存 (Local Cache)</div>
|
||||
<div class="tag">进程内</div>
|
||||
<div class="title">
|
||||
本地缓存 (Local Cache)
|
||||
</div>
|
||||
<div class="tag">
|
||||
进程内
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="architecture">
|
||||
<div class="app-instance">
|
||||
<div class="instance-label">应用实例 1</div>
|
||||
<div class="instance-label">
|
||||
应用实例 1
|
||||
</div>
|
||||
<div class="cache-box">
|
||||
<div class="cache-label">缓存</div>
|
||||
<div class="cache-label">
|
||||
缓存
|
||||
</div>
|
||||
<div class="cache-data">
|
||||
<div v-for="item in localCache1" :key="item" class="data-item">
|
||||
<div
|
||||
v-for="item in localCache1"
|
||||
:key="item"
|
||||
class="data-item"
|
||||
>
|
||||
{{ item }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -31,11 +47,19 @@
|
||||
</div>
|
||||
|
||||
<div class="app-instance">
|
||||
<div class="instance-label">应用实例 2</div>
|
||||
<div class="instance-label">
|
||||
应用实例 2
|
||||
</div>
|
||||
<div class="cache-box">
|
||||
<div class="cache-label">缓存</div>
|
||||
<div class="cache-label">
|
||||
缓存
|
||||
</div>
|
||||
<div class="cache-data">
|
||||
<div v-for="item in localCache2" :key="item" class="data-item">
|
||||
<div
|
||||
v-for="item in localCache2"
|
||||
:key="item"
|
||||
class="data-item"
|
||||
>
|
||||
{{ item }}
|
||||
</div>
|
||||
</div>
|
||||
@@ -45,29 +69,53 @@
|
||||
|
||||
<div class="metrics">
|
||||
<div class="metric">
|
||||
<div class="metric-label">响应时间</div>
|
||||
<div class="metric-value fast">~1 ms</div>
|
||||
<div class="metric-label">
|
||||
响应时间
|
||||
</div>
|
||||
<div class="metric-value fast">
|
||||
~1 ms
|
||||
</div>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<div class="metric-label">容量</div>
|
||||
<div class="metric-value">~1 GB</div>
|
||||
<div class="metric-label">
|
||||
容量
|
||||
</div>
|
||||
<div class="metric-value">
|
||||
~1 GB
|
||||
</div>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<div class="metric-label">一致性</div>
|
||||
<div class="metric-value warning">低</div>
|
||||
<div class="metric-label">
|
||||
一致性
|
||||
</div>
|
||||
<div class="metric-value warning">
|
||||
低
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pros-cons">
|
||||
<div class="pros">
|
||||
<div class="list-title">✅ 优点</div>
|
||||
<div class="list-item">极快(无网络开销)</div>
|
||||
<div class="list-item">简单(内存 Map)</div>
|
||||
<div class="list-title">
|
||||
✅ 优点
|
||||
</div>
|
||||
<div class="list-item">
|
||||
极快(无网络开销)
|
||||
</div>
|
||||
<div class="list-item">
|
||||
简单(内存 Map)
|
||||
</div>
|
||||
</div>
|
||||
<div class="cons">
|
||||
<div class="list-title">❌ 缺点</div>
|
||||
<div class="list-item">容量受限</div>
|
||||
<div class="list-item">实例间不一致</div>
|
||||
<div class="list-title">
|
||||
❌ 缺点
|
||||
</div>
|
||||
<div class="list-item">
|
||||
容量受限
|
||||
</div>
|
||||
<div class="list-item">
|
||||
实例间不一致
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -75,33 +123,51 @@
|
||||
<!-- Distributed Cache -->
|
||||
<div class="cache-side distributed">
|
||||
<div class="side-header">
|
||||
<div class="title">分布式缓存 (Distributed Cache)</div>
|
||||
<div class="tag">独立服务</div>
|
||||
<div class="title">
|
||||
分布式缓存 (Distributed Cache)
|
||||
</div>
|
||||
<div class="tag">
|
||||
独立服务
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="architecture">
|
||||
<div class="instances-row">
|
||||
<div class="app-instance-small">
|
||||
<div class="instance-label-small">实例 1</div>
|
||||
<div class="instance-label-small">
|
||||
实例 1
|
||||
</div>
|
||||
</div>
|
||||
<div class="app-instance-small">
|
||||
<div class="instance-label-small">实例 2</div>
|
||||
<div class="instance-label-small">
|
||||
实例 2
|
||||
</div>
|
||||
</div>
|
||||
<div class="app-instance-small">
|
||||
<div class="instance-label-small">实例 3</div>
|
||||
<div class="instance-label-small">
|
||||
实例 3
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="network-layer">
|
||||
<div class="network-label">网络</div>
|
||||
<div class="network-arrows">⬇️ ⬇️ ⬇️</div>
|
||||
<div class="network-label">
|
||||
网络
|
||||
</div>
|
||||
<div class="network-arrows">
|
||||
⬇️ ⬇️ ⬇️
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="redis-cluster">
|
||||
<div class="cluster-label">Redis 集群</div>
|
||||
<div class="cluster-label">
|
||||
Redis 集群
|
||||
</div>
|
||||
<div class="redis-nodes">
|
||||
<div class="redis-node">
|
||||
<div class="node-label">Node 1</div>
|
||||
<div class="node-label">
|
||||
Node 1
|
||||
</div>
|
||||
<div class="node-data">
|
||||
<div
|
||||
v-for="item in redisData1"
|
||||
@@ -113,7 +179,9 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="redis-node">
|
||||
<div class="node-label">Node 2</div>
|
||||
<div class="node-label">
|
||||
Node 2
|
||||
</div>
|
||||
<div class="node-data">
|
||||
<div
|
||||
v-for="item in redisData2"
|
||||
@@ -125,7 +193,9 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="redis-node">
|
||||
<div class="node-label">Node 3</div>
|
||||
<div class="node-label">
|
||||
Node 3
|
||||
</div>
|
||||
<div class="node-data">
|
||||
<div
|
||||
v-for="item in redisData3"
|
||||
@@ -142,48 +212,96 @@
|
||||
|
||||
<div class="metrics">
|
||||
<div class="metric">
|
||||
<div class="metric-label">响应时间</div>
|
||||
<div class="metric-value medium">~5 ms</div>
|
||||
<div class="metric-label">
|
||||
响应时间
|
||||
</div>
|
||||
<div class="metric-value medium">
|
||||
~5 ms
|
||||
</div>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<div class="metric-label">容量</div>
|
||||
<div class="metric-value">~100 GB</div>
|
||||
<div class="metric-label">
|
||||
容量
|
||||
</div>
|
||||
<div class="metric-value">
|
||||
~100 GB
|
||||
</div>
|
||||
</div>
|
||||
<div class="metric">
|
||||
<div class="metric-label">一致性</div>
|
||||
<div class="metric-value good">高</div>
|
||||
<div class="metric-label">
|
||||
一致性
|
||||
</div>
|
||||
<div class="metric-value good">
|
||||
高
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pros-cons">
|
||||
<div class="pros">
|
||||
<div class="list-title">✅ 优点</div>
|
||||
<div class="list-item">容量可扩展</div>
|
||||
<div class="list-item">全局共享</div>
|
||||
<div class="list-title">
|
||||
✅ 优点
|
||||
</div>
|
||||
<div class="list-item">
|
||||
容量可扩展
|
||||
</div>
|
||||
<div class="list-item">
|
||||
全局共享
|
||||
</div>
|
||||
</div>
|
||||
<div class="cons">
|
||||
<div class="list-title">❌ 缺点</div>
|
||||
<div class="list-item">网络延迟</div>
|
||||
<div class="list-item">需要维护</div>
|
||||
<div class="list-title">
|
||||
❌ 缺点
|
||||
</div>
|
||||
<div class="list-item">
|
||||
网络延迟
|
||||
</div>
|
||||
<div class="list-item">
|
||||
需要维护
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="interactive-demo">
|
||||
<div class="demo-title">交互演示:写入和读取数据</div>
|
||||
<div class="demo-title">
|
||||
交互演示:写入和读取数据
|
||||
</div>
|
||||
<div class="demo-controls">
|
||||
<button class="demo-btn" @click="simulateWrite">写入数据</button>
|
||||
<button class="demo-btn secondary" @click="simulateRead">
|
||||
<button
|
||||
class="demo-btn"
|
||||
@click="simulateWrite"
|
||||
>
|
||||
写入数据
|
||||
</button>
|
||||
<button
|
||||
class="demo-btn secondary"
|
||||
@click="simulateRead"
|
||||
>
|
||||
读取数据
|
||||
</button>
|
||||
<button class="demo-btn reset" @click="reset">重置</button>
|
||||
<button
|
||||
class="demo-btn reset"
|
||||
@click="reset"
|
||||
>
|
||||
重置
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="demo-result" v-if="lastOperation">
|
||||
<div class="result-icon">{{ lastOperation.icon }}</div>
|
||||
<div class="result-text">{{ lastOperation.text }}</div>
|
||||
<div class="result-detail">{{ lastOperation.detail }}</div>
|
||||
<div
|
||||
v-if="lastOperation"
|
||||
class="demo-result"
|
||||
>
|
||||
<div class="result-icon">
|
||||
{{ lastOperation.icon }}
|
||||
</div>
|
||||
<div class="result-text">
|
||||
{{ lastOperation.text }}
|
||||
</div>
|
||||
<div class="result-detail">
|
||||
{{ lastOperation.detail }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,8 +5,12 @@
|
||||
<template>
|
||||
<div class="locality-demo">
|
||||
<div class="header">
|
||||
<div class="title">局部性原理演示</div>
|
||||
<div class="subtitle">理解缓存为什么有效</div>
|
||||
<div class="title">
|
||||
局部性原理演示
|
||||
</div>
|
||||
<div class="subtitle">
|
||||
理解缓存为什么有效
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tabs">
|
||||
@@ -28,18 +32,20 @@
|
||||
|
||||
<div class="tab-content">
|
||||
<!-- Temporal Locality -->
|
||||
<div v-if="activeTab === 'temporal'" class="temporal-demo">
|
||||
<div
|
||||
v-if="activeTab === 'temporal'"
|
||||
class="temporal-demo"
|
||||
>
|
||||
<div class="description">
|
||||
<strong>时间局部性</strong
|
||||
>:如果你访问了某个数据,未来很可能再次访问它。
|
||||
<br />
|
||||
<span class="example"
|
||||
>例子:用户登录后,每次请求都需要查询用户信息</span
|
||||
>
|
||||
<strong>时间局部性</strong>:如果你访问了某个数据,未来很可能再次访问它。
|
||||
<br>
|
||||
<span class="example">例子:用户登录后,每次请求都需要查询用户信息</span>
|
||||
</div>
|
||||
|
||||
<div class="timeline">
|
||||
<div class="timeline-title">访问时间线</div>
|
||||
<div class="timeline-title">
|
||||
访问时间线
|
||||
</div>
|
||||
<div class="timeline-events">
|
||||
<div
|
||||
v-for="(event, index) in temporalEvents"
|
||||
@@ -47,7 +53,9 @@
|
||||
class="event"
|
||||
:class="{ hit: event.hit, miss: !event.hit }"
|
||||
>
|
||||
<div class="event-time">{{ event.time }}</div>
|
||||
<div class="event-time">
|
||||
{{ event.time }}
|
||||
</div>
|
||||
<div class="event-action">
|
||||
<span class="user-icon">👤</span>
|
||||
<span>查询 user_{{ event.userId }}</span>
|
||||
@@ -60,7 +68,9 @@
|
||||
</div>
|
||||
|
||||
<div class="cache-state">
|
||||
<div class="cache-title">当前缓存状态</div>
|
||||
<div class="cache-title">
|
||||
当前缓存状态
|
||||
</div>
|
||||
<div class="cache-items">
|
||||
<div
|
||||
v-for="item in cacheItems"
|
||||
@@ -68,24 +78,32 @@
|
||||
class="cache-item"
|
||||
:class="{ active: item.active }"
|
||||
>
|
||||
<div class="item-id">{{ item.id }}</div>
|
||||
<div class="item-hits">命中 {{ item.hits }} 次</div>
|
||||
<div class="item-id">
|
||||
{{ item.id }}
|
||||
</div>
|
||||
<div class="item-hits">
|
||||
命中 {{ item.hits }} 次
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Spatial Locality -->
|
||||
<div v-if="activeTab === 'spatial'" class="spatial-demo">
|
||||
<div
|
||||
v-if="activeTab === 'spatial'"
|
||||
class="spatial-demo"
|
||||
>
|
||||
<div class="description">
|
||||
<strong>空间局部性</strong
|
||||
>:如果你访问了某个数据,很可能访问它附近的数据。
|
||||
<br />
|
||||
<strong>空间局部性</strong>:如果你访问了某个数据,很可能访问它附近的数据。
|
||||
<br>
|
||||
<span class="example">例子:浏览商品列表时,通常会翻到下一页</span>
|
||||
</div>
|
||||
|
||||
<div class="product-grid">
|
||||
<div class="grid-title">商品浏览序列</div>
|
||||
<div class="grid-title">
|
||||
商品浏览序列
|
||||
</div>
|
||||
<div class="products">
|
||||
<div
|
||||
v-for="product in products"
|
||||
@@ -97,7 +115,9 @@
|
||||
current: product.current
|
||||
}"
|
||||
>
|
||||
<div class="product-id">{{ product.id }}</div>
|
||||
<div class="product-id">
|
||||
{{ product.id }}
|
||||
</div>
|
||||
<div class="product-status">
|
||||
<span v-if="product.current">👁️ 当前</span>
|
||||
<span v-else-if="product.cached">⚡ 已缓存</span>
|
||||
@@ -109,14 +129,18 @@
|
||||
|
||||
<div class="spatial-explanation">
|
||||
<div class="explanation-item">
|
||||
<div class="icon">📊</div>
|
||||
<div class="icon">
|
||||
📊
|
||||
</div>
|
||||
<div class="text">
|
||||
<strong>预取策略</strong>:当你浏览第 5 个商品时,系统自动将 6-8
|
||||
预加载到缓存
|
||||
</div>
|
||||
</div>
|
||||
<div class="explanation-item">
|
||||
<div class="icon">🎯</div>
|
||||
<div class="icon">
|
||||
🎯
|
||||
</div>
|
||||
<div class="text">
|
||||
<strong>命中率提升</strong>:空间局部性让缓存命中率达到 70-90%
|
||||
</div>
|
||||
@@ -126,22 +150,44 @@
|
||||
</div>
|
||||
|
||||
<div class="interactive-controls">
|
||||
<button class="control-btn" @click="addEvent">添加访问事件</button>
|
||||
<button class="control-btn secondary" @click="reset">重置</button>
|
||||
<button
|
||||
class="control-btn"
|
||||
@click="addEvent"
|
||||
>
|
||||
添加访问事件
|
||||
</button>
|
||||
<button
|
||||
class="control-btn secondary"
|
||||
@click="reset"
|
||||
>
|
||||
重置
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="stats">
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">总访问次数</div>
|
||||
<div class="stat-value">{{ totalAccess }}</div>
|
||||
<div class="stat-label">
|
||||
总访问次数
|
||||
</div>
|
||||
<div class="stat-value">
|
||||
{{ totalAccess }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">缓存命中</div>
|
||||
<div class="stat-value hit">{{ hitCount }}</div>
|
||||
<div class="stat-label">
|
||||
缓存命中
|
||||
</div>
|
||||
<div class="stat-value hit">
|
||||
{{ hitCount }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<div class="stat-label">命中率</div>
|
||||
<div class="stat-value">{{ hitRate }}%</div>
|
||||
<div class="stat-label">
|
||||
命中率
|
||||
</div>
|
||||
<div class="stat-value">
|
||||
{{ hitRate }}%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,8 +5,12 @@
|
||||
<template>
|
||||
<div class="multi-level-cache-demo">
|
||||
<div class="header">
|
||||
<div class="title">多级缓存架构</div>
|
||||
<div class="subtitle">每一层都是上一层的"保护伞"</div>
|
||||
<div class="title">
|
||||
多级缓存架构
|
||||
</div>
|
||||
<div class="subtitle">
|
||||
每一层都是上一层的"保护伞"
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cache-levels">
|
||||
@@ -20,33 +24,53 @@
|
||||
miss: level.status === 'miss'
|
||||
}"
|
||||
>
|
||||
<div class="level-number">L{{ level.layer }}</div>
|
||||
<div class="level-number">
|
||||
L{{ level.layer }}
|
||||
</div>
|
||||
<div class="level-content">
|
||||
<div class="level-header">
|
||||
<div class="level-name">{{ level.name }}</div>
|
||||
<div class="level-name">
|
||||
{{ level.name }}
|
||||
</div>
|
||||
<div class="level-meta">
|
||||
<span class="latency">{{ level.latency }}</span>
|
||||
<span class="capacity">{{ level.capacity }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="level-description">{{ level.description }}</div>
|
||||
<div class="level-status" v-if="level.status">
|
||||
<span v-if="level.status === 'hit'" class="status-badge hit"
|
||||
>✅ 命中</span
|
||||
>
|
||||
<span v-if="level.status === 'miss'" class="status-badge miss"
|
||||
>❌ 未命中</span
|
||||
>
|
||||
<div class="level-description">
|
||||
{{ level.description }}
|
||||
</div>
|
||||
<div
|
||||
v-if="level.status"
|
||||
class="level-status"
|
||||
>
|
||||
<span
|
||||
v-if="level.status === 'hit'"
|
||||
class="status-badge hit"
|
||||
>✅ 命中</span>
|
||||
<span
|
||||
v-if="level.status === 'miss'"
|
||||
class="status-badge miss"
|
||||
>❌ 未命中</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="level-arrow" v-if="index < cacheLevels.length - 1">↓</div>
|
||||
<div
|
||||
v-if="index < cacheLevels.length - 1"
|
||||
class="level-arrow"
|
||||
>
|
||||
↓
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<div class="control-group">
|
||||
<label>请求数据</label>
|
||||
<button class="request-btn" @click="makeRequest" :disabled="processing">
|
||||
<button
|
||||
class="request-btn"
|
||||
:disabled="processing"
|
||||
@click="makeRequest"
|
||||
>
|
||||
{{ processing ? '处理中...' : '发起请求' }}
|
||||
</button>
|
||||
</div>
|
||||
@@ -55,18 +79,29 @@
|
||||
<label>模拟场景</label>
|
||||
<select
|
||||
v-model="scenario"
|
||||
@change="onScenarioChange"
|
||||
class="scenario-select"
|
||||
@change="onScenarioChange"
|
||||
>
|
||||
<option value="normal">正常访问 (70% 命中率)</option>
|
||||
<option value="cold">冷启动 (0% 命中率)</option>
|
||||
<option value="hot">热点数据 (95% 命中率)</option>
|
||||
<option value="normal">
|
||||
正常访问 (70% 命中率)
|
||||
</option>
|
||||
<option value="cold">
|
||||
冷启动 (0% 命中率)
|
||||
</option>
|
||||
<option value="hot">
|
||||
热点数据 (95% 命中率)
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="request-flow" v-if="requestHistory.length > 0">
|
||||
<div class="flow-title">请求流程</div>
|
||||
<div
|
||||
v-if="requestHistory.length > 0"
|
||||
class="request-flow"
|
||||
>
|
||||
<div class="flow-title">
|
||||
请求流程
|
||||
</div>
|
||||
<div class="flow-timeline">
|
||||
<div
|
||||
v-for="(event, index) in requestHistory"
|
||||
@@ -74,70 +109,96 @@
|
||||
class="flow-event"
|
||||
:class="event.type"
|
||||
>
|
||||
<div class="event-level">{{ event.level }}</div>
|
||||
<div class="event-level">
|
||||
{{ event.level }}
|
||||
</div>
|
||||
<div class="event-action">
|
||||
<span class="event-icon">{{ event.icon }}</span>
|
||||
<span class="event-text">{{ event.action }}</span>
|
||||
</div>
|
||||
<div class="event-time">{{ event.time }}ms</div>
|
||||
<div class="event-time">
|
||||
{{ event.time }}ms
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="statistics">
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">总请求数</div>
|
||||
<div class="stat-value">{{ stats.totalRequests }}</div>
|
||||
<div class="stat-label">
|
||||
总请求数
|
||||
</div>
|
||||
<div class="stat-value">
|
||||
{{ stats.totalRequests }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">缓存命中</div>
|
||||
<div class="stat-value hit">{{ stats.cacheHits }}</div>
|
||||
<div class="stat-label">
|
||||
缓存命中
|
||||
</div>
|
||||
<div class="stat-value hit">
|
||||
{{ stats.cacheHits }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">命中率</div>
|
||||
<div class="stat-value">{{ stats.hitRate }}%</div>
|
||||
<div class="stat-label">
|
||||
命中率
|
||||
</div>
|
||||
<div class="stat-value">
|
||||
{{ stats.hitRate }}%
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">平均响应时间</div>
|
||||
<div class="stat-value">{{ stats.avgLatency }}ms</div>
|
||||
<div class="stat-label">
|
||||
平均响应时间
|
||||
</div>
|
||||
<div class="stat-value">
|
||||
{{ stats.avgLatency }}ms
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">数据库访问</div>
|
||||
<div class="stat-value db">{{ stats.dbAccess }}</div>
|
||||
<div class="stat-label">
|
||||
数据库访问
|
||||
</div>
|
||||
<div class="stat-value db">
|
||||
{{ stats.dbAccess }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="explanation">
|
||||
<div class="explanation-title">多级缓存的优势</div>
|
||||
<div class="explanation-title">
|
||||
多级缓存的优势
|
||||
</div>
|
||||
<div class="explanation-grid">
|
||||
<div class="explanation-item">
|
||||
<div class="item-icon">🛡️</div>
|
||||
<div class="item-icon">
|
||||
🛡️
|
||||
</div>
|
||||
<div class="item-text">
|
||||
<strong>逐级过滤</strong>
|
||||
<br />
|
||||
<span class="item-detail"
|
||||
>每层过滤掉大部分请求,最终到达数据库的可能只有 1%</span
|
||||
>
|
||||
<br>
|
||||
<span class="item-detail">每层过滤掉大部分请求,最终到达数据库的可能只有 1%</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="explanation-item">
|
||||
<div class="item-icon">⚡</div>
|
||||
<div class="item-icon">
|
||||
⚡
|
||||
</div>
|
||||
<div class="item-text">
|
||||
<strong>极速响应</strong>
|
||||
<br />
|
||||
<span class="item-detail"
|
||||
>上层缓存命中时,响应时间从 50ms 降至 0-10ms</span
|
||||
>
|
||||
<br>
|
||||
<span class="item-detail">上层缓存命中时,响应时间从 50ms 降至 0-10ms</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="explanation-item">
|
||||
<div class="item-icon">💰</div>
|
||||
<div class="item-icon">
|
||||
💰
|
||||
</div>
|
||||
<div class="item-text">
|
||||
<strong>降低成本</strong>
|
||||
<br />
|
||||
<span class="item-detail"
|
||||
>减少昂贵的数据库查询,节省服务器资源</span
|
||||
>
|
||||
<br>
|
||||
<span class="item-detail">减少昂贵的数据库查询,节省服务器资源</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,38 +5,69 @@
|
||||
<template>
|
||||
<div class="product-cache-demo">
|
||||
<div class="header">
|
||||
<div class="title">商品详情页缓存系统实战</div>
|
||||
<div class="subtitle">完整的三级缓存架构 + 监控面板</div>
|
||||
<div class="title">
|
||||
商品详情页缓存系统实战
|
||||
</div>
|
||||
<div class="subtitle">
|
||||
完整的三级缓存架构 + 监控面板
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="architecture-overview">
|
||||
<div class="overview-title">系统架构</div>
|
||||
<div class="overview-title">
|
||||
系统架构
|
||||
</div>
|
||||
<div class="architecture-diagram">
|
||||
<div class="layer client">
|
||||
<div class="layer-label">客户端</div>
|
||||
<div class="layer-icon">📱</div>
|
||||
<div class="layer-label">
|
||||
客户端
|
||||
</div>
|
||||
<div class="layer-icon">
|
||||
📱
|
||||
</div>
|
||||
</div>
|
||||
<div class="arrow">↓</div>
|
||||
<div class="layer local-cache" :class="{ hit: currentLevel === 1 }">
|
||||
<div class="layer-label">L1: 本地缓存 (Caffeine)</div>
|
||||
<div class="arrow">
|
||||
↓
|
||||
</div>
|
||||
<div
|
||||
class="layer local-cache"
|
||||
:class="{ hit: currentLevel === 1 }"
|
||||
>
|
||||
<div class="layer-label">
|
||||
L1: 本地缓存 (Caffeine)
|
||||
</div>
|
||||
<div class="layer-stats">
|
||||
<div>容量: 1000</div>
|
||||
<div>TTL: 30s</div>
|
||||
<div>命中: {{ localHits }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="arrow">↓</div>
|
||||
<div class="layer redis-cache" :class="{ hit: currentLevel === 2 }">
|
||||
<div class="layer-label">L2: Redis 集群</div>
|
||||
<div class="arrow">
|
||||
↓
|
||||
</div>
|
||||
<div
|
||||
class="layer redis-cache"
|
||||
:class="{ hit: currentLevel === 2 }"
|
||||
>
|
||||
<div class="layer-label">
|
||||
L2: Redis 集群
|
||||
</div>
|
||||
<div class="layer-stats">
|
||||
<div>容量: 100万</div>
|
||||
<div>TTL: 5min</div>
|
||||
<div>命中: {{ redisHits }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="arrow">↓</div>
|
||||
<div class="layer database" :class="{ hit: currentLevel === 3 }">
|
||||
<div class="layer-label">L3: MySQL 数据库</div>
|
||||
<div class="arrow">
|
||||
↓
|
||||
</div>
|
||||
<div
|
||||
class="layer database"
|
||||
:class="{ hit: currentLevel === 3 }"
|
||||
>
|
||||
<div class="layer-label">
|
||||
L3: MySQL 数据库
|
||||
</div>
|
||||
<div class="layer-stats">
|
||||
<div>持久化存储</div>
|
||||
<div>查询: {{ dbQueries }}</div>
|
||||
@@ -47,21 +78,35 @@
|
||||
|
||||
<div class="demo-sections">
|
||||
<div class="section query-demo">
|
||||
<div class="section-title">查询商品</div>
|
||||
<div class="section-title">
|
||||
查询商品
|
||||
</div>
|
||||
<div class="query-controls">
|
||||
<input
|
||||
v-model="productId"
|
||||
type="text"
|
||||
placeholder="输入商品ID (如: P001)"
|
||||
class="product-input"
|
||||
/>
|
||||
<button class="query-btn" @click="queryProduct" :disabled="querying">
|
||||
>
|
||||
<button
|
||||
class="query-btn"
|
||||
:disabled="querying"
|
||||
@click="queryProduct"
|
||||
>
|
||||
{{ querying ? '查询中...' : '查询' }}
|
||||
</button>
|
||||
<button class="reset-btn" @click="resetDemo">重置</button>
|
||||
<button
|
||||
class="reset-btn"
|
||||
@click="resetDemo"
|
||||
>
|
||||
重置
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="query-result" v-if="queryResult">
|
||||
<div
|
||||
v-if="queryResult"
|
||||
class="query-result"
|
||||
>
|
||||
<div class="result-header">
|
||||
<span class="result-icon">{{ queryResult.icon }}</span>
|
||||
<span class="result-title">{{ queryResult.title }}</span>
|
||||
@@ -95,8 +140,13 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="query-flow" v-if="queryFlow.length > 0">
|
||||
<div class="flow-title">查询流程</div>
|
||||
<div
|
||||
v-if="queryFlow.length > 0"
|
||||
class="query-flow"
|
||||
>
|
||||
<div class="flow-title">
|
||||
查询流程
|
||||
</div>
|
||||
<div class="flow-steps">
|
||||
<div
|
||||
v-for="(step, index) in queryFlow"
|
||||
@@ -104,50 +154,82 @@
|
||||
class="flow-step"
|
||||
:class="step.type"
|
||||
>
|
||||
<div class="step-level">{{ step.level }}</div>
|
||||
<div class="step-result">{{ step.result }}</div>
|
||||
<div class="step-time">{{ step.time }}ms</div>
|
||||
<div class="step-level">
|
||||
{{ step.level }}
|
||||
</div>
|
||||
<div class="step-result">
|
||||
{{ step.result }}
|
||||
</div>
|
||||
<div class="step-time">
|
||||
{{ step.time }}ms
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section cache-monitor">
|
||||
<div class="section-title">缓存监控</div>
|
||||
<div class="section-title">
|
||||
缓存监控
|
||||
</div>
|
||||
|
||||
<div class="metrics-grid">
|
||||
<div class="metric-card">
|
||||
<div class="metric-label">总请求数</div>
|
||||
<div class="metric-value">{{ metrics.totalRequests }}</div>
|
||||
<div class="metric-label">
|
||||
总请求数
|
||||
</div>
|
||||
<div class="metric-value">
|
||||
{{ metrics.totalRequests }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<div class="metric-label">本地缓存命中</div>
|
||||
<div class="metric-value local">{{ localHits }}</div>
|
||||
<div class="metric-label">
|
||||
本地缓存命中
|
||||
</div>
|
||||
<div class="metric-value local">
|
||||
{{ localHits }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<div class="metric-label">Redis命中</div>
|
||||
<div class="metric-value redis">{{ redisHits }}</div>
|
||||
<div class="metric-label">
|
||||
Redis命中
|
||||
</div>
|
||||
<div class="metric-value redis">
|
||||
{{ redisHits }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="metric-card">
|
||||
<div class="metric-label">数据库查询</div>
|
||||
<div class="metric-value db">{{ dbQueries }}</div>
|
||||
<div class="metric-label">
|
||||
数据库查询
|
||||
</div>
|
||||
<div class="metric-value db">
|
||||
{{ dbQueries }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hit-rate-display">
|
||||
<div class="rate-label">整体命中率</div>
|
||||
<div class="rate-value">{{ overallHitRate }}%</div>
|
||||
<div class="rate-label">
|
||||
整体命中率
|
||||
</div>
|
||||
<div class="rate-value">
|
||||
{{ overallHitRate }}%
|
||||
</div>
|
||||
<div class="rate-bar">
|
||||
<div
|
||||
class="rate-fill"
|
||||
:style="{ width: overallHitRate + '%' }"
|
||||
></div>
|
||||
/>
|
||||
</div>
|
||||
<div class="rate-target">
|
||||
目标: > 90%
|
||||
</div>
|
||||
<div class="rate-target">目标: > 90%</div>
|
||||
</div>
|
||||
|
||||
<div class="cache-stats-detail">
|
||||
<div class="stats-title">详细统计</div>
|
||||
<div class="stats-title">
|
||||
详细统计
|
||||
</div>
|
||||
<div class="stats-list">
|
||||
<div class="stat-item">
|
||||
<span class="stat-label">本地缓存命中率:</span>
|
||||
@@ -171,35 +253,61 @@
|
||||
</div>
|
||||
|
||||
<div class="features">
|
||||
<div class="feature-title">核心特性</div>
|
||||
<div class="feature-title">
|
||||
核心特性
|
||||
</div>
|
||||
<div class="feature-grid">
|
||||
<div class="feature-item">
|
||||
<div class="feature-icon">🛡️</div>
|
||||
<div class="feature-name">多级缓存</div>
|
||||
<div class="feature-icon">
|
||||
🛡️
|
||||
</div>
|
||||
<div class="feature-name">
|
||||
多级缓存
|
||||
</div>
|
||||
<div class="feature-desc">
|
||||
本地缓存 + Redis 双层防护,减少 99% 数据库查询
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<div class="feature-icon">🔒</div>
|
||||
<div class="feature-name">防击穿</div>
|
||||
<div class="feature-desc">互斥锁保护热点数据,避免并发查询数据库</div>
|
||||
<div class="feature-icon">
|
||||
🔒
|
||||
</div>
|
||||
<div class="feature-name">
|
||||
防击穿
|
||||
</div>
|
||||
<div class="feature-desc">
|
||||
互斥锁保护热点数据,避免并发查询数据库
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<div class="feature-icon">🎯</div>
|
||||
<div class="feature-name">防穿透</div>
|
||||
<div class="feature-desc">缓存空对象,防止查询不存在的商品</div>
|
||||
<div class="feature-icon">
|
||||
🎯
|
||||
</div>
|
||||
<div class="feature-name">
|
||||
防穿透
|
||||
</div>
|
||||
<div class="feature-desc">
|
||||
缓存空对象,防止查询不存在的商品
|
||||
</div>
|
||||
</div>
|
||||
<div class="feature-item">
|
||||
<div class="feature-icon">⏰</div>
|
||||
<div class="feature-name">随机 TTL</div>
|
||||
<div class="feature-desc">避免缓存雪崩,过期时间加随机值</div>
|
||||
<div class="feature-icon">
|
||||
⏰
|
||||
</div>
|
||||
<div class="feature-name">
|
||||
随机 TTL
|
||||
</div>
|
||||
<div class="feature-desc">
|
||||
避免缓存雪崩,过期时间加随机值
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="code-preview">
|
||||
<div class="code-title">核心代码片段</div>
|
||||
<div class="code-title">
|
||||
核心代码片段
|
||||
</div>
|
||||
<pre class="code-block"><code>// 三级缓存查询
|
||||
public Product getProduct(String productId) {
|
||||
// L1: 本地缓存
|
||||
|
||||
Reference in New Issue
Block a user