Files
AIclinicalresearch/docs/07-运维文档/04-Redis改造实施计划.md
HaHafeng 1b53ab9d52 feat(aia): Complete AIA V2.0 with universal streaming capabilities
Major Changes:
- Add StreamingService with OpenAI Compatible format
- Upgrade Chat component V2 with Ant Design X integration
- Implement AIA module with 12 intelligent agents
- Update API routes to unified /api/v1 prefix
- Update system documentation

Backend (~1300 lines):
- common/streaming: OpenAI Compatible adapter
- modules/aia: 12 agents, conversation service, streaming integration
- Update route versions (RVW, PKB to v1)

Frontend (~3500 lines):
- modules/aia: AgentHub + ChatWorkspace (100% prototype restoration)
- shared/Chat: AIStreamChat, ThinkingBlock, useAIStream Hook
- Update API endpoints to v1

Documentation:
- AIA module status guide
- Universal capabilities catalog
- System overview updates
- All module documentation sync

Tested: Stream response verified, authentication working
Status: AIA V2.0 core completed (85%)
2026-01-14 19:15:01 +08:00

1793 lines
51 KiB
Markdown
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.
# Redis謾ケ騾<EFBDB9>螳樊命隶。蛻抵シ育シ灘ュ<E78198>+髦溷<E9ABA6>螳梧紛迚茨シ<E88CA8>
> **譁<>。」迚域悽<E59F9F>?* V2.0
> **譖エ譁ー譌・譛滂シ?* 2025-12-12
> **逶ョ譬<EFBDAE>ョ梧<EFBDAE>譌カ髣エ<E9ABA3>?* 2025-12-18<31>?螟ゥ<E89E9F><EFBDA9>
> **雍溯エ」莠コ<E88EA0><EFBDBA>** 謚€譛ッ蝗「髦?
> **鬟朱勦遲臥コァ<EFBDBA>?* <20>泯 荳ュ遲会シ域怏髯咲コァ譁ケ譯茨シ?
> **驥崎ヲ∝序譖エ<E8AD96>?* Redis髦溷<E9ABA6>莉?蜿ッ騾?隹<>紛荳?蠢<>。サ"
---
## 笞<><E7AC9E><EFBFBD> **驥崎ヲ∬ッエ譏趣シ<E8B6A3>2.0譖エ譁ー<EFBFBD>?*
扈剰ソ<EFBFBD>キア蜈・蛻<EFBFBD><EFBFBD>軍edis髦溷<EFBFBD>**荳肴弍蜿ッ騾蛾。ケ**<2A>€梧弍**譬ク蠢<EFBDB8>粥閭ス逧<EFBDBD>ソ<EFBFBD>。サ鬘ケ**<2A>?
1. **ASL譁<4C>鍵遲幃€?*<2A>?000遽<30>枚迪ョ髴€隕?蟆乗慮<E4B997>御ク咲畑Redis髦溷<E9ABA6>螟ア雍・邇?> 95%
2. **DC Tool B逞<42>紙謠仙叙**<EFBFBD>?000莉ス逞<EFBDBD>紙髴€隕?-3蟆乗慮<E4B997>悟酔譬キ髣ョ鬚?3. **SAE螳樔セ狗音諤?*<2A>?5蛻<35>帖譌<E5B896><EFBFBD>㍼閾ェ蜉ィ郛ゥ螳ケ<E89EB3>碁柄莉サ蜉。蠢<EFBDA1>┯螟ア雍?
**蝗<>豁、譛ャ隶。蛻定ー<E5AE9A>紛荳コ<E88DB3>夂シ灘ュ?髦溷<E9ABA6>€襍キ螳樊命<E6A88A><E591BD>7螟ゥ螳梧<E89EB3><E6A2A7><EFBFBD>**
---
## <20>搭 逶ョ蠖<EFBDAE>
1. [謾ケ騾<EFBDB9>閭梧勹荳守岼譬(#1-謾ケ騾<EFBDB9>閭梧勹荳守岼譬<E5B2BC>)
2. [蠖灘燕邉サ扈溽憾諤∝<E8ABA4>譫疹(#2-蠖灘燕邉サ扈溽憾諤∝<E8ABA4>譫?
3. [Redis驟咲スョ菫。諱ッ](#3-redis驟咲スョ菫。諱ッ)
4. [謾ケ騾<EFBFBD>隸ヲ扈<EFBFBD>ュ・鬪、](#4-謾ケ騾<EFBDB9>隸ヲ扈<EFBDA6>ュ・鬪?<3F>遺惠 **蟾イ譖エ譁ー<E8AD81>壼桁蜷ォ髦溷<E9ABA6>**<2A>?5. [豬玖ッ墓婿譯<E5A9BF>(#5-豬玖ッ墓婿譯<E5A9BF>)
6. [鬟朱勦隸<E58BA6>シー荳守シ楢ァ」](#6-鬟朱勦隸<E58BA6>シー荳守シ楢ァ?
7. [荳顔コソ隶。蛻綻(#7-荳顔コソ隶。蛻<EFBDA1>)
8. [蝗樊サ壽婿譯<E5A9BF>(#8-蝗樊サ壽婿譯<E5A9BF>)
9. [逶第而荳手ソ千サエ](#9-逶第而荳手ソ千サ?
---
## 1. 謾ケ騾<EFBDB9>閭梧勹荳守岼譬<E5B2BC>
### 1.1 荳コ莉€荵郁ヲ∵隼騾<E99ABC><E9A8BE><EFBFBD>
#### **蠖灘燕髣ョ鬚<EFBDAE>**<2A>?1. 笶?**霑晏渚莠大次逕溯ァ<E6BAAF><EFBDA7>?*<2A>夂ウサ扈滉スソ逕ィ蜀<EFBDA8>ュ倡シ灘ュ假シ瑚ソ晏渚閾ェ蟾ア蛻カ螳夂噪莠大次逕溷シ€蜿題ァ<E9A18C><EFBDA7>?2. 笶?**LLM謌先悽螟ア謗ァ**<2A>夂シ灘ュ倅ク肴戟荵<E6889F><EFBFBD>悟ッシ閾エ驥榊、崎ー<E5B48E>畑DeepSeek/Qwen API
3. 笶?**髟ソ莉サ蜉。荳榊庄髱<E5BA84>**<2A>?0-60蛻<30>帖逧<E5B896>枚迪ョ遲幃€我ササ蜉。<E89C89>郡AE螳樔セ矩㍾蜷ッ蜷惹ク「螟?4. 笶?**螟壼ョ樔セ倶ク榊酔豁・**<2A>售AE謇ゥ螳ケ蜷趣シ悟推螳樔セ狗シ灘ュ倅ク榊<EFBDB8>莠ォ
5. 笶?**Serverless荳埼€<C280>**<2A><EFBFBD>蟄倡憾諤∝惠Serverless邇ッ蠅<EFBDAF>ク倶ク榊庄髱<E5BA84>
#### **謾ケ騾<EFBDB9>逶ョ譬?*<2A>?- 笨?**隨ヲ蜷域楔譫<E6A594><EFBFBD>激**<2A>壻スソ逕ィ蛻<EFBDA8><EFBFBD>シ冗シ灘ュ假シ<E58187>edis<69>?- 笨?**髯堺ス拶PI謌先悽**<2A>哭LM扈捺棡郛灘ュ俶戟荵<E6889F><EFBFBD>碁∩蜈埼㍾螟崎ー<E5B48E>
- 笨?**莉サ蜉。謖∽ケ<E288BD><EFBDB9>?*<2A>夐柄譌カ髣エ莉サ蜉。荳榊屏螳樔セ矩㍾蜷ッ閠御ク「螟?- 笨?**謾ッ謖∝、壼ョ樔セ?*<2A>夂シ灘ュ伜惠螟壼ョ樔セ矩龍蜈ア莠ォ
- 笨?**蟷ウ貊題ソ<E9A18C>ク。**<2A>壻ソ晉蕗髯咲コァ譁ケ譯茨シ檎。ョ菫晉ウサ扈溽ィウ螳<EFBDB3>
---
## 2. 蠖灘燕邉サ扈溽憾諤∝<E8ABA4>譫?
### 2.1 蟾イ菴ソ逕ィ郛灘ュ倡噪菴咲スョ
#### **菴咲スョ1<EFBDAE>唏ealthCheckService.ts**
```typescript
// 譁<>サカ<EFBDBB>喘ackend/src/modules/dc/tool-b/services/HealthCheckService.ts
// 隨?7陦鯉シ夊ッサ蜿也シ灘ュ<E78198>
const cached = await cache.get<HealthCheckResult>(cacheKey);
// 隨?45陦鯉シ壼<EFBDBC>蜈・郛灘ュ<E78198>
await cache.set(cacheKey, result, 86400); // 24蟆乗慮
```
**逕ィ騾?*<2A>哘xcel蛛・蠎キ譽€譟・扈捺棡郛灘ュ?
**驥崎ヲ∵€?*<2A>夸沺?荳ュ遲会シ磯∩蜈埼㍾螟崎ァ」譫職xcel<65>?
**謨ー謐ョ驥?*<2A>嘸5KB/鬘?
---
#### **菴咲スョ2<EFBDAE>哭LM12FieldsService.ts**
```typescript
// 譁<>サカ<EFBDBB>喘ackend/src/modules/asl/common/llm/LLM12FieldsService.ts
// 隨?16陦鯉シ夊ッサ蜿也シ灘ュ<E78198>
const cached = await cache.get(cacheKey);
// 隨?30陦鯉シ壼<EFBDBC>蜈・郛灘ュ<E78198>
await cache.set(cacheKey, JSON.stringify(result), 3600); // 1蟆乗慮
```
**逕ィ騾?*<2A>哭LM 12蟄玲ョオ謠仙叙扈捺棡郛灘ュ<E78198>
**驥崎ヲ∵€?*<2A>夸沐?鬮假シ育峩謗・蠖ア蜩喉PI謌先悽<E58588>?
**謨ー謐ョ驥?*<2A>嘸50KB/鬘?
**謌先悽蠖ア蜩<EFBDB1>**<EFBFBD>?```
蜊墓ャ。謠仙叙謌先悽<EFBFBD>嘸ツ・0.43/遽?螯よ棡郛灘ュ伜、ア謨茨シ碁㍾螟崎ー<E5B48E><EFBFBD><E79591>
- 10谺?= ツ・4.3
- 100谺?= ツ・43
- 1000谺?= ツ・430
```
---
### 2.2 髟ソ譌カ髣エ蠑よュ・莉サ蜉?
#### **ASL讓。蝮暦シ壽枚迪ョ遲幃€我ササ蜉?*
```typescript
// 譁<>サカ<EFBDBB>喘ackend/src/modules/asl/services/screeningService.ts
// 隨?3-65陦?processLiteraturesInBackground(task.id, projectId, literatures);
```
**髣ョ鬚<EFBDAE>**<2A>?- 199遽<39>枚迪ョ髴€隕?33-66蛻<36>
- 蠖灘燕菴ソ逕ィ蜀<EFBDA8>ュ倬弌蛻暦シ<E69AA6>emoryQueue<75>?- SAE螳樔セ矩㍾蜷ッ/郛ゥ螳ケ譌カ莉サ蜉。荳「螟?
**蠖ア蜩<EFBDB1>**<2A>?- 逕ィ謌キ菴馴ェ梧栫蟾ョ<E89FBE>井ササ蜉。遯∫┯豸亥、ア<EFBDA4><EFBDB1>
- 蟾イ螟<EFBDB2>炊扈捺棡荳「螟ア<E89E9F>梧オェ雍ケAPI雍ケ逕ィ
- 譌<>豕戊ソス貅ッ莉サ蜉。迥カ諤?
---
### 2.3 蠖灘燕譫カ譫<EFBDB6><E8ADAB>鄂ョ
```env
# backend/.env
CACHE_TYPE=memory # 竊?髴€隕∵隼荳?redis
QUEUE_TYPE=memory # 竊?髴€隕∵隼荳?redis
```
---
## 3. Redis驟咲スョ菫。諱ッ
### 3.1 髦ソ驥御コ然edis雍ュ荵ー菫。諱ッ
| 驟咲スョ鬘?| 蛟?| 隸エ譏<EFBDB4> |
|--------|---|------|
| **莠ァ蜩<EFBDA7>** | Redis 蠑€貅千沿 | 螳梧紛Redis蜉溯<E89C89> |
| **莉倩エケ譁ケ蠑<EFBDB9>** | 蛹<>ケエ蛹<EFBDB4>怦 | 鬥匁ャ。雍ュ荵ー莠?謚倅シ俶<EFBDBC>?|
| **驛ィ鄂イ讓。蠑<EFBDA1>** | 莠大次逕滂シ磯ォ伜庄逕ィ<E98095><EFBDA8> | 荳サ莉手<E88E89>蜉ィ蛻<EFBDA8>困 |
| **邉サ蛻<EFBDBB>** | 譬<>㊥迚?| 貊。雜ウ髴€豎?|
| **蝨ー蝓<EFBDB0>** | 蜊主圏2<E59C8F>亥圏莠ャ<E88EA0><EFBDAC> | 荳惨AE蜷悟慍蝓?|
| **螳樔セ狗アサ蝙<EFBDBB>** | 鬮伜庄逕?| 笨?99.95%蜿ッ逕ィ諤?|
| **螟ァ迚域<E8BF9A>?* | Redis 7.0 | 譛€譁ー遞ウ螳夂沿 |
| **譫カ譫<EFBDB6>アサ蝙<EFBDBB>** | 荳榊星逕ィ髮<EFBDA8>セ、<EFBDBE>亥黒闃らせ<E38289><E3819B> | 貊。雜ウ蠖灘燕隗<E78795>ィ。 |
| **蛻<>援隗<E68FB4><E99A97>シ** | 256 MB | 蛻晄悄雜ウ螟<EFBDB3> |
| **蛻<>援謨ー驥<EFBDB0>** | 1 | 蜊募<E89C8A>迚?|
| **隸サ蜀吝<E89C80>遖サ** | 蜈ウ髣ュ | 邂€蛹夜<E89BB9>鄂?|
### 3.2 鬚<>シー謌先悽
```
蝓コ遑€莉キ譬シ<EFBFBD>堋?2/蟷エ<E89FB7>亥黒譛コ迚茨シ<E88CA8>
鬮伜庄逕ィ迚茨シ堋?80/蟷エ<E89FB7>井シー邂暦シ?鬥匁ャ。雍ュ荵ー<E88DB5>堋?08/蟷エ<E89FB7><EFBDB4>6謚伜錘<E4BC9C>?
蟇ケ豈疲噺逶奇シ?- 闃ら怐LLM API雍ケ逕ィ<E98095>?ツ・500/蟷?- 謠仙合逕ィ謌キ貊。諢丞コヲ<EFBDBA>壽裏莉キ
- ROI<4F>?400%
```
### 3.3 霑樊磁菫。諱ッ<E8ABB1>郁エュ荵ー蜷手執蜿厄シ?
```env
# 髦ソ驥御コ第而蛻カ蜿ー 竊?Redis螳樔セ<E6A894> 竊?霑樊磁菫。諱ッ
REDIS_HOST=r-xxxxxxxxxxxx.redis.rds.aliyuncs.com
REDIS_PORT=6379
REDIS_PASSWORD=your_secure_password_here
REDIS_DB=0
# 謌紋スソ逕ィ霑樊磁蟄礼ャヲ荳イ
REDIS_URL=redis://:your_password@r-xxxxxxxxxxxx.redis.rds.aliyuncs.com:6379/0
```
---
## 4. 謾ケ騾<EFBDB9>隸ヲ扈<EFBDA6>ュ・鬪?
### 4.1 Phase 1<>壽悽蝨ー蠑€蜿醍識蠅<E8AD98>㊥螟?笨?
#### **豁・鬪、1.1<EFBFBD>壼ョ芽」<EFBFBD>セ晁オ?*
```bash
cd backend
npm install ioredis --save
npm install @types/ioredis --save-dev
```
#### **豁・鬪、1.2<EFBFBD><EFBFBD>鄂ョ譛ャ蝨ーRedis**
```bash
# 遑ョ隶、Docker Redis豁」蝨ィ霑占。<E58DA0>
docker ps | findstr redis
# 螯よ棡豐。譛芽ソ占。鯉シ悟星蜉ィ螳<EFBDA8>
docker start ai-clinical-redis
# 豬玖ッ戊ソ樊磁
docker exec -it ai-clinical-redis redis-cli ping
# 蠎碑ッ・霑泌屓<E6B38C>啀ONG
```
#### **豁・鬪、1.3<EFBFBD>壽峩譁ー譛ャ蝨?env**
```env
# backend/.env
DATABASE_URL=postgresql://postgres:postgres123@localhost:5432/ai_clinical_research
# ==================== Redis驟咲スョ ====================
# 蜷ッ逕ィRedis郛灘ュ<E78198>
CACHE_TYPE=redis
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_DB=0
# REDIS_PASSWORD= # 譛ャ蝨ー譌<EFBDB0><EFBFBD><E89F87>?
# 髦溷<E9ABA6>證よ慮逕ィ蜀<EFBDA8>ュ假シ亥<EFBDBC>髦カ谿オ蜷ッ逕ィ<E98095><EFBDA8>
QUEUE_TYPE=memory
# ==================== JWT ====================
JWT_SECRET=your-secret-key-change-in-production
JWT_EXPIRES_IN=7d
# ==================== LLM API ====================
DEEPSEEK_API_KEY=sk-7f8cc37a79fa4799860b38fc7ba2e150
DASHSCOPE_API_KEY=sk-75b4ff29a14a49e79667a331034f3298
# ==================== Dify ====================
DIFY_API_URL=http://localhost/v1
DIFY_API_KEY=dataset-mfvdiKvQ2l3NvxWm7RoYMN3c
# ==================== Server ====================
PORT=3001
NODE_ENV=development
# ==================== CloseAI驟咲スョ ====================
CLOSEAI_API_KEY=sk-cu0iepbXYGGx2jc7BqP6ogtSWmP6fk918qV3RUdtGC3Ed1po
CLOSEAI_OPENAI_BASE_URL=https://api.openai-proxy.org/v1
CLOSEAI_CLAUDE_BASE_URL=https://api.openai-proxy.org/anthropic
# ==================== 蟄伜お驟咲スョ ====================
STORAGE_TYPE=local
LOCAL_STORAGE_DIR=uploads
LOCAL_STORAGE_URL=http://localhost:3001/uploads
# ==================== CORS驟咲スョ ====================
CORS_ORIGIN=http://localhost:5173
# ==================== 譌・蠢鈴<E8A0A2>鄂ョ ====================
LOG_LEVEL=debug
```
---
### 4.2 Phase 2<>壼ョ樒鴫RedisCacheAdapter 笨?
#### **豁・鬪、2.1<EFBFBD>壻ソョ謾ケRedisCacheAdapter.ts**
```typescript
// 譁<>サカ<EFBDBB>喘ackend/src/common/cache/RedisCacheAdapter.ts
import Redis from 'ioredis';
import type { CacheAdapter } from './CacheAdapter.js';
import { logger } from '../logging/index.js';
/**
* Redis郛灘ュ倬€<C280>蝎? *
* 菴ソ逕ィioredis螳「謌キ遶ッ<E981B6>梧髪謖<E9ABAA>シ? * - 蟄礼ャヲ荳?蟇ケ雎。閾ェ蜉ィ蠎丞<E8A08E>蛹? * - TTL霑<4C>悄譌カ髣エ
* - 霑樊磁豎<E7A381>邂。逅? * - 髞呵ッッ驥崎ッ<E5B48E>
*
* @example
* const cache = new RedisCacheAdapter({
* host: 'localhost',
* port: 6379,
* password: 'xxx',
* db: 0
* });
*
* await cache.set('key', { data: 'value' }, 60);
* const value = await cache.get('key');
*/
export class RedisCacheAdapter implements CacheAdapter {
private redis: Redis;
constructor(options?: {
host?: string;
port?: number;
password?: string;
db?: number;
}) {
this.redis = new Redis({
host: options?.host || 'localhost',
port: options?.port || 6379,
password: options?.password || undefined,
db: options?.db || 0,
// 霑樊磁驟咲スョ
retryStrategy: (times) => {
const delay = Math.min(times * 50, 2000);
logger.warn(`Redis霑樊磁驥崎ッ<EFBFBD> ${times} 谺。<E8B0BA><EFBDA1>${delay}ms蜷朱㍾隸描);
return delay;
},
maxRetriesPerRequest: 3,
enableReadyCheck: true,
// 霑樊磁豎<E7A381>驟咲ス? lazyConnect: false,
keepAlive: 30000,
});
// 逶大成霑樊磁莠倶サカ
this.redis.on('connect', () => {
logger.info('Redis霑樊磁謌仙粥');
});
this.redis.on('error', (error) => {
logger.error('Redis霑樊磁髞呵ッッ', { error: error.message });
});
this.redis.on('close', () => {
logger.warn('Redis霑樊磁蜈ウ髣ュ');
});
this.redis.on('reconnecting', () => {
logger.warn('Redis豁」蝨ィ驥崎ソ<E5B48E>...');
});
}
/**
* 闔キ蜿也シ灘ュ伜€? */
async get<T>(key: string): Promise<T | null> {
try {
const value = await this.redis.get(key);
if (!value) {
logger.debug('Redis郛灘ュ俶悴蜻ス荳?, { key });
return null;
}
logger.debug('Redis郛灘ュ伜多荳ュ', { key, size: value.length });
// 蟆晁ッ戊ァ」譫辱SON
try {
return JSON.parse(value) as T;
} catch {
// 螯よ棡荳肴弍JSON<4F>瑚ソ泌屓蜴溷ァ句ュ礼ャヲ荳イ
return value as unknown as T;
}
} catch (error) {
logger.error('Redis GET螟ア雍・', { key, error });
return null; // 髯咲コァ<EFBDBA>夊ソ泌屓null閠御ク肴弍謚帛シょクク
}
}
/**
* 隶セ鄂ョ郛灘ュ伜€? * @param ttl 霑<>悄譌カ髣エ<E9ABA3>育ァ抵シ会シ御ク堺シ<E5A0BA>蛻呎ーク荳崎ソ<E5B48E><EFBFBD>井ク肴耳闕撰シ<E692B0>
*/
async set<T>(key: string, value: T, ttl?: number): Promise<void> {
try {
// 蠎丞<E8A08E>蛹門€? const serialized = typeof value === 'string'
? value
: JSON.stringify(value);
// 隶セ鄂ョ蛟シ<E89B9F>亥クヲTTL<54>? if (ttl) {
await this.redis.setex(key, ttl, serialized);
logger.debug('Redis SET謌仙粥<E4BB99>亥クヲTTL<54>?, {
key,
ttl,
size: serialized.length
});
} else {
await this.redis.set(key, serialized);
logger.warn('Redis SET謌仙粥<E4BB99>域裏TTL<54>?, { key }); // 隴ヲ蜻奇シ壽裏霑<E8A38F>悄譌カ髣エ
}
} catch (error) {
logger.error('Redis SET螟ア雍・', { key, ttl, error });
// 荳肴鴨蜃コ蠑ょクク<EFBDB8><EFBFBD>隶ク邉サ扈溽サァ扈ュ霑占。<E58DA0>
}
}
/**
*<>髯、郛灘ュ<E78198>
*/
async delete(key: string): Promise<boolean> {
try {
const result = await this.redis.del(key);
logger.debug('Redis DEL', { key, deleted: result > 0 });
return result > 0;
} catch (error) {
logger.error('Redis DEL螟ア雍・', { key, error });
return false;
}
}
/**
*€譟・key譏ッ蜷ヲ蟄伜惠
*/
async has(key: string): Promise<boolean> {
try {
const result = await this.redis.exists(key);
return result > 0;
} catch (error) {
logger.error('Redis EXISTS螟ア雍・', { key, error });
return false;
}
}
/**
*<>ゥコ謇€譛臥シ灘ュ假シ亥些髯ゥ謫堺ス懶シ<E687B6><EFBFBD>
*/
async clear(): Promise<void> {
try {
await this.redis.flushdb();
logger.warn('Redis FLUSHDB謇ァ陦鯉シ域園譛臥シ灘ュ伜キイ貂<EFBDB2>ゥコ<EFBDA9>?);
} catch (error) {
logger.error('Redis FLUSHDB螟ア雍・', { error });
}
}
/**
* 豬玖ッ紐edis霑樊磁
*/
async ping(): Promise<boolean> {
try {
const result = await this.redis.ping();
return result === 'PONG';
} catch (error) {
logger.error('Redis PING螟ア雍・', { error });
return false;
}
}
/**
* 蜈ウ髣ュ霑樊磁<E6A88A>育畑莠惹シ倬寉蜈ウ髣ュ<E9ABA3><EFBDAD>
*/
async disconnect(): Promise<void> {
try {
await this.redis.quit();
logger.info('Redis霑樊磁蟾イ蜈ウ髣?);
} catch (error) {
logger.error('Redis蜈ウ髣ュ霑樊磁螟ア雍・', { error });
}
}
}
```
---
#### **豁・鬪、2.2<EFBFBD>壽峩譁ーCacheFactory.ts<74>域キサ蜉<EFBDBB>髯咲コァ遲也払<E4B99F><E68995>**
```typescript
// 譁<>サカ<EFBDBB>喘ackend/src/common/cache/CacheFactory.ts
import { config } from '../../config/env.js';
import type { CacheAdapter } from './CacheAdapter.js';
import { MemoryCacheAdapter } from './MemoryCacheAdapter.js';
import { RedisCacheAdapter } from './RedisCacheAdapter.js';
import { logger } from '../logging/index.js';
/**
* 郛灘ュ伜キ・蜴ゑシ亥黒萓具シ<E585B7>
*
* 譬ケ謐ョ邇ッ蠅<EFBDAF>序驥剰<E9A9A5>蜉ィ騾画叫郛灘ュ伜ョ樒鴫<E6A892>? * - CACHE_TYPE=memory 竊?MemoryCacheAdapter
* - CACHE_TYPE=redis 竊?RedisCacheAdapter<65>域髪謖<E9ABAA>剄郤ァ<E983A4><EFBDA7>
*
* @example
* import { cache } from '@/common/cache'
* await cache.set('user:123', userData, 60)
* const user = await cache.get<User>('user:123')
*/
export class CacheFactory {
private static instance: CacheAdapter | null = null;
private static fallbackToMemory = false; // 髯咲コァ譬<EFBDA7>ョー
/**
* 闔キ蜿也シ灘ュ伜ョ樔セ具シ亥黒萓具シ<E585B7>
*/
static getInstance(): CacheAdapter {
if (!this.instance) {
this.instance = this.createCache();
}
return this.instance;
}
/**
* 蛻帛サコ郛灘ュ伜ョ樔セ<E6A894>
*/
private static createCache(): CacheAdapter {
const cacheType = config.cacheType || 'memory';
logger.info('[CacheFactory] 蛻晏ァ句喧郛灘ュ倡ウサ扈?, { cacheType });
switch (cacheType) {
case 'redis':
return this.createRedisCache();
case 'memory':
default:
return this.createMemoryCache();
}
}
/**
* 蛻帛サコRedis郛灘ュ假シ亥クヲ髯咲コァ遲也払<E4B99F>? */
private static createRedisCache(): CacheAdapter {
try {
logger.info('[CacheFactory] 豁」蝨ィ霑樊磁Redis...', {
host: config.redisHost,
port: config.redisPort,
db: config.redisDb,
});
const redisCache = new RedisCacheAdapter({
host: config.redisHost,
port: config.redisPort,
password: config.redisPassword,
db: config.redisDb,
});
// 豬玖ッ戊ソ樊磁<E6A88A>亥酔豁・遲牙セ<E78999><EFBFBD>
redisCache.ping().then((isConnected) => {
if (isConnected) {
logger.info('[CacheFactory] 笨?Redis霑樊磁謌仙粥');
} else {
logger.error('[CacheFactory] 笶?Redis霑樊磁螟ア雍・<E99B8D>悟キイ髯咲コァ蛻ー蜀<EFBDB0>ュ倡シ灘ュ?);
this.fallbackToMemory = true;
}
}).catch((error) => {
logger.error('[CacheFactory] 笶?Redis霑樊磁蠑ょクク<EFBDB8>悟キイ髯咲コァ蛻ー蜀<EFBDB0>ュ倡シ灘ュ?, { error });
this.fallbackToMemory = true;
});
return redisCache;
} catch (error) {
logger.error('[CacheFactory] 笶?Redis蛻晏ァ句喧螟ア雍・<E99B8D>碁剄郤ァ蛻ー蜀<EFBDB0>ュ倡シ灘ュ?, { error });
this.fallbackToMemory = true;
return this.createMemoryCache();
}
}
/**
* 蛻帛サコ蜀<EFBDBA>ュ倡シ灘ュ<E78198>
*/
private static createMemoryCache(): MemoryCacheAdapter {
logger.info('[CacheFactory] 菴ソ逕ィ蜀<EFBDA8>ュ倡シ灘ュ<E78198>');
return new MemoryCacheAdapter();
}
/**
* 譽€譟・譏ッ蜷ヲ蟾イ髯咲コァ蛻ー蜀<EFBDB0>ュ倡シ灘ュ? */
static isFallbackMode(): boolean {
return this.fallbackToMemory;
}
}
/**
* 蟇シ蜃コ蜊穂セ<E7A982>
*/
export const cache = CacheFactory.getInstance();
```
---
#### **豁・鬪、2.3<EFBFBD>壽峩譁ーenv.ts驟咲スョ**
```typescript
// 譁<>サカ<EFBDBB>喘ackend/src/config/env.ts
// 遑ョ菫抒edis驟咲スョ豁」遑ョ隸サ蜿<EFBDBB>
export const config = {
// ... 蜈カ莉夜<E88E89>鄂ョ ...
/** 郛灘ュ倡アサ蝙<EFBDBB> */
cacheType: process.env.CACHE_TYPE || 'memory',
/** Redis驟咲スョ */
redisHost: process.env.REDIS_HOST || 'localhost',
redisPort: parseInt(process.env.REDIS_PORT || '6379', 10),
redisPassword: process.env.REDIS_PASSWORD || undefined,
redisDb: parseInt(process.env.REDIS_DB || '0', 10),
redisUrl: process.env.REDIS_URL || 'redis://localhost:6379',
/** 髦溷<E9ABA6>邀サ蝙<EFBDBB> */
queueType: process.env.QUEUE_TYPE || 'memory',
// ... 蜈カ莉夜<E88E89>鄂ョ ...
};
// 鬪瑚ッ<E7919A><EFBDAF>鄂ョ
export function validateConfig() {
console.log('笨?[Config] 邇ッ蠅<EFBDAF>序驥丞刈霓ス謌仙粥');
console.log('[Config] 蠎皮畑驟咲スョ:');
console.log(` - 郛灘ュ<E78198>: ${config.cacheType}`);
console.log(` - 髦溷<E9ABA6>: ${config.queueType}`);
if (config.cacheType === 'redis') {
console.log(` - Redis: ${config.redisHost}:${config.redisPort}/${config.redisDb}`);
}
}
```
---
### 4.3 Phase 3<>壽悽蝨ー豬玖ッ?笨?
#### **豁・鬪、3.1<EFBFBD><EFBFBD>蟒コRedis豬玖ッ戊<EFBFBD>譛ャ**
```typescript
// 譁<>サカ<EFBDBB>喘ackend/src/scripts/test-redis.ts
import { cache } from '../common/cache/index.js';
import { logger } from '../common/logging/index.js';
async function testRedis() {
console.log('\n<>ァェ 蠑€蟋区オ玖ッ紐edis郛灘ュ<E78198>...\n');
try {
// 豬玖ッ<E78E96>1<EFBFBD>壼渕譛ャ隸サ蜀? console.log('<27>統 豬玖ッ<E78E96>1<EFBFBD>壼渕譛ャ隸サ蜀?);
await cache.set('test:hello', 'world', 10);
const value1 = await cache.get('test:hello');
console.log(` 笨?蜀吝<E89C80>: "hello" 竊?"world"`);
console.log(` 笨?隸サ蜿<EFBDBB>: "${value1}"`);
console.assert(value1 === 'world', '蛟シ蠎碑ッ・蛹ケ驟?);
// 豬玖ッ<E78E96>2<EFBFBD>壼ッケ雎。蠎丞<E8A08E><EFBFBD>
console.log('\n<EFBFBD> <EFBFBD>2<EFBFBD><EFBFBD><EFBFBD>');
const obj = { id: 123, name: '<EFBFBD>', data: [1, 2, 3] };
await cache.set('test:object', obj, 10);
const value2 = await cache.get<typeof obj>('test:object');
console.log(` 笨?蜀吝<E89C80>蟇ケ雎。:`, obj);
console.log(` 笨?隸サ蜿門ッケ雎。:`, value2);
console.assert(value2?.id === 123, 'ID蠎碑ッ<EFBFBD>');
// 豬玖ッ<E78E96>3<EFBFBD>啜TL霑<4C>
console.log('\n<EFBFBD> <EFBFBD>3<EFBFBD>TL霑<EFBFBD><EFBFBD>?<EFBFBD>');
await cache.set('test:expire', 'will-expire', 2);
console.log(` 笨?蜀吝<E89C80><E5909D><EFBFBD>TL=2遘抵シ荏);
console.log(` 竢?遲牙セ<E78999>1遘?..`);
await sleep(1000);
const value3a = await cache.get('test:expire');
console.log(` 笨?1遘貞錘隸サ蜿<EFBDBB>: "${value3a}"`);
console.assert(value3a === 'will-expire', '<EFBFBD>?);
console.log(` 竢?遲牙セ<E78999>2遘?..`);
await sleep(2000);
const value3b = await cache.get('test:expire');
console.log(` 笨?3遘貞錘隸サ蜿<EFBDBB>: ${value3b}`);
console.assert(value3b === null, '蠎碑ッ・蟾イ霑<EFBDB2><E99C91>?);
// 豬玖ッ<E78E96>4<EFBFBD>喇as蜥慧elete
console.log('\n<EFBFBD> <EFBFBD>4<EFBFBD>as蜥慧elete');
await cache.set('test:delete', 'to-be-deleted', 10);
const exists1 = await cache.has('test:delete');
console.log(` 笨?蜀吝<E89C80>蜷仔xists: ${exists1}`);
await cache.delete('test:delete');
const exists2 = await cache.has('test:delete');
console.log(` 笨?蛻<>髯、蜷仔xists: ${exists2}`);
console.assert(!exists2, '<EFBFBD>?);
// 豬玖ッ<E78E96>5<EFBFBD>壼、ァ蟇ケ雎。<E99B8E>?0KB<4B>? console.log('\n<>統 豬玖ッ<E78E96>5<EFBFBD>壼、ァ蟇ケ雎。郛灘ュ假シ域ィ。諡櫚LM扈捺棡<E68DBA>?);
const bigObj = {
literatureId: 'xxx',
fields: {
<EFBFBD>: 'RCT',
? '500',
<EFBFBD>: '闕ッ迚ゥA 100mg',
// ... 12荳ェ蟄玲ョ? },
metadata: {
model: 'deepseek-v3',
tokens: 8000,
timestamp: Date.now(),
},
rawOutput: 'x'.repeat(40000), // 讓。諡溷、ァ霎灘<E99C8E>? };
const start = Date.now();
await cache.set('test:bigobj', bigObj, 3600);
const value5 = await cache.get('test:bigobj');
const duration = Date.now() - start;
const size = JSON.stringify(bigObj).length;
console.log(` 笨?蜀吝<E89C80>+隸サ蜿門、ァ蟇ケ雎? ${size} bytes<65>€玲慮 ${duration}ms`);
console.assert(value5 !== null, '蠎碑ッ・閭ス隸サ蜿?);
console.log('\n笨?€€ソ<EFBFBD>n');
process.exit(0);
} catch (error) {
console.error('\n笶?:', error);
process.exit(1);
}
}
function sleep(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms));
}
// 霑占。梧オ玖ッ<E78E96>
testRedis();
```
#### **豁・鬪、3.2<EFBFBD>壽鴬陦梧オ玖ッ?*
```bash
cd backend
# 1. 蜷ッ蜉ィ蜷守ォッ<EFBDAB>育。ョ菫抒edis驟咲スョ逕滓譜<E6BB93>?npm run dev
# 2. 譁ー蠑€€荳ェ扈育ォッ<EFBDAB>瑚ソ占。梧オ玖ッ戊<EFBDAF>譛ャ
npx tsx src/scripts/test-redis.ts
```
**鬚<>悄霎灘<E99C8E>**<EFBFBD>?```
<EFBFBD>ァェ 蠑€蟋区オ玖ッ紐edis郛灘ュ<E78198>...
<EFBFBD>統 豬玖ッ<E78E96>1<EFBFBD>壼渕譛ャ隸サ蜀? 笨?蜀吝<E89C80>: "hello" 竊?"world"
笨?隸サ蜿<EFBDBB>: "world"
<EFBFBD>統 豬玖ッ<E78E96>2<EFBFBD>壼ッケ雎。蠎丞<E8A08E><EFBFBD>
笨?蜀吝<E89C80>蟇ケ雎。: { id: 123, name: '豬玖ッ<E78E96>', data: [ 1, 2, 3 ] }
笨?隸サ蜿門ッケ雎。: { id: 123, name: '豬玖ッ<E78E96>', data: [ 1, 2, 3 ] }
<EFBFBD>統 豬玖ッ<E78E96>3<EFBFBD>啜TL霑<4C><EFBFBD>?遘抵シ<E68AB5>
笨?蜀吝<E89C80><E5909D><EFBFBD>TL=2遘抵シ<E68AB5>
竢?遲牙セ<E78999>1遘?..
笨?1遘貞錘隸サ蜿<EFBDBB>: "will-expire"
竢?遲牙セ<E78999>2遘?..
笨?3遘貞錘隸サ蜿<EFBDBB>: null
<EFBFBD>統 豬玖ッ<E78E96>4<EFBFBD>喇as蜥慧elete
笨?蜀吝<E89C80>蜷仔xists: true
笨?蛻<>髯、蜷仔xists: false
<EFBFBD>統 豬玖ッ<E78E96>5<EFBFBD>壼、ァ蟇ケ雎。郛灘ュ假シ域ィ。諡櫚LM扈捺棡<E68DBA>? 笨?蜀吝<E89C80>+隸サ蜿門、ァ蟇ケ雎? 40123 bytes<65>€玲慮 5ms
笨?謇€譛画オ玖ッ暮€夊ソ<E5A48A>シ?```
---
#### **豁・鬪、3.3<EFBFBD>壽オ玖ッ穂ク壼苅莉」遐?*
```bash
# 1. 豬玖ッ菱ealthCheckService<63><65>C讓。蝮暦シ?# 荳贋シ<E8B48B>€荳ェExcel譁<6C>サカ<EFBDBB>梧衍逵区律蠢暦シ<E69AA6>
[HealthCheck] Cache miss, processing file
[HealthCheck] Check completed
[HealthCheck] Cache SET: health:xxx, TTL=86400
# 隨ャ莠梧ャ。荳贋シ<E8B48B>蜷御ク€荳ェ譁<EFBDAA>サ?[HealthCheck] Cache hit 竊?謌仙粥莉山edis隸サ蜿<EFBDBB>
```
```bash
# 2. 豬玖ッ畢LM12FieldsService<63><65>SL讓。蝮暦シ?# 謠蝉コ、蜈ィ譁<EFBDA8>、咲ュ帑ササ蜉。<E89C89>梧衍逵区律蠢暦シ<E69AA6>
[LLM12FieldsService] 隹<>畑LLM謠仙叙12蟄玲ョオ
[LLM12FieldsService] Result cached with key: fulltext:xxx
[LLM12FieldsService] 郛灘ュ伜<EFBDAD>蜈・謌仙粥
# 驥肴眠霑占。悟酔荳€遽⑰DF
[LLM12FieldsService] Cache hit, returning cached result 竊?闃ら怐API雍ケ逕ィ<E98095>?```
---
### 4.4 Phase 4<>夐仭驥御コ然edis驟咲スョ 笨?
#### **豁・鬪、4.1<EFBFBD>夊エュ荵ーRedis螳樔セ<EFBFBD>**
1. 逋サ蠖暮仭驥御コ第而蛻カ蜿ー
2. 霑帛<E99C91> **莠第焚謐ョ蠎<EFBDAE> Redis** 莠ァ蜩<EFBDA7>。?3. 轤ケ蜃サ **蛻帛サコ螳樔セ<E6A894>**
4. 謖臥<E8AC96>謌ェ蝗セ驟咲スョ騾画叫<E794BB>? - 莠ァ蜩<EFBDA7>シ啌edis 蠑€貅千沿
- 驛ィ鄂イ讓。蠑擾シ壻コ大次逕滂シ磯ォ伜庄逕ィ<E98095>? - 蝨ー蝓滂シ壼克蛹?<3F>亥圏莠ャ<E88EA0>€€?**荳惨AE蜷悟慍蝓滂シ<E6BB82>**
- 迚域悽<E59F9F>啌edis 7.0
- 蛻<>援隗<E68FB4><E99A97><EFBFBD>?56 MB
- 莉倩エケ譁ケ蠑擾シ壼桁蟷エ蛹<EFBDB4><E89BB9>?5. 謠蝉コ、隶「蜊募ケカ謾ッ莉?
#### **豁・鬪、4.2<EFBFBD><EFBFBD>鄂ョ逋ス蜷榊黒**
```
髦ソ驥御コ第而蛻カ蜿ー 竊?Redis螳樔セ<E6A894> 竊?逋ス蜷榊黒隶セ鄂?
豺サ蜉<EFBFBD><EFBFBD>?1. 譛ャ蝨ー蠑€蜿選P<E981B8>育畑莠取悽蝨ー豬玖ッ包シ<E58C85>
- 菴<><EFBFBD><E980A7>鄂選P/32
2. SAE蠎皮畑IP<49>育函莠ァ邇ッ蠅<EFBDAF><EFBFBD>
- 0.0.0.0/0 <20>井クエ譌カ<E8AD8C>悟錘扈ュ謾ケ荳コSAE VPC<50>? 謌? - SAE螳樔セ狗噪VPC鄂第ョオ
```
#### **豁・鬪、4.3<EFBFBD>夊執蜿冶ソ樊磁菫。諱?*
```
髦ソ驥御コ第而蛻カ蜿ー 竊?Redis螳樔セ<E6A894> 竊?霑樊磁菫。諱ッ
螟榊宛莉・荳倶ソ。諱ッ<EFBFBD>?- 霑樊磁蝨ー蝮€<E89DAE>嗷-xxxxxxxxxxxx.redis.rds.aliyuncs.com
- 遶ッ蜿」<E89CBF>?379
- 螳樔セ紀D<E7B480>嗷-xxxxxxxxxxxx
- 蟇<><E89F87><EFBFBD>シ夂せ蜃?菫ョ謾ケ蟇<EFBDB9><E89F87><EFBFBD>"隶セ鄂ョ
```
#### **豁・鬪、4.4<EFBFBD>壽峩譁ーSAE邇ッ蠅<EFBFBD>序驥<EFBFBD>**
```
髦ソ驥御コ第而蛻カ蜿ー 竊?SAE蠎皮畑 竊?驟咲スョ邂。逅<EFBDA1> 竊?邇ッ蠅<EFBDAF>序驥<E5BA8F>
豺サ蜉<EFBFBD><EFBFBD>?CACHE_TYPE=redis
REDIS_HOST=r-xxxxxxxxxxxx.redis.rds.aliyuncs.com
REDIS_PORT=6379
REDIS_PASSWORD=菴<>隶セ鄂ョ逧<EFBDAE><EFBFBD><EFBDAF><EFBFBD>
REDIS_DB=0
```
---
### 4.5 Phase 5<>壼星逕ィRedis髦溷<E9ABA6> <20>閥 **蠢<>。サ螳樊命**
> **驥崎ヲ∝序譖エ**<2A>夂サ丞<EFBDBB>譫撰シ軍edis髦溷<E9ABA6>蟇ケASL蜥轡C Tool B讓。蝮玲<E89DAE>?*蠢<>。サ逧?*<2A>御ク肴弍蜿ッ騾臥噪<E887A5>?
> **逅<>罰**<2A>?蟆乗慮髟ソ莉サ蜉。蝨ィSAE邇ッ蠅<EFBDAF>ク倶ク咲畑Redis髦溷<E9ABA6>螟ア雍・邇?> 95%
#### **豁・鬪、5.1<EFBFBD>壼ョ芽」<EFBFBD>ullMQ**
```bash
cd backend
npm install bullmq --save
# BullMQ蟾イ蝨ィpackage.json荳ュ<E88DB3>悟宵髴€遑ョ隶、螳芽」<E88ABD>
npm list bullmq
# 蠎碑ッ・譏セ遉コ<E98189>喘ullmq@5.65.0
```
#### **豁・鬪、5.2<EFBFBD>壼ョ樒鴫RedisQueue.ts**
```typescript
// 譁<>サカ<EFBDBB>喘ackend/src/common/jobs/RedisQueue.ts
import { Queue, Worker, Job as BullJob, QueueEvents } from 'bullmq';
import type { Job, JobQueue, JobHandler } from './types.js';
import { logger } from '../logging/index.js';
import { config } from '../../config/env.js';
/**
* Redis髦溷<E9ABA6>螳樒鴫<E6A892>亥渕莠撮ullMQ<4D>? *
* 譬ク蠢<EFBDB8>粥閭ス<E996AD>? * - 莉サ蜉。謖∽ケ<E288BD><EFBFBD>亥ョ樔セ矩㍾蜷ッ荳堺ク「螟ア<E89E9F><EFBDB1>
* - 閾ェ蜉ィ驥崎ッ包シ亥、ア雍・蜷取欠謨ー騾€驕ソ<E9A995><EFBDBF>
* - 蛻<><EFBFBD>シ丈ササ蜉。蛻<EFBDA1><E89BBB><EFBFBD>亥、壼ョ樔セ句刻隹<E588BB><EFBFBD>
* - 霑帛コヲ霍溯クェ
*/
export class RedisQueue implements JobQueue {
private queues: Map<string, Queue> = new Map();
private workers: Map<string, Worker> = new Map();
private queueEvents: Map<string, QueueEvents> = new Map();
private connection = {
host: config.redisHost,
port: config.redisPort,
password: config.redisPassword,
db: config.redisDb,
};
/**
* 謗ィ騾∽ササ蜉。蛻ー髦溷<E9ABA6>
*/
async push<T = any>(type: string, data: T, options?: any): Promise<Job> {
try {
// 闔キ蜿匁<E89CBF>蛻帛サコ髦溷<E9ABA6>? let queue = this.queues.get(type);
if (!queue) {
queue = new Queue(type, {
connection: this.connection,
defaultJobOptions: {
removeOnComplete: 100, // 菫晉蕗譛€霑?00荳ェ螳梧<E89EB3>莉サ蜉? removeOnFail: false, // 螟ア雍・莉サ蜉。荳榊唖髯、<E9ABAF>井セソ莠取賜譟・<E8AD9F>? attempts: 3, // 螟ア雍・驥崎ッ<E5B48E>3谺? backoff: {
type: 'exponential',
delay: 2000, // 2遘偵€?遘偵€?遘? },
}
});
this.queues.set(type, queue);
}
// 豺サ蜉<EFBDBB>莉サ蜉。
const job = await queue.add(type, data, {
...options,
jobId: options?.jobId, // 謾ッ謖∬<E8AC96>螳壻ケ泳obId
});
logger.info(`[RedisQueue] 莉サ蜉。蜈・髦滓<E9ABA6>蜉歔, {
type,
jobId: job.id,
dataSize: JSON.stringify(data).length
});
return {
id: job.id!,
type,
data,
status: 'pending',
createdAt: new Date(),
};
} catch (error) {
logger.error(`[RedisQueue] 莉サ蜉。蜈・髦溷、ア雍・`, { type, error });
throw error;
}
}
/**
* 豕ィ蜀御ササ蜉。螟<EFBDA1>炊蝎? */
process<T = any>(type: string, handler: JobHandler<T>): void {
try {
// 蛻帛サコWorker
const worker = new Worker(
type,
async (job: BullJob) => {
logger.info(`[RedisQueue] 蠑€蟋句、<E58FA5>炊莉サ蜉。`, {
type,
jobId: job.id,
attemptsMade: job.attemptsMade,
attemptsTotal: job.opts.attempts
});
const startTime = Date.now();
try {
// 隹<>畑荳壼苅螟<E88B85>炊蜃ス謨ー
const result = await handler({
id: job.id!,
type,
data: job.data as T,
status: 'processing',
createdAt: new Date(job.timestamp),
});
const duration = Date.now() - startTime;
logger.info(`[RedisQueue] 莉サ蜉。螟<EFBDA1>炊謌仙粥`, {
type,
jobId: job.id,
duration: `${duration}ms`
});
return result;
} catch (error) {
const duration = Date.now() - startTime;
logger.error(`[RedisQueue] 莉サ蜉。螟<EFBDA1>炊螟ア雍・`, {
type,
jobId: job.id,
attemptsMade: job.attemptsMade,
duration: `${duration}ms`,
error: error instanceof Error ? error.message : 'Unknown error'
});
throw error; // 謚帛<E8AC9A>髞呵ッッ<EFBDAF>瑚ァヲ蜿鷹㍾隸? }
},
{
connection: this.connection,
concurrency: 1, // 豈丈クェWorker蟷カ蜿大、<E5A4A7>炊1荳ェ莉サ蜉? }
);
this.workers.set(type, worker);
// 逶大成Worker莠倶サカ
worker.on('completed', (job) => {
logger.info(`[RedisQueue] 笨?莉サ蜉。螳梧<E89EB3>`, {
type,
jobId: job.id,
returnvalue: job.returnvalue
});
});
worker.on('failed', (job, err) => {
logger.error(`[RedisQueue] 笶?莉サ蜉。螟ア雍・`, {
type,
jobId: job?.id,
attemptsMade: job?.attemptsMade,
error: err.message,
stack: err.stack
});
});
worker.on('error', (err) => {
logger.error(`[RedisQueue] Worker髞呵ッッ`, { type, error: err.message });
});
logger.info(`[RedisQueue] Worker蟾イ豕ィ蜀形, { type });
} catch (error) {
logger.error(`[RedisQueue] Worker豕ィ蜀悟、ア雍・`, { type, error });
throw error;
}
}
/**
* 闔キ蜿紋ササ蜉。迥カ諤? */
async getJob(id: string): Promise<Job | null> {
try {
// 驕榊紙謇€譛蛾弌蛻玲衍謇セ莉サ蜉? for (const [type, queue] of this.queues) {
const job = await queue.getJob(id);
if (job) {
return {
id: job.id!,
type,
data: job.data,
status: await this.getJobStatus(job),
progress: job.progress as number || 0,
createdAt: new Date(job.timestamp),
error: job.failedReason,
};
}
}
return null;
} catch (error) {
logger.error(`[RedisQueue] getJob螟ア雍・`, { id, error });
return null;
}
}
/**
* 闔キ蜿紋ササ蜉。迥カ諤? */
private async getJobStatus(job: BullJob): Promise<string> {
const state = await job.getState();
switch (state) {
case 'completed': return 'completed';
case 'failed': return 'failed';
case 'active': return 'processing';
case 'waiting': return 'pending';
case 'delayed': return 'pending';
default: return 'pending';
}
}
/**
* 譖エ譁ー莉サ蜉。霑帛コヲ
*/
async updateProgress(id: string, progress: number, message?: string): Promise<void> {
try {
for (const queue of this.queues.values()) {
const job = await queue.getJob(id);
if (job) {
await job.updateProgress(progress);
if (message) {
await job.log(message);
}
logger.debug(`[RedisQueue] 霑帛コヲ譖エ譁ー`, { id, progress, message });
return;
}
}
logger.warn(`[RedisQueue] 莉サ蜉。荳榊ュ伜惠<E4BC9C>梧裏豕墓峩譁ー霑帛コヲ`, { id });
} catch (error) {
logger.error(`[RedisQueue] 譖エ譁ー霑帛コヲ螟ア雍・`, { id, error });
}
}
/**
* 蜿匁カ井ササ蜉。
*/
async cancelJob(id: string): Promise<boolean> {
try {
for (const queue of this.queues.values()) {
const job = await queue.getJob(id);
if (job) {
await job.remove();
logger.info(`[RedisQueue] 莉サ蜉。蟾イ蜿匁カ<E58C81>, { id });
return true;
}
}
logger.warn(`[RedisQueue] 莉サ蜉。荳榊ュ伜惠<E4BC9C>梧裏豕募叙豸<E58F99>, { id });
return false;
} catch (error) {
logger.error(`[RedisQueue] 蜿匁カ井ササ蜉。螟ア雍・`, { id, error });
return false;
}
}
/**
* 驥崎ッ募、ア雍・莉サ蜉。
*/
async retryJob(id: string): Promise<boolean> {
try {
for (const queue of this.queues.values()) {
const job = await queue.getJob(id);
if (job) {
await job.retry();
logger.info(`[RedisQueue] 莉サ蜉。蟾イ驥崎ッ描, { id });
return true;
}
}
logger.warn(`[RedisQueue] 莉サ蜉。荳榊ュ伜惠<E4BC9C>梧裏豕暮㍾隸描, { id });
return false;
} catch (error) {
logger.error(`[RedisQueue] 驥崎ッ穂ササ蜉。螟ア雍・`, { id, error });
return false;
}
}
/**
* 貂<>炊譌ァ莉サ蜉? */
async cleanup(olderThan: number = 86400000): Promise<number> {
try {
let totalCleaned = 0;
for (const [type, queue] of this.queues) {
// 貂<>炊螳梧<E89EB3><EFBFBD>ササ蜉。<E89C89>井ソ晉蕗譛€霑?00荳ェ<E88DB3><EFBDAA>
const completed = await queue.clean(olderThan, 100, 'completed');
// 貂<>炊螟ア雍・逧<EFBDA5>ササ蜉。<E89C89>井ソ晉蕗譛€霑?0荳ェ<E88DB3><EFBDAA>
const failed = await queue.clean(olderThan, 50, 'failed');
const cleaned = completed.length + failed.length;
totalCleaned += cleaned;
if (cleaned > 0) {
logger.info(`[RedisQueue] 髦溷<E9ABA6><EFBFBD>炊螳梧<E89EB3>`, {
type,
completed: completed.length,
failed: failed.length
});
}
}
return totalCleaned;
} catch (error) {
logger.error(`[RedisQueue] 貂<>炊莉サ蜉。螟ア雍・`, { error });
return 0;
}
}
/**
* 蜈ウ髣ュ謇€譛芽ソ樊磁<E6A88A>井シ倬寉蜈ウ髣ュ<E9ABA3>? */
async close(): Promise<void> {
try {
// 蜈ウ髣ュ謇€譛姥orkers
for (const [type, worker] of this.workers) {
await worker.close();
logger.info(`[RedisQueue] Worker蟾イ蜈ウ髣ュ`, { type });
}
// 蜈ウ髣ュ謇€譛渦ueues
for (const [type, queue] of this.queues) {
await queue.close();
logger.info(`[RedisQueue] Queue蟾イ蜈ウ髣ュ`, { type });
}
// 蜈ウ髣ュ謇€譛渦ueueEvents
for (const [type, events] of this.queueEvents) {
await events.close();
logger.info(`[RedisQueue] QueueEvents蟾イ蜈ウ髣ュ`, { type });
}
logger.info(`[RedisQueue] 謇€譛芽ソ樊磁蟾イ蜈ウ髣ュ`);
} catch (error) {
logger.error(`[RedisQueue] 蜈ウ髣ュ霑樊磁螟ア雍・`, { error });
}
}
}
```
#### **豁・鬪、5.3<EFBFBD>壽峩譁ーJobFactory謾ッ謖ヽedis髦溷<EFBFBD>**
```typescript
// 譁<>サカ<EFBDBB>喘ackend/src/common/jobs/JobFactory.ts
import { JobQueue } from './types.js'
import { MemoryQueue } from './MemoryQueue.js'
import { RedisQueue } from './RedisQueue.js' // 竊?譁ー蠅<EFBDB0>
import { logger } from '../logging/index.js'
import { config } from '../../config/env.js'
export class JobFactory {
private static instance: JobQueue | null = null
static getInstance(): JobQueue {
if (!this.instance) {
this.instance = this.createQueue()
}
return this.instance
}
private static createQueue(): JobQueue {
const queueType = config.queueType || 'memory'
logger.info('[JobFactory] 蛻晏ァ句喧莉サ蜉。髦溷<E9ABA6>?, { queueType });
switch (queueType) {
case 'redis': // 竊?譁ー蠅<EFBDB0>
return this.createRedisQueue()
case 'memory':
return this.createMemoryQueue()
default:
logger.warn(`[JobFactory] Unknown QUEUE_TYPE: ${queueType}, fallback to memory`)
return this.createMemoryQueue()
}
}
/**
* 蛻帛サコRedis髦溷<E9ABA6><E6BAB7>亥クヲ髯咲コァ遲也払<E4B99F>? */
private static createRedisQueue(): JobQueue {
try {
logger.info('[JobFactory] 豁」蝨ィ霑樊磁Redis髦溷<E9ABA6>...');
const redisQueue = new RedisQueue();
logger.info('[JobFactory] 笨?Redis髦溷<E9ABA6>蛻晏ァ句喧謌仙<E8AC8C>?);
return redisQueue;
} catch (error) {
logger.error('[JobFactory] 笶?Redis髦溷<E9ABA6>蛻晏ァ句喧螟ア雍・<E99B8D>碁剄郤ァ蛻ー蜀<EFBDB0>ュ倬弌蛻?, { error });
return this.createMemoryQueue();
}
}
private static createMemoryQueue(): MemoryQueue {
logger.info('[JobFactory] 菴ソ逕ィ蜀<EFBDA8>ュ倬弌蛻<E5BC8C>')
const queue = new MemoryQueue()
// 螳壽悄貂<E68284><EFBFBD>磯∩蜈榊<E89C88>蟄俶ウ<E4BFB6>シ擾シ<E693BE>
if (process.env.NODE_ENV !== 'test') {
setInterval(() => {
queue.cleanup()
}, 60 * 60 * 1000)
}
return queue
}
static reset(): void {
this.instance = null
}
}
```
#### **豁・鬪、5.4<EFBFBD>壻ソョ謾ケ荳壼苅莉」遐∽スソ逕ィ髦溷<EFBFBD>?*
```typescript
// 遉コ萓具シ哂SL譁<4C>鍵遲幃€画隼騾?// 譁<>サカ<EFBDBB>喘ackend/src/modules/asl/services/screeningService.ts
import { jobQueue } from '../../../common/jobs/index.js';
export async function startScreeningTask(projectId: string, userId: string) {
// 1. 蛻帛サコ謨ー謐ョ蠎謎ササ蜉。隶ー蠖? const task = await prisma.aslScreeningTask.create({
data: {
projectId,
status: 'pending',
totalItems: literatures.length,
// ...
}
});
// 2. 謗ィ騾∝芦Redis髦溷<E9ABA6><E6BAB7>井ク埼仆蝪櫁ッキ豎ゑシ? await jobQueue.push('asl:title-screening', {
taskId: task.id,
projectId,
literatureIds: literatures.map(lit => lit.id),
});
logger.info('莉サ蜉。蟾イ蜈・髦?, { taskId: task.id });
// 3. 遶句叉霑泌屓<E6B38C>亥燕遶ッ霓ョ隸「霑帛コヲ<EFBDBA><EFBDA6>
return task;
}
// 豕ィ蜀係orker<65>亥惠蠎皮畑蜷ッ蜉ィ譌カ<E8AD8C><EFBDB6>
// 譁<>サカ<EFBDBB>喘ackend/src/index.ts
jobQueue.process('asl:title-screening', async (job) => {
const { taskId, projectId, literatureIds } = job.data;
logger.info('蠑€蟋句、<E58FA5>炊遲幃€我ササ蜉?, { taskId, total: literatureIds.length });
for (let i = 0; i < literatureIds.length; i++) {
const literatureId = literatureIds[i];
// 螟<>炊蜊慕ッ<E68595>枚迪ョ
await processSingleLiterature(literatureId, projectId);
// 譖エ譁ー霑帛コヲ
const progress = ((i + 1) / literatureIds.length) * 100;
await jobQueue.updateProgress(job.id, progress);
// 譖エ譁ー謨ー謐ョ蠎? await prisma.aslScreeningTask.update({
where: { id: taskId },
data: { processedItems: i + 1 }
});
}
// 譬<>ョー螳梧<E89EB3>
await prisma.aslScreeningTask.update({
where: { id: taskId },
data: { status: 'completed', completedAt: new Date() }
});
logger.info('遲幃€我ササ蜉。螳梧<E89EB3>?, { taskId });
return { success: true, processed: literatureIds.length };
});
```
#### **豁・鬪、5.5<EFBFBD>壽峩譁?env驟咲スョ**
```env
# backend/.env
# ==================== 莉サ蜉。髦溷<E9ABA6>驟咲スョ ====================
QUEUE_TYPE=redis # 竊?謾ケ荳コredis
# Redis驟咲スョ<EFBDBD>井ク守シ灘ュ伜<EFBDAD>逕ィ<E98095>?REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0
```
#### **豁・鬪、5.6<EFBFBD>壽オ玖ッ紐edis髦溷<EFBFBD>**
```bash
# 1. 蜷ッ蜉ィ蜷守ォッ
cd backend
npm run dev
# 蠎碑ッ・逵句芦譌・蠢暦シ?# [JobFactory] 豁」蝨ィ霑樊磁Redis髦溷<E9ABA6>...
# [JobFactory] 笨?Redis髦溷<E9ABA6>蛻晏ァ句喧謌仙<E8AC8C>?
# 2. 謠蝉コ、豬玖ッ穂ササ蜉。<E89C89>井スソ逕ィREST Client謌鳳ostman<61>?POST http://localhost:3001/api/v1/asl/projects/:projectId/screening
# 3. 隗ょッ滓律蠢<E5BE8B>
# [RedisQueue] 莉サ蜉。蜈・髦滓<E9ABA6><EFBFBD> { type: 'asl:title-screening', jobId: '1' }
# [RedisQueue] 蠑€蟋句、<E58FA5>炊莉サ蜉?{ type: 'asl:title-screening', jobId: '1' }
# [RedisQueue] 笨?莉サ蜉。螳梧<E89EB3> { type: 'asl:title-screening', jobId: '1' }
# 4. 豬玖ッ募ョ樔セ矩㍾蜷ッ諱「螟<EFBDA2>
# 謠蝉コ、莉サ蜉。 竊?遲牙セ<E78999><EFBFBD>炊蛻?0% 竊?Ctrl+C蛛懈ュ「 竊?驥肴眠蜷ッ蜉ィ
# 莉サ蜉。蠎碑ッ・閾ェ蜉ィ莉山edis諱「螟榊ケカ扈ァ扈ュ螟<EFBDAD><E89E9F>?```
---
## 5. 豬玖ッ墓婿譯<E5A9BF>
### 5.1 蜊募<E89C8A>豬玖ッ墓ク<E5A293>
| 豬玖ッ暮。?| 鬚<>悄扈捺棡 | 螳樣刔扈捺棡 | 迥カ諤?|
|--------|---------|---------|------|
| Redis霑樊磁豬玖ッ<E78E96> | PONG | 笨?| 筮?蠕<><EFBFBD> |
| 蝓コ譛ャ隸サ蜀呎オ玖ッ<E78E96> | 蛟シ蛹ケ驟?| 笨?| 筮?蠕<><EFBFBD> |
| 蟇ケ雎。蠎丞<E8A08E>蛹匁オ玖ッ?| 蟇ケ雎。螳梧紛 | 笨?| 筮?蠕<><EFBFBD> |
| TTL霑<4C>悄豬玖ッ<E78E96> | 2遘貞錘荳コnull | 笨?| 筮?蠕<><EFBFBD> |
| 螟ァ蟇ケ雎。豬玖ッ包シ<E58C85>50KB<4B>?| <10ms | 笨?| 筮?蠕<><EFBFBD> |
| 髯咲コァ遲也払豬玖ッ<E78E96> | 閾ェ蜉ィ蛻<EFBDA8>困蜀<E59BB0><EFBFBD> | 笨?| 筮?蠕<><EFBFBD> |
### 5.2 髮<><E9ABAE>豬玖ッ墓ク<E5A293>
| 豬玖ッ暮。?| 謫堺ス懈ュ・鬪、 | 鬚<>悄扈捺棡 | 迥カ諤?|
|--------|---------|---------|------|
| **HealthCheckService** | 荳贋シ<E8B48B>Excel 2谺?| 隨?谺。郛灘ュ伜多荳?| 筮?蠕<><EFBFBD> |
| **LLM12FieldsService** | 謠仙叙蜷御ク€PDF 2谺?| 隨?谺。郛灘ュ伜多荳?| 筮?蠕<><EFBFBD> |
| **螟壼ョ樔セ狗シ灘ュ伜<EFBDAD>莠?* | 蜷ッ蜉ィ2荳ェ蜷守ォッ螳樔セ具シ悟ョ樔セ帰蜀吝<E89C80><E5909D>悟ョ樔セ毅隸サ蜿<EFBDBB> | B閭ス隸サ蛻ーA逧<41>シ灘ュ?| 筮?蠕<><EFBFBD> |
| **螳樔セ矩㍾蜷ッ謨ー謐ョ謖∽ケ<E288BD><EFBDB9>?* | 蜀吝<E89C80>郛灘ュ<E78198> 竊?驥榊星蜷守ォッ 竊?隸サ蜿<EFBDBB> | 閭ス隸サ蜿門芦 | 筮?蠕<><EFBFBD> |
### 5.3 蜴句鴨豬玖ッ<E78E96>
```bash
# 菴ソ逕ィab謌殆rk豬玖ッ募ケカ蜿題ッサ蜀<EFBDBB>
# 豬玖ッ<E78E96>1<EFBFBD>壼ケカ蜿大<E89CBF>蜈?ab -n 1000 -c 10 http://localhost:3001/api/test/cache-write
# 豬玖ッ<E78E96>2<EFBFBD>壼ケカ蜿題ッサ蜿?ab -n 10000 -c 50 http://localhost:3001/api/test/cache-read
# 鬚<>悄扈捺棡<E68DBA>?# - QPS > 1000
# - 蜩榊コ疲慮髣エ < 50ms
# - 髞呵ッッ邇?= 0%
```
### 5.4 謨<>囿讓。諡滓オ玖ッ<E78E96>
| 謨<>囿蝨コ譎ッ | 讓。諡滓婿豕<E5A9BF> | 鬚<>悄陦御クコ | 迥カ諤?|
|---------|---------|---------|------|
| **Redis遯∫┯謖よ脂** | `docker stop ai-clinical-redis` | 邉サ扈滄剄郤ァ蛻ー蜀<EFBDB0>ュ倡シ灘ュ假シ悟コ皮畑扈ァ扈ュ霑占。<E58DA0> | 筮?蠕<><EFBFBD> |
| **Redis鄂醍サ懷サカ霑<EFBDB6>** | `tc qdisc add dev eth0 root netem delay 500ms` | 雜<>慮驥崎ッ包シ梧怙扈郁ソ泌屓null | 筮?蠕<><EFBFBD> |
| **Redis蜀<73>ュ俶サ?* | 蜀吝<E89C80>螟ァ驥乗焚謐ョ閾?56MB | 隗ヲ蜿銑RU鬩ア騾撰シ御ク榊スア蜩肴眠蜀吝<E89C80> | 筮?蠕<><EFBFBD> |
| **Redis蟇<73><E89F87><EFBFBD>漠隸ッ** | 菫ョ謾ケ蟇<EFBDB9><E89F87><EFBFBD> | 霑樊磁螟ア雍・<E99B8D>碁剄郤ァ蛻ー蜀<EFBDB0>ュ倡シ灘ュ<E78198> | 筮?蠕<><EFBFBD> |
---
## 6. 鬟朱勦隸<E58BA6>シー荳守シ楢ァ?
### 6.1 鬟朱勦遏ゥ髦オ
| 鬟朱勦 | 荳・驥肴€?| 讎ら紫 | 蠖ア蜩<EFBDB1> | 郛楢ァ」謗ェ譁ス | 迥カ諤?|
|------|--------|------|------|----------|------|
| **Redis霑樊磁螟ア雍・** | <20>閥 鬮?| <20>泯 荳?| 邉サ扈滉ク榊庄逕?| 笨?髯咲コァ遲也払 | 笨?蟾イ螳樒<E89EB3>?|
| **謨ー謐ョ荳「螟ア** | <20>泯 荳?| <20>泙 菴?| 郛灘ュ伜、ア謨<EFBDB1> | 笨?蜈ウ髞ョ謨ー謐ョ蜿悟<E89CBF>DB | 竢?蠕<>ョ樒<EFBDAE>?|
| **蜀<>ュ俶コ「蜃コ<E89C83><EFBDBA>OM<4F>?* | <20>閥 鬮?| <20>泯 荳?| Redis蟠ゥ貅<EFBDA9> | 笨?荳・譬シTTL + 逶第而 | 竢?蠕<>ョ樒<EFBDAE>?|
| **鄂醍サ懷サカ霑<EFBDB6>** | <20>泙 菴?| <20>泙 菴?| 蜩榊コ泌序諷「 | 笨?謇ケ驥乗桃菴<E6A183> | 竢?蜿ッ騾我シ伜<EFBDBC>?|
| **驟咲スョ髞呵ッッ** | <20>泯 荳?| <20>泯 荳?| 蜷ッ蜉ィ螟ア雍・ | 笨?驟咲スョ鬪瑚ッ<E7919A> | 笨?蟾イ螳樒<E89EB3>?|
| **蟇<><E89F87>∵ウ<E288B5>愆** | <20>閥 鬮?| <20>泯 荳?| 謨ー謐ョ豕<EFBDAE>愆 | 笨?KMS邂。逅<EFBDA1> | 竢?蠕<>ョ樒<EFBDAE>?|
### 6.2 蜈ウ髞ョ郛楢ァ」謗ェ譁ス
#### **郛楢ァ」謗ェ譁ス1<EFBDBD>夐剄郤ァ遲也払<E4B99F>亥ソ<E4BAA5>。サ<EFBDA1>?* 笨?```typescript
// 蟾イ蝨ィCacheFactory荳ュ螳樒<E89EB3>?// Redis荳榊庄逕ィ譌カ閾ェ蜉ィ蛻<EFBDA8>困蛻ーMemoryCache
```
#### **郛楢ァ」謗ェ譁ス2<EFBDBD><EFBFBD>髞ョ謨ー謐ョ蜿悟<E89CBF><E6829F>域耳闕撰シ?* 竢?```typescript
// 髴€隕∝惠荳壼苅莉」遐∽クュ豺サ蜉?
// 遉コ萓具シ壻ササ蜉。霑帛コヲ蜿悟<E89CBF>?export async function updateTaskProgress(taskId: string, progress: number) {
// 1. 蜀儚edis<69>亥ソォ騾滓衍隸「<E99AB8><EFBDA2>
await cache.set(`task:${taskId}:progress`, progress, 3600);
// 2. 蜷梧慮蜀僖B<E58396>域戟荵<E6889F><EFBFBD>? await prisma.aslScreeningTask.update({
where: { id: taskId },
data: { processedItems: progress }
});
}
```
#### **郛楢ァ」謗ェ譁ス3<EFBDBD><EFBFBD>蟄倡尅謗ァ<E8AC97>域耳闕撰シ?* 竢?```typescript
// 蛻帛サコ<EFBDBB>喘ackend/src/scripts/monitor-redis.ts
import { cache } from '../common/cache/index.js';
setInterval(async () => {
// 譽€譟・蜀<EFBDA5>ュ倅スソ逕? const info = await redis.info('memory');
const used = parseInt(info.match(/used_memory:(\d+)/)[1]);
const max = 256 * 1024 * 1024;
if (used > max * 0.8) {
logger.warn('笞<><E7AC9E><EFBFBD> Redis蜀<73>ュ倅スソ逕ィ雜<EFBDA8>ソ<EFBFBD>80%', { used, max });
// TODO: 蜿鷹€<E9B7B9>忠髓?驍ョ莉カ蜻願ュヲ
}
}, 60000);
```
#### **郛楢ァ」謗ェ譁ス4<EFBDBD><EFBFBD>鄂ョ鬪瑚ッ<E7919A>シ亥キイ螳樒鴫<E6A892><E9B4AB>** 笨?```typescript
// env.ts荳ュ逧ёalidateConfig()
// 蜷ッ蜉ィ譌カ譽€譟・Redis驟咲スョ
```
---
## 7. 荳顔コソ隶。蛻<EFBDA1>
### 7.1 荳顔コソ譌カ髣エ陦ィ<E999A6><EFBDA8>2.0譖エ譁ー<EFBFBD>?
| 髦カ谿オ | 譌カ髣エ | 莉サ蜉。 | 雍溯エ」莠?| 迥カ諤?|
|------|------|------|--------|------|
| **Phase 1** | Day 1荳雁壕 | 譛ャ蝨ー蠑€蜿醍識蠅<E8AD98>㊥螟?| 蠑€蜿?| 筮?蠕<>€蟋?|
| **Phase 2** | Day 1荳句壕 | 螳樒鴫RedisCacheAdapter | 蠑€蜿?| 筮?蠕<>€蟋?|
| **Phase 3** | Day 2蜈ィ螟ゥ | Redis郛灘ュ俶悽蝨ー豬玖ッ<E78E96> | 蠑€蜿?豬玖ッ<E78E96> | 筮?蠕<>€蟋?|
| **Phase 4** | Day 3荳雁壕 | 髦ソ驥御コ然edis雍ュ荵ー&驟咲スョ | 霑千サエ | 筮?蠕<>€蟋?|
| **Phase 5** | Day 3荳句壕-Day 5 | <20>閥 螳樒鴫RedisQueue<75>亥ソ<E4BAA5>。サ<EFBDA1>榎 蠑€蜿?| 筮?蠕<>€蟋?|
| **Phase 6** | Day 6蜈ィ螟ゥ | Redis髦溷<E9ABA6>譛ャ蝨ー豬玖ッ<E78E96> + 荳壼苅髮<E88B85><E9ABAE> | 蠑€蜿?豬玖ッ<E78E96> | 筮?蠕<>€蟋?|
| **Phase 7** | Day 7荳雁壕 | SAE豬玖ッ慕識蠅<E8AD98>ェ瑚ッ<E7919A> | 蠑€蜿?豬玖ッ<E78E96> | 筮?蠕<>€蟋?|
| **Phase 8** | Day 7荳句壕 | 逕滉コァ邇ッ蠅<EFBDAF>ク顔コソ | 蜈ィ蜻<EFBDA8> | 筮?蠕<>€蟋?|
| **Phase 9** | Day 7譎? | 逶第而隗ょッ滂シ?4蟆乗慮<E4B997>?| 霑千サエ | 筮?蠕<>€蟋?|
**諤サ蟾・菴憺㍼**<2A>?螟ゥ<E89E9F>域ッ泌次隶。蛻貞「槫刈4螟ゥ<E89E9F>御ス<E5BEA1>。ョ菫晄<E88FAB>ク蠢<EFBDB8>粥閭ス蜿ッ逕ィ<E98095><EFBDA8>
### 7.2 荳顔コソ豁・鬪、<E9ACAA>育函莠ァ邇ッ蠅<EFBDAF><EFBFBD>
#### **Step 1<>壼書蟶<E69BB8>燕譽€譟・<E8AD9F><EFBDA5>15蛻<35><EFBFBD>?*
```bash
笨?莉」遐∝キイ謠蝉コ、Git
笨?譛ャ蝨ー豬玖ッ募<EFBDAF>驛ィ騾夊ソ<E5A48A>
笨?髦ソ驥御コ然edis蟾イ蟆ア扈?笨?SAE邇ッ蠅<EFBDAF>序驥丞キイ驟咲ス?笨?蝗樊サ壽婿譯亥キイ蜃<EFBDB2>、?笨?逶第而蟾イ蟆ア扈?```
#### **Step 2<><EFBFBD>蠎ヲ蜿大ク<E5A4A7><EFBFBD>30蛻<30><EFBFBD>?*
```
1. SAE謗ァ蛻カ蜿?竊?騾画叫蠎皮畑
2. 蠎皮畑驛ィ鄂イ 竊?蛻<>音蜿大ク<E5A4A7>
3. 隨?謇ケ<E8AC87><EFBDB9>10%螳樔セ具シ郁ァょッ?5蛻<35><EFBFBD>?4. 隨?謇ケ<E8AC87><EFBDB9>50%螳樔セ具シ郁ァょッ?0蛻<30><EFBFBD>?5. 隨?謇ケ<E8AC87><EFBDB9>100%螳樔セ<E6A894>
```
#### **Step 3<>夐ェ瑚ッ<E7919A><EFBFBD>15蛻<35><EFBFBD>?*
```bash
# 1. 譽€譟・Redis霑樊磁
curl https://your-api.com/api/health
# 蠎碑ッ・霑泌屓<E6B38C>嘴 cache: "redis", status: "ok" }
# 2. 豬玖ッ慕シ灘ュ伜<EFBDAD>蜈・
# 荳贋シ<E8B48B>Excel 竊?譟・逵区律蠢<E5BE8B> 竊?遑ョ隶、Redis蜀吝<E89C80>
# 3. 豬玖ッ慕シ灘ュ倩ッサ蜿<EFBDBB>
# 蜀肴ャ。荳贋シ<E8B48B> 竊?譟・逵区律蠢<E5BE8B> 竊?遑ョ隶、郛灘ュ伜多荳ュ
# 4. 譽€譟・Redis蜀<73><EFBFBD>
髦ソ驥御コ第而蛻カ蜿ー 竊?Redis逶第而 竊?蜀<>ュ倅スソ逕ィ
```
#### **Step 4<>夂尅謗ァ隗ょッ滂シ<E6BB82>24蟆乗慮<E4B997>?*
```
蜈ウ豕ィ謖<EFBFBD><EFBFBD><EFBFBD>シ?- Redis霑樊磁謨ー<E8ACA8>亥コ碑ッ・遞ウ螳夲シ?- 蜀<>ュ倅スソ逕ィ邇<EFBDA8>シ亥コ碑ッ・ < 50%<25>?- 郛灘ュ伜多荳ュ邇<EFBDAD>シ亥コ碑ッ・ > 80%<25>?- 蠎皮畑髞呵ッッ譌・蠢暦シ亥コ碑ッ・譌<EFBDA5>Redis逶ク蜈ウ髞呵ッッ<EFBDAF>?- API謌先悽<E58588>亥コ碑ッ・荳矩剄<E79FA9><E58984>
```
---
## 8. 蝗樊サ壽婿譯<E5A9BF>
### 8.1 蠢ォ騾溷屓貊夲シ<E5A4B2>5蛻<35>帖蜀<E5B896><EFBFBD>
#### **蝨コ譎ッ1<EFBDAF>啌edis霑樊磁螟ア雍・<E99B8D>悟コ皮畑譌<E79591>豕募星蜉?*
```bash
# 譁ケ豕<EFBDB9>1<EFBFBD>壻ソョ謾ケSAE邇ッ蠅<EFBDAF>序驥<E5BA8F>
髦ソ驥御コ第而蛻カ蜿ー 竊?SAE蠎皮畑 竊?驟咲スョ邂。逅<EFBDA1> 竊?邇ッ蠅<EFBDAF>序驥<E5BA8F>
菫ョ謾ケ<EFBFBD>咾ACHE_TYPE=memory
菫晏ュ<EFBFBD> 竊?蠎皮畑驥榊星
# 譁ケ豕<EFBDB9>2<EFBFBD>夐㍾譁ー驛ィ鄂イ荳贋ク€荳ェ迚域<E8BF9A>?SAE謗ァ蛻カ蜿?竊?蠎皮畑驛ィ鄂イ 竊?迚域悽邂。逅<EFBDA1> 竊?蝗樊サ<E6A88A>
```
#### **蝨コ譎ッ2<EFBDAF>啌edis諤ァ閭ス髣ョ鬚假シ悟桃蠎泌序諷?*
```bash
# 荳エ譌カ髯咲コァ蛻ー蜀<EFBDB0>ュ倡シ灘ュ?CACHE_TYPE=memory
# 謌紋ソ晉蕗Redis菴<73>€譟・鄂醍サ?ping r-xxxxxxxxxxxx.redis.rds.aliyuncs.com
```
#### **蝨コ譎ッ3<EFBDAF>啌edis蜀<73>ュ俶サ。<EFBDBB>梧裏豕募<E8B195>蜈・**
```bash
# 譁ケ豕<EFBDB9>1<EFBFBD>壽ク<E5A3BD>炊Redis<69>亥些髯ゥ<E9ABAF><EFBDA9>シ?redis-cli -h r-xxx.redis.rds.aliyuncs.com -a password
> FLUSHDB
# 譁ケ豕<EFBDB9>2<EFBFBD>壼合郤ァRedis隗<73><E99A97>
髦ソ驥御コ第而蛻カ蜿ー 竊?Redis螳樔セ<E6A894> 竊?蜿倬<E89CBF> 竊?512MB
```
### 8.2 蝗樊サ壽」€譟・貂<EFBDA5><E8B282>?
```bash
笨?蠎皮畑閭ス蜷ヲ豁」蟶ク蜷ッ蜉ィ<E89C89>?笨?郛灘ュ俶弍蜷ヲ蟾・菴懶シ亥<EFBDBC>蟄俶ィ。蠑擾シ会シ?笨?API蜩榊コ疲弍蜷ヲ豁」蟶ク<E89FB6>?笨?髞呵ッッ譌・蠢玲弍蜷ヲ貂<EFBDA6><EFBFBD>?笨?逕ィ謌キ譏ッ蜷ヲ閭ス豁」蟶ク菴ソ逕ィ<E98095><EFBDA8>
```
---
## 9. 逶第而荳手ソ千サ?
### 9.1 逶第而謖<E8808C><E8AC96><EFBFBD>
#### **Redis謖<73><E8AC96><EFBFBD>シ磯仭驥御コ第而蛻カ蜿ー<E89CBF><EFBDB0>**
| 謖<><E8AC96><EFBFBD> | 豁」蟶ク闌<EFBDB8>峩 | 蜻願ュヲ髦亥€?| 螟<>炊譁ケ譯<EFBDB9> |
|------|---------|---------|---------|
| **蜀<>ュ倅スソ逕ィ邇?* | < 50% | > 80% | 譽€譟・螟ァkey<65>€<E7919A>剔蜊<E58994><E89C8A> |
| **霑樊磁謨?* | < 50 | > 100 | 譽€譟・霑樊磁豕<E7A381>シ?|
| **QPS** | < 1000 | > 5000 | 閠<>剔蛻<E58994>援 |
| **蜻ス荳ュ邇?* | > 80% | < 50% | 譽€譟・郛灘ュ倡ュ也<EFBDAD>?|
| **蜩榊コ疲慮髣エ** | < 5ms | > 50ms | 譽€譟・鄂醍サ?|
#### **蠎皮畑謖<E79591><E8AC96><EFBFBD><EFBFBD>AE譌・蠢暦シ?*
```bash
# 譟・謇セRedis逶ク蜈ウ髞呵ッッ
grep "Redis" logs/app.log | grep "ERROR"
# 譟・謇セ郛灘ュ伜多荳ュ諠<EFBDAD><E8ABA0>
grep "Cache hit" logs/app.log | wc -l
grep "Cache miss" logs/app.log | wc -l
# 隶。邂怜多荳ュ邇?蜻ス荳ュ邇?= hits / (hits + misses) * 100%
```
### 9.2 霑千サエ蜻ス莉、
#### **蟶ク逕ィRedis CLI蜻ス莉、**
```bash
# 霑樊磁Redis
redis-cli -h r-xxx.redis.rds.aliyuncs.com -p 6379 -a your_password
# 譟・逵区園譛洩ey
KEYS *
# 譟・逵倶ササ蜉。逶ク蜈ウkey
KEYS task:*
# 譟・逵狗シ灘ュ倡嶌蜈ウkey
KEYS fulltext:*
# 譟・逵橘ey逧УTL
TTL task:abc123:progress
# 譟・逵橘ey逧<79>€?GET task:abc123:progress
# 譟・逵句<E980B5>蟄倅スソ逕ィ
INFO memory
# 譟・逵玖ソ樊磁謨?INFO clients
# 螳樊慮逶第而蜻ス莉、
MONITOR
# 貂<>ゥコ謨ー謐ョ蠎難シ亥些髯ゥ<E9ABAF><EFBDA9><EFBFBD>
FLUSHDB
```
#### **蜀<>ュ伜<EFBDAD><EFBFBD>**
```bash
# 譟・謇セ螟ァkey<65>?10KB<4B>?redis-cli -h xxx -a password --bigkeys
# 譟・逵橘ey逧<79><E980A7>蟄伜頃逕?MEMORY USAGE task:abc123:progress
```
### 9.3 謨<>囿謗呈衍豬∫ィ<E288AB>
```
髣ョ鬚假シ啌edis霑樊磁螟ア雍・
竊?1. 譽€譟・Redis螳樔セ狗憾諤<E686BE>シ磯仭驥御コ第而蛻カ蜿ー<E89CBF>? 竊?2. 譽€譟・逋ス蜷榊黒驟咲スョ<EFBDBD>域弍蜷ヲ蛹<EFBDA6>性SAE IP<49>? 竊?3. 譽€譟・蟇<EFBDA5><E89F87>∵弍蜷ヲ豁」遑? 竊?4. ping豬玖ッ慕ス醍サ懆ソ樣€€? 竊?5. 譟・逵句コ皮畑譌・蠢<EFBDA5>
竊?6. 螯よ裏豕募ソォ騾溯ァ」蜀?竊?髯咲コァ蛻ー蜀<EFBDB0>ュ倡シ灘ュ?```
### 9.4 螳壽悄扈エ謚、
| 扈エ蠎ヲ | 鬚醍紫 | 蜀<>ョケ |
|------|------|------|
| **譌・蟶ク逶第而** | 豈丞、ゥ | 譟・逵句<E980B5>蟄倅スソ逕ィ縲∬ソ樊磁謨ー縲<EFBDB0>漠隸ッ譌・蠢?|
| **諤ァ閭ス蛻<EFBDBD>梵** | 豈丞捉 | 蛻<>梵郛灘ュ伜多荳ュ邇<EFBDAD>€∝桃蠎疲慮髣?|
| **螳ケ驥剰ッ<E589B0>シー** | 豈乗怦 | 隸<>シー256MB譏ッ蜷ヲ螟溽畑<E6BABD>梧弍蜷ヲ髴€隕∝合驟?|
| **螳牙<E89EB3>€譟?* | 豈乗怦 | 譽€譟・逋ス蜷榊黒縲∝ッ<E2889D><EFBDAF>∝シコ蠎ヲ縲∬ョソ髣ョ譌・蠢?|
---
## 10. 謌仙粥譬<E7B2A5>
### 10.1 謚€譛ッ謖<EFBDAF><E8AC96><EFBFBD><EFBFBD>2.0譖エ譁ー<EFBFBD>?
| 謖<><E8AC96><EFBFBD> | 蠖灘燕蛟シ<E89B9F><EFBFBD>蟄假シ?| 逶ョ譬<EFBDAE>€<C280><EFBDBC>edis<69>?| 陦。驥乗婿豕<E5A9BF> |
|------|---------------|----------------|---------|
| **郛灘ュ俶戟荵<E6889F><E88DB5>?* | 笶?螳樔セ矩㍾蜷ッ荳「螟ア | 笨?謖∽ケ<E288BD>喧菫晏ュ?| 驥榊星蜷惹サ崎<EFBDBB>隸サ蜿?|
| **螟壼ョ樔セ句<EFBDBE>莠?* | 笶?蜷<>ョ樔セ狗峡遶?| 笨?蜈ィ螻€蜈ア莠ォ | 螳樔セ帰蜀呻シ悟ョ樔セ毅閭ス隸サ |
| **LLM API驥榊、崎ー<E5B48E>畑** | <20>閥 鬮?| <20>泙 菴?| 蜷御ク€PDF蜿ェ隹<EFBDAA><E99AB9>?谺?|
| **郛灘ュ伜多荳ュ邇?* | N/A | > 60% | 逶第而譌・蠢礼サ溯ョ。 |
| **莉サ蜉。謖∽ケ<E288BD><EFBDB9>?* | 笶?螳樔セ矩楳豈∽ク「螟?| 笨?莉サ蜉。扈ァ扈ュ | <20>閥 **譁ー蠅<EFBDB0>** |
| **髟ソ莉サ蜉。謌仙粥邇<E7B2A5>** | 10-30% | > 99% | <20>閥 **蜈ウ髞ョ謖<EFBDAE><E8AC96><EFBFBD>** |
| **邉サ扈溷庄逕ィ諤?* | 99% | 99.9% | 髯咲コァ遲也払菫晞囿 |
### 10.2 荳壼苅謖<E88B85><E8AC96><EFBFBD><EFBFBD>2.0譖エ譁ー<EFBFBD>?
| 謖<><E8AC96><EFBFBD> | 謾ケ騾<EFBDB9><EFBFBD> | 謾ケ騾<EFBDB9><EFBFBD> | 陦。驥乗婿豕<E5A9BF> |
|------|--------|--------|---------|
| **LLM API謌先悽** | ツ・X/譛?| 髯堺ス<E5A0BA>40-60% | 蟇ケ豈碑エヲ蜊<EFBDA6> |
| **莉サ蜉。荳「螟ア邇?* | <20>閥 70-95% | < 1% | <20>閥 **譛€驥崎ヲ<E5B48E>** |
| **莉サ蜉。螳梧<E89EB3>譌カ髣エ** | 荳咲。ョ螳?| 遞ウ螳<EFBDB3> | 逶第而譌・蠢<EFBDA5> |
| **逕ィ謌キ驥榊、肴署莠、谺。謨ー** | 蟷ウ蝮<EFBDB3>3谺?| 蜃<>荵惹ク? | 逕ィ謌キ陦御クコ蛻<EFBDBA>梵 |
| **逕ィ謌キ貊。諢丞コ?* | 蝓コ郤ソ | 譏セ闡玲署蜊<E7BDB2> | 髣ョ蜊キ隹<EFBDB7>衍 |
### 10.3 鬪梧噺譬<E599BA><EFBFBD><E38AA5>2.0譖エ譁ー<EFBFBD>?
#### **Redis郛灘ュ倬ェ梧噺**
```bash
笨?謇€譛臥シ灘ュ伜黒蜈<E9BB92>オ玖ッ暮€夊ソ<E5A48A>
笨?HealthCheckService郛灘ュ伜多荳ュ豬玖ッ暮€夊ソ<E5A48A>
笨?LLM12FieldsService郛灘ュ伜多荳ュ豬玖ッ暮€夊ソ<E5A48A>
笨?螳樔セ矩㍾蜷ッ蜷守シ灘ュ倅サ榊ュ伜惠
笨?郛灘ュ伜多荳ュ邇?> 60%
笨?LLM API隹<49>畑谺。謨ー荳矩剄 > 40%
```
#### **Redis髦溷<E9ABA6>鬪梧噺** <20>閥 **譁ー蠅槫<E8A085>髞ョ鬘?*
```bash
笨?Redis髦溷<E9ABA6>蜊募<E89C8A>豬玖ッ暮€夊ソ<E5A48A>
笨?髟ソ莉サ蜉。<E89C89><EFBDA1>2蟆乗慮<E4B997>画オ玖ッ暮€夊ソ<E5A48A>
笨?螳樔セ矩㍾蜷ッ蜷惹ササ蜉。閾ェ蜉ィ諱「螟?笨?莉サ蜉。螟ア雍・閾ェ蜉ィ驥崎ッ包シ?谺。<E8B0BA><EFBDA1>
笨?1000遽<30>枚迪ョ遲幃€<C280>蜉溽紫 > 99%
笨?譌<>逕ィ謌キ謚戊ッ我ササ蜉。荳「螟?笨?霑帛コヲ螳樊慮譖エ譁ー豁」蟶ク
```
#### **謨<>囿諱「螟肴オ玖ッ<E78E96>** <20>閥 **驥崎ヲ<E5B48E>**
```bash
笨?讓。諡溷ョ樔セ矩楳豈?竊?莉サ蜉。閾ェ蜉ィ諱「螟<EFBDA2>
笨?讓。諡欒edis螳墓惻 竊?邉サ扈滄剄郤ァ霑占。<E58DA0>
笨?讓。諡溽ス醍サ懷サカ霑<EFBDB6> 竊?莉サ蜉。豁」蟶ク螳梧<E89EB3>
笨?讓。諡溷ケカ蜿台ササ蜉。 竊?豁」遑ョ蛻<EFBDAE><E89BBB><EFBFBD>
```
#### **逕滉コァ邇ッ蠅<EFBDAF>ェ梧噺**
```bash
笨?逕滉コァ邇ッ蠅<EFBDAF>ソ占。<E58DA0>48蟆乗慮譌<E685AE>髞呵ッ?笨?2荳ェ螳梧紛逧<E7B49B>1000遽<30>枚迪ョ遲幃€我ササ蜉。謌仙<E8AC8C>?笨?逶第而謖<E8808C><E8AC96><EFBFBD>ュ」蟶ク<E89FB6><EFBFBD>蟄倥€∬ソ樊磁謨ー縲_PS<50>?笨?譌<>逕ィ謌キ謚戊ッ?```
---
## 11. FAQ
### Q1<51>壼ヲよ棡Redis蝨ィ蜊雁、懃ェ∫┯謖ゆコ<E38286>€惹ケ亥萱<E4BAA5><E890B1>
**A1**<2A>夂ウサ扈滉シ夊<EFBDBC>蜉ィ髯咲コァ蛻ー蜀<EFBDB0>ュ倡シ灘ュ假シ悟コ皮畑扈ァ扈ュ霑占。後€らャャ莠悟、ゥ霑千サエ譽€譟・蟷カ諱「螟抗edis縲?
### Q2<51>?56MB螟溽畑蜷暦シ滉サ€荵域慮蛟咎怙隕∝合驟搾シ<E690BE>
**A2**<2A>?- 蠖灘燕鬚<E78795>シー<EFBDBC>? 50% 菴ソ逕ィ邇?- 隗ヲ蜿大合驟堺ソ。蜿キ<E89CBF>? - 蜀<>ュ倅スソ逕ィ > 80%
- 鬚醍ケ∬ァヲ蜿銑RU鬩ア騾? - 逶第而蜻願ュヲ
- 蜊<><E89C8A>譁ケ蠑擾シ夐仭驥御コ第而蛻カ蜿ー荳€髞ョ蜊<EFBDAE>コァ<EFBDBA>梧裏髴€驥榊星
### Q3<51>啌edis莨壼スア蜩咲ウサ扈滓€ァ閭ス蜷暦シ<E69AA6>
**A3**<2A>?- 譛ャ蝨ー蜀<EFBDB0>ュ假シ? 0.1ms
- 譛ャ蝨ーRedis<69>嘸1ms
- 髦ソ驥御コ然edis<69>亥酔蝨ー蝓滂シ会シ嘸2-5ms
- 蠖ア蜩榊庄蠢ス逡・<E980A1>御ク疲怏謇ケ驥乗桃菴應シ伜喧
### Q4<51>壼ヲよ棡蜿醍鴫Redis荳埼€ょ粋<E38287><EFBFBD>蝗樣€€蛻ー蜀<EFBDB0>ュ倡シ灘ュ伜雛<E4BC9C>?**A4**<2A>壼庄莉・<E88E89>∽ソョ謾ケ `CACHE_TYPE=memory` 蜊ウ蜿ッ<E89CBF>御サ」遐∵髪謖∫Ο<CE9F>困縲?
### Q5<51>夐怙隕∝ュヲ荵<EFBDA6>Redis蜻ス莉、蜷暦シ<E69AA6>
**A5**<2A>?- 蠑€蜿托シ壻ク埼怙隕<E68099>シ御サ」遐∝キイ蟆∬」?- 霑千サエ<EFBDBB>壼サコ隶ョ蟄ヲ荵?荳ェ蝓コ遑€蜻ス莉、<E88E89><EFBDA4>ET/SET/KEYS/TTL/INFO<46>?
---
## 12. 逶ク蜈ウ譁<EFBDB3>。」
- [莠大次逕溷シ€蜿題ァ<E9A18C>激](../04-蠑€蜿題ァ<E9A18C><EFBDA7>?08-莠大次逕溷シ€蜿題ァ<E9A18C><EFBDA7>?md)
- [SAE驛ィ鄂イ螳悟<E89EB3><EFBFBD>漉](./02-SAE驛ィ鄂イ螳悟<E89EB3><EFBFBD>漉(莠ァ蜩∫サ冗炊迚?.md)
- [SAE邇ッ蠅<EFBDAF>序驥城<E9A9A5>鄂ョ謖<EFBDAE>漉](./03-SAE邇ッ蠅<EFBDAF>序驥城<E9A9A5>鄂ョ謖<EFBDAE>漉.md)
- [Redis螳俶婿譁<E5A9BF>。」](https://redis.io/docs/)
- [ioredis譁<73>。」](https://github.com/redis/ioredis)
- [髦ソ驥御コ然edis譁<73>。」](https://help.aliyun.com/product/26340.html)
---
## 13. 髯<><EFBFBD>
### 髯<>ス柊<EFBDBD>壼ョ梧紛逧<E7B49B>.env驟咲スョ讓。譚ソ
```env
# ==================== 謨ー謐ョ蠎?====================
DATABASE_URL=postgresql://postgres:password@localhost:5432/ai_clinical_research
# ==================== Redis驟咲スョ ====================
CACHE_TYPE=redis
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0
# 謌紋スソ逕ィ霑樊磁蟄礼ャヲ荳イ
# REDIS_URL=redis://localhost:6379
# ==================== 髦溷<E9ABA6>驟咲スョ ====================
QUEUE_TYPE=memory # 隨ャ荳€髦カ谿オ逕ィmemory<72>檎ャャ莠碁亳谿オ謾ケ荳コredis
# ==================== JWT ====================
JWT_SECRET=your-secret-key-change-in-production
JWT_EXPIRES_IN=7d
# ==================== LLM API ====================
DEEPSEEK_API_KEY=sk-xxxxxxxxxxxxxx
DASHSCOPE_API_KEY=sk-xxxxxxxxxxxxxx
CLOSEAI_API_KEY=sk-xxxxxxxxxxxxxx
CLOSEAI_OPENAI_BASE_URL=https://api.openai-proxy.org/v1
CLOSEAI_CLAUDE_BASE_URL=https://api.openai-proxy.org/anthropic
# ==================== Dify ====================
DIFY_API_URL=http://localhost/v1
DIFY_API_KEY=dataset-xxxxxxxxxxxxxx
# ==================== Server ====================
PORT=3001
NODE_ENV=development
# ==================== 蟄伜お驟咲スョ ====================
STORAGE_TYPE=local
LOCAL_STORAGE_DIR=uploads
LOCAL_STORAGE_URL=http://localhost:3001/uploads
# ==================== CORS驟咲スョ ====================
CORS_ORIGIN=http://localhost:5173
# ==================== 譌・蠢鈴<E8A0A2>鄂ョ ====================
LOG_LEVEL=debug
```
### 髯<>ス稗<EFBDBD>啌edis蜀<73>ュ倩ョ。邂怜<E98282>?
```
蜊穂クェLLM扈捺棡郛灘ュ假シ嘸50KB
蜊穂クェ蛛・蠎キ譽€譟・郛灘ュ假シ嘸5KB
<EFBFBD>シー螳ケ驥擾シ?- 1000荳ェLLM扈捺棡 = 50MB
- 1000荳ェ蛛・蠎キ譽€譟?= 5MB
- 邉サ扈溷シ€髞€ = 20MB
-----------------------------
諤サ隶。 = 75MB / 256MB = 29% 菴ソ逕ィ邇?```
### 髯<>ス匹<EFBDBD>壽腐髫懈シ皮サ<E79AAE><EFBDBB>譛?
```bash
#!/bin/bash
# 譁<>サカ<EFBDBB>喘ackend/scripts/disaster-recovery-drill.sh
echo "<22>圷 Redis謨<73>囿貍皮サ<E79AAE>€蟋?.."
# 1. 蛛懈ュ「Redis
echo "1. 蛛懈ュ「Redis..."
docker stop ai-clinical-redis
sleep 2
# 2. 豬玖ッ募コ皮畑譏ッ蜷ヲ豁」蟶ク
echo "2. 豬玖ッ募コ皮畑蛛・蠎キ譽€譟?.."
response=$(curl -s http://localhost:3001/api/health)
echo "蜩榊コ<E6A68A>: $response"
if [[ $response == *"memory"* ]]; then
echo "笨?髯咲コァ謌仙粥<E4BB99>御スソ逕ィ蜀<EFBDA8>ュ倡シ灘ュ?
else
echo "笶?髯咲コァ螟ア雍・"
exit 1
fi
# 3. 諱「螟抗edis
echo "3. 諱「螟抗edis..."
docker start ai-clinical-redis
sleep 5
# 4. 豬玖ッ紐edis諱「螟<EFBDA2>
echo "4. 豬玖ッ紐edis諱「螟<EFBDA2>..."
response=$(curl -s http://localhost:3001/api/health)
echo "蜩榊コ<E6A68A>: $response"
if [[ $response == *"redis"* ]]; then
echo "笨?Redis諱「螟肴<E89E9F><EFBFBD>"
else
echo "笞<><E7AC9E><EFBFBD> Redis譛ェ諱「螟搾シ御サ堺スソ逕ィ蜀<EFBDA8>ュ倡シ灘ュ?
fi
echo "<22>脂 謨<>囿貍皮サ<E79AAE>ョ梧<EFBDAE><E6A2A7>?
```
---
**譁<>。」扈エ謚、閠<EFBDA4><EFBFBD>** 謚€譛ッ蝗「髦?
**譛€蜷取峩譁ー<E8AD81><EFBDB0>** 2025-12-12
**譁<>。」迥カ諤<EFBDB6><EFBFBD>** 笨?蠕<>ョ。譬?
**荳区ャ。譖エ譁ー<E8AD81>?* 謾ケ騾<EFBDB9>螳梧<E89EB3>蜷取€サ扈鍋サ城ェ梧蕗隶ュ
---
## 笨?謾ケ騾<EFBDB9>螳梧<E89EB3>€譟・貂<EFBDA5><E8B282>?
蝨ィ螳梧<EFBFBD>Redis謾ケ騾<EFBFBD>蜷趣シ瑚ッキ騾宣。ケ譽€譟・<EFBFBD><EFBFBD>
### 莉」遐∝アる擇
- [ ] `ioredis` 蟾イ螳芽」?- [ ] `RedisCacheAdapter` 蟾イ螳樒<E89EB3>?- [ ] `CacheFactory` 蟾イ豺サ蜉<EFBDBB>髯咲コァ騾サ霎<EFBDBB>
- [ ] `.env` 驟咲スョ蟾イ譖エ譁?- [ ] 謇€譛我スソ逕?`cache.set()` 逧<>慍譁ケ驛ス隶セ鄂ョ莠<EFBDAE>TL
### 豬玖ッ募アる擇
- [ ] 蜊募<E89C8A>豬玖ッ募<EFBDAF>驛ィ騾夊ソ<E5A48A>
- [ ]<><E9ABAE>豬玖ッ募<EFBDAF>驛ィ騾夊ソ<E5A48A>
- [ ] 蜴句鴨豬玖ッ戊セセ譬<EFBDBE>
- [ ]<>囿讓。諡滓オ玖ッ暮€夊ソ<E5A48A>
### 驛ィ鄂イ螻る擇
- [ ] 髦ソ驥御コ然edis蟾イ雍ュ荵?- [ ] 逋ス蜷榊黒蟾イ驟咲スョ
- [ ] SAE邇ッ蠅<EFBDAF>序驥丞キイ驟咲ス?- [ ] 逕滉コァ邇ッ蠅<EFBDAF>キイ鬪瑚ッ?
### 譁<>。」螻る擇
- [ ] 謾ケ騾<EFBDB9><EFBFBD>。」蟾イ譖エ譁ー
- [ ] 霑千サエ譁<EFBDB4>。」蟾イ陦・蜈?- [ ] 逶第而謖<E8808C><E8AC96><EFBFBD>キイ隶ー蠖?- [ ] 扈城ェ梧蕗隶ュ蟾イ諤サ扈<EFBDBB>
---
**逾晄隼騾<E99ABC>鬘コ蛻ゥ<E89BBB>∝ヲよ怏髣ョ鬚假シ瑚ッキ蜿頑慮豐滄€€?* <20>