Files
test-repo/docs/.vitepress/theme/components/appendix/cache-design/LocalVsDistributedCacheDemo.vue
T

604 lines
13 KiB
Vue
Raw Normal View History

<!--
LocalVsDistributedCacheDemo.vue
本地缓存 vs 分布式缓存对比演示
-->
<template>
<div class="cache-comparison-demo">
<div class="header">
<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>
<div class="architecture">
<div class="app-instance">
<div class="instance-label">应用实例 1</div>
<div class="cache-box">
<div class="cache-label">缓存</div>
<div class="cache-data">
<div v-for="item in localCache1" :key="item" class="data-item">
{{ item }}
</div>
</div>
</div>
</div>
<div class="app-instance">
<div class="instance-label">应用实例 2</div>
<div class="cache-box">
<div class="cache-label">缓存</div>
<div class="cache-data">
<div v-for="item in localCache2" :key="item" class="data-item">
{{ item }}
</div>
</div>
</div>
</div>
</div>
<div class="metrics">
<div class="metric">
<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>
<div class="metric">
<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>
<div class="cons">
<div class="list-title"> 缺点</div>
<div class="list-item">容量受限</div>
<div class="list-item">实例间不一致</div>
</div>
</div>
</div>
<!-- Distributed Cache -->
<div class="cache-side distributed">
<div class="side-header">
<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>
<div class="app-instance-small">
<div class="instance-label-small">实例 2</div>
</div>
<div class="app-instance-small">
<div class="instance-label-small">实例 3</div>
</div>
</div>
<div class="network-layer">
<div class="network-label">网络</div>
<div class="network-arrows"> </div>
</div>
<div class="redis-cluster">
<div class="cluster-label">Redis 集群</div>
<div class="redis-nodes">
<div class="redis-node">
<div class="node-label">Node 1</div>
<div class="node-data">
<div
v-for="item in redisData1"
:key="item"
class="data-item small"
>
{{ item }}
</div>
</div>
</div>
<div class="redis-node">
<div class="node-label">Node 2</div>
<div class="node-data">
<div
v-for="item in redisData2"
:key="item"
class="data-item small"
>
{{ item }}
</div>
</div>
</div>
<div class="redis-node">
<div class="node-label">Node 3</div>
<div class="node-data">
<div
v-for="item in redisData3"
:key="item"
class="data-item small"
>
{{ item }}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="metrics">
<div class="metric">
<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>
<div class="metric">
<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>
<div class="cons">
<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-controls">
<button class="demo-btn" @click="simulateWrite">写入数据</button>
<button class="demo-btn secondary" @click="simulateRead">
读取数据
</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>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const localCache1 = ref(['user:1', 'user:2', 'config:A'])
const localCache2 = ref(['user:3', 'config:B'])
const redisData1 = ref(['user:1', 'user:2', 'user:3'])
const redisData2 = ref(['product:A', 'product:B', 'product:C'])
const redisData3 = ref(['config:A', 'config:B'])
const lastOperation = ref(null)
let dataCounter = 4
const simulateWrite = () => {
const key = `user:${dataCounter++}`
// Local cache: Write to instance 1 only
localCache1.value.push(key)
if (localCache1.value.length > 5) localCache1.value.shift()
// Distributed cache: Hash to a node
const nodeIndex = dataCounter % 3
if (nodeIndex === 0) redisData1.value.push(key)
else if (nodeIndex === 1) redisData2.value.push(key)
else redisData3.value.push(key)
lastOperation.value = {
icon: '✍️',
text: `写入 ${key}`,
detail: '本地缓存: 仅实例1有数据 | 分布式缓存: 所有实例共享'
}
}
const simulateRead = () => {
const key = 'user:1'
const inLocal1 = localCache1.value.includes(key)
const inLocal2 = localCache2.value.includes(key)
const inRedis =
redisData1.value.includes(key) ||
redisData2.value.includes(key) ||
redisData3.value.includes(key)
lastOperation.value = {
icon: '🔍',
text: `读取 ${key}`,
detail: `本地缓存: 实例1${inLocal1 ? '✅' : '❌'} 实例2${inLocal2 ? '✅' : '❌'} | 分布式缓存: ${inRedis ? '✅' : '❌'}`
}
}
const reset = () => {
localCache1.value = ['user:1', 'user:2', 'config:A']
localCache2.value = ['user:3', 'config:B']
redisData1.value = ['user:1', 'user:2', 'user:3']
redisData2.value = ['product:A', 'product:B', 'product:C']
redisData3.value = ['config:A', 'config:B']
dataCounter = 4
lastOperation.value = null
}
</script>
<style scoped>
.cache-comparison-demo {
border: 1px solid var(--vp-c-divider);
background: var(--vp-c-bg-soft);
border-radius: 12px;
padding: 1.5rem;
margin: 1.5rem 0;
font-family: var(--vp-font-family-base);
}
.header {
margin-bottom: 1.5rem;
}
.title {
font-weight: 700;
font-size: 1.1rem;
}
.subtitle {
color: var(--vp-c-text-2);
font-size: 0.9rem;
}
.comparison-view {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 2rem;
margin-bottom: 2rem;
}
@media (max-width: 960px) {
.comparison-view {
grid-template-columns: 1fr;
}
}
.cache-side {
background: var(--vp-c-bg);
border-radius: 10px;
padding: 1.25rem;
border: 2px solid var(--vp-c-divider);
}
.cache-side.local {
border-color: #3b82f6;
}
.cache-side.distributed {
border-color: #ef4444;
}
.side-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
}
.side-header .title {
font-size: 1rem;
font-weight: 700;
}
.tag {
padding: 0.25rem 0.75rem;
border-radius: 999px;
font-size: 0.75rem;
font-weight: 600;
background: var(--vp-c-bg-soft);
}
.architecture {
margin-bottom: 1rem;
}
.app-instance {
background: #eff6ff;
padding: 0.75rem;
border-radius: 6px;
margin-bottom: 0.5rem;
border: 1px solid #bfdbfe;
}
.instance-label {
font-size: 0.8rem;
font-weight: 600;
margin-bottom: 0.5rem;
color: #1e40af;
}
.cache-box {
background: white;
padding: 0.5rem;
border-radius: 6px;
border: 1px dashed #93c5fd;
}
.cache-label {
font-size: 0.7rem;
font-weight: 600;
margin-bottom: 0.35rem;
color: var(--vp-c-text-2);
}
.cache-data {
display: flex;
flex-wrap: wrap;
gap: 0.35rem;
}
.data-item {
padding: 0.2rem 0.5rem;
background: #dbeafe;
border-radius: 4px;
font-size: 0.7rem;
font-weight: 600;
color: #1e40af;
}
.data-item.small {
padding: 0.15rem 0.35rem;
font-size: 0.65rem;
}
.instances-row {
display: flex;
gap: 0.5rem;
margin-bottom: 0.75rem;
}
.app-instance-small {
flex: 1;
background: #fef2f2;
padding: 0.5rem;
border-radius: 6px;
border: 1px solid #fecaca;
text-align: center;
}
.instance-label-small {
font-size: 0.75rem;
font-weight: 600;
color: #991b1b;
}
.network-layer {
text-align: center;
padding: 0.5rem;
background: #fef3c7;
border-radius: 6px;
margin-bottom: 0.75rem;
}
.network-label {
font-size: 0.75rem;
font-weight: 600;
margin-bottom: 0.25rem;
}
.network-arrows {
font-size: 1.2rem;
}
.redis-cluster {
background: #fef2f2;
padding: 0.75rem;
border-radius: 6px;
border: 1px solid #fecaca;
}
.cluster-label {
font-size: 0.8rem;
font-weight: 600;
margin-bottom: 0.5rem;
color: #991b1b;
text-align: center;
}
.redis-nodes {
display: flex;
gap: 0.5rem;
}
.redis-node {
flex: 1;
background: white;
padding: 0.5rem;
border-radius: 6px;
border: 1px dashed #fca5a5;
}
.node-label {
font-size: 0.7rem;
font-weight: 600;
margin-bottom: 0.35rem;
color: var(--vp-c-text-2);
text-align: center;
}
.node-data {
display: flex;
flex-wrap: wrap;
gap: 0.25rem;
}
.metrics {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 0.75rem;
margin-bottom: 1rem;
padding: 0.75rem;
background: var(--vp-c-bg-soft);
border-radius: 6px;
}
.metric {
text-align: center;
}
.metric-label {
font-size: 0.75rem;
color: var(--vp-c-text-2);
margin-bottom: 0.25rem;
}
.metric-value {
font-size: 0.9rem;
font-weight: 700;
}
.metric-value.fast {
color: #22c55e;
}
.metric-value.medium {
color: #f59e0b;
}
.metric-value.good {
color: #22c55e;
}
.metric-value.warning {
color: #ef4444;
}
.pros-cons {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
}
.pros,
.cons {
padding: 0.75rem;
border-radius: 6px;
}
.pros {
background: #f0fdf4;
border: 1px solid #bbf7d0;
}
.cons {
background: #fef2f2;
border: 1px solid #fecaca;
}
.list-title {
font-size: 0.8rem;
font-weight: 600;
margin-bottom: 0.5rem;
}
.list-item {
font-size: 0.75rem;
margin-bottom: 0.35rem;
line-height: 1.4;
}
.interactive-demo {
background: var(--vp-c-bg);
border-radius: 10px;
padding: 1.5rem;
border: 1px solid var(--vp-c-divider);
}
.demo-title {
font-weight: 600;
margin-bottom: 1rem;
font-size: 0.95rem;
}
.demo-controls {
display: flex;
gap: 0.75rem;
margin-bottom: 1rem;
flex-wrap: wrap;
}
.demo-btn {
padding: 0.75rem 1.5rem;
background: var(--vp-c-brand);
color: white;
border: none;
border-radius: 6px;
cursor: pointer;
font-weight: 600;
font-size: 0.9rem;
transition: all 0.2s;
}
.demo-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.demo-btn.secondary {
background: #3b82f6;
}
.demo-btn.reset {
background: var(--vp-c-bg);
color: var(--vp-c-text-1);
border: 1px solid var(--vp-c-divider);
}
.demo-result {
display: flex;
align-items: center;
gap: 1rem;
padding: 0.75rem;
background: var(--vp-c-bg-soft);
border-radius: 6px;
border-left: 4px solid var(--vp-c-brand);
}
.result-icon {
font-size: 1.5rem;
}
.result-text {
font-weight: 600;
font-size: 0.9rem;
}
.result-detail {
font-size: 0.85rem;
color: var(--vp-c-text-2);
}
</style>