缓存是提高应用性能的重要手段,TypeORM 提供了多种缓存策略和机制,让开发者能够灵活地管理数据缓存。
缓存类型
1. 查询缓存
TypeORM 提供了内置的查询缓存功能,可以缓存查询结果。
typescriptimport { DataSource } from 'typeorm'; const dataSource = new DataSource({ type: 'mysql', host: 'localhost', port: 3306, username: 'root', password: 'password', database: 'myapp', entities: [User, Post], synchronize: false, logging: true, // 启用查询缓存 cache: { type: 'database', // 或 'redis', 'ioredis' duration: 30000, // 缓存持续时间(毫秒) options: { // 数据库缓存选项 tableName: 'query_cache', }, }, });
2. Redis 缓存
使用 Redis 作为缓存存储:
typescriptimport { DataSource } from 'typeorm'; const dataSource = new DataSource({ type: 'mysql', host: 'localhost', port: 3306, username: 'root', password: 'password', database: 'myapp', entities: [User, Post], synchronize: false, logging: true, cache: { type: 'redis', // 或 'ioredis' duration: 30000, options: { host: 'localhost', port: 6379, password: 'password', db: 0, }, }, });
3. 数据库缓存
使用数据库表作为缓存存储:
typescriptconst dataSource = new DataSource({ type: 'mysql', host: 'localhost', port: 3306, username: 'root', password: 'password', database: 'myapp', entities: [User, Post], synchronize: false, logging: true, cache: { type: 'database', duration: 30000, options: { tableName: 'query_cache', }, }, });
查询缓存使用
基本查询缓存
typescriptimport { DataSource } from 'typeorm'; // 启用缓存的查询 const users = await dataSource .getRepository(User) .find({ cache: true, // 启用缓存 }); // 自定义缓存时间 const users = await dataSource .getRepository(User) .find({ cache: { id: 'users_list', // 缓存 ID milliseconds: 60000, // 缓存 60 秒 }, });
QueryBuilder 缓存
typescript// 使用 QueryBuilder 启用缓存 const users = await dataSource .getRepository(User) .createQueryBuilder('user') .cache(true) // 启用缓存 .getMany(); // 自定义缓存配置 const users = await dataSource .getRepository(User) .createQueryBuilder('user') .cache('user_list', 60000) // 缓存 ID 和时间 .where('user.age > :age', { age: 18 }) .getMany();
关联查询缓存
typescript// 缓存关联查询 const usersWithPosts = await dataSource .getRepository(User) .find({ relations: ['posts'], cache: true, }); // 使用 QueryBuilder 缓存关联查询 const usersWithPosts = await dataSource .getRepository(User) .createQueryBuilder('user') .leftJoinAndSelect('user.posts', 'post') .cache('users_with_posts', 30000) .getMany();
缓存管理
清除缓存
typescript// 清除特定查询缓存 await dataSource .getRepository(User) .query('CLEAR CACHE'); // 清除所有缓存 await dataSource.queryResultCache?.clear(); // 清除特定缓存 ID await dataSource.queryResultCache?.remove(['users_list']);
缓存失效策略
typescript// 在更新数据后清除相关缓存 async function updateUser(userId: number, userData: Partial<User>) { const userRepository = dataSource.getRepository(User); // 更新用户 await userRepository.update(userId, userData); // 清除相关缓存 await dataSource.queryResultCache?.remove([ 'users_list', 'user_details', ]); } // 使用事务和缓存清除 async function updateUserWithCache(userId: number, userData: Partial<User>) { await dataSource.transaction(async (manager) => { const userRepository = manager.getRepository(User); // 更新用户 await userRepository.update(userId, userData); // 清除缓存 await manager.queryResultCache?.remove([ 'users_list', ]); }); }
实体缓存
实体级缓存
typescriptimport { Entity, PrimaryGeneratedColumn, Column } from 'typeorm'; @Entity() export class User { @PrimaryGeneratedColumn() id: number; @Column() name: string; @Column() email: string; // 实体缓存配置 @Column({ type: 'timestamp' }) cachedAt: Date; } // 使用实体缓存 async function getUserWithCache(userId: number) { const cacheKey = `user:${userId}`; // 尝试从缓存获取 const cached = await dataSource.queryResultCache?.get(cacheKey); if (cached) { return JSON.parse(cached); } // 从数据库获取 const user = await dataSource.getRepository(User).findOne({ where: { id: userId }, }); // 存入缓存 await dataSource.queryResultCache?.store( cacheKey, JSON.stringify(user), 60000 ); return user; }
自定义缓存装饰器
typescriptimport { Cacheable } from './decorators/Cacheable'; @Entity() export class Product { @PrimaryGeneratedColumn() id: number; @Column() name: string; @Column({ type: 'decimal', precision: 10, scale: 2 }) price: number; @Cacheable({ ttl: 30000 }) // 缓存 30 秒 async getDetails(): Promise<Product> { return this; } } // 使用自定义缓存装饰器 const product = await productRepository.findOne({ where: { id: 1 } }); const details = await product.getDetails();
缓存策略
读写分离缓存
typescript// 读操作使用缓存 async function getUser(userId: number) { const cacheKey = `user:${userId}`; // 尝试从缓存读取 const cached = await dataSource.queryResultCache?.get(cacheKey); if (cached) { return JSON.parse(cached); } // 从数据库读取 const user = await dataSource.getRepository(User).findOne({ where: { id: userId }, }); // 存入缓存 await dataSource.queryResultCache?.store( cacheKey, JSON.stringify(user), 60000 ); return user; } // 写操作清除缓存 async function updateUser(userId: number, userData: Partial<User>) { const userRepository = dataSource.getRepository(User); // 更新数据库 await userRepository.update(userId, userData); // 清除缓存 const cacheKey = `user:${userId}`; await dataSource.queryResultCache?.remove([cacheKey]); }
分层缓存
typescript// 一级缓存:内存缓存 const memoryCache = new Map<string, any>(); // 二级缓存:Redis 缓存 const redisCache = dataSource.queryResultCache; async function getProduct(productId: number) { const cacheKey = `product:${productId}`; // 尝试一级缓存 if (memoryCache.has(cacheKey)) { return memoryCache.get(cacheKey); } // 尝试二级缓存 const cached = await redisCache?.get(cacheKey); if (cached) { const product = JSON.parse(cached); memoryCache.set(cacheKey, product); return product; } // 从数据库获取 const product = await dataSource.getRepository(Product).findOne({ where: { id: productId }, }); // 存入二级缓存 await redisCache?.store(cacheKey, JSON.stringify(product), 60000); // 存入一级缓存 memoryCache.set(cacheKey, product); return product; }
缓存预热
typescript// 应用启动时预热缓存 async function warmupCache() { // 预热热门用户 const popularUsers = await dataSource .getRepository(User) .createQueryBuilder('user') .where('user.followersCount > :count', { count: 1000 }) .limit(100) .getMany(); for (const user of popularUsers) { const cacheKey = `user:${user.id}`; await dataSource.queryResultCache?.store( cacheKey, JSON.stringify(user), 300000 // 5 分钟 ); } // 预热热门文章 const popularPosts = await dataSource .getRepository(Post) .createQueryBuilder('post') .where('post.views > :views', { views: 1000 }) .limit(100) .getMany(); for (const post of popularPosts) { const cacheKey = `post:${post.id}`; await dataSource.queryResultCache?.store( cacheKey, JSON.stringify(post), 300000 ); } } // 在应用启动时调用 warmupCache();
缓存最佳实践
1. 选择合适的缓存策略
typescript// ✅ 好的做法:为不同类型的数据使用不同的缓存策略 // 静态数据:长时间缓存 const staticData = await dataSource .getRepository(Category) .find({ cache: { milliseconds: 3600000, // 1 小时 }, }); // 动态数据:短时间缓存 const dynamicData = await dataSource .getRepository(Post) .find({ cache: { milliseconds: 30000, // 30 秒 }, }); // 实时数据:不缓存 const realTimeData = await dataSource .getRepository(OnlineUser) .find({ cache: false, });
2. 缓存键命名规范
typescript// ✅ 好的做法:使用清晰的缓存键命名 const cacheKeys = { user: (id: number) => `user:${id}`, userList: (page: number) => `user_list:page:${page}`, userPosts: (userId: number) => `user:${userId}:posts`, popularPosts: () => `posts:popular`, }; // 使用示例 await dataSource.queryResultCache?.store( cacheKeys.user(1), JSON.stringify(user), 60000 );
3. 缓存失效时机
typescript// ✅ 好的做法:在数据变更时及时清除缓存 class UserService { async updateUser(userId: number, userData: Partial<User>) { const userRepository = dataSource.getRepository(User); // 更新数据 await userRepository.update(userId, userData); // 清除相关缓存 await this.clearUserCache(userId); } async clearUserCache(userId: number) { const cacheKeys = [ `user:${userId}`, `user:${userId}:posts`, `user:${userId}:comments`, ]; await dataSource.queryResultCache?.remove(cacheKeys); } }
4. 监控缓存性能
typescript// 缓存性能监控 class CacheMonitor { private hits = 0; private misses = 0; async get<T>(key: string): Promise<T | null> { const cached = await dataSource.queryResultCache?.get(key); if (cached) { this.hits++; return JSON.parse(cached); } this.misses++; return null; } getHitRate(): number { const total = this.hits + this.misses; return total > 0 ? this.hits / total : 0; } getStats() { return { hits: this.hits, misses: this.misses, hitRate: this.getHitRate(), }; } } // 使用监控 const monitor = new CacheMonitor(); async function getProduct(productId: number) { const cacheKey = `product:${productId}`; // 尝试从缓存获取 const cached = await monitor.get<Product>(cacheKey); if (cached) { return cached; } // 从数据库获取 const product = await dataSource.getRepository(Product).findOne({ where: { id: productId }, }); // 存入缓存 await dataSource.queryResultCache?.store( cacheKey, JSON.stringify(product), 60000 ); return product; } // 定期输出缓存统计 setInterval(() => { console.log('Cache Stats:', monitor.getStats()); }, 60000);
高级缓存技巧
1. 缓存穿透防护
typescript// 使用空值缓存防止缓存穿透 async function getUser(userId: number) { const cacheKey = `user:${userId}`; // 尝试从缓存获取 const cached = await dataSource.queryResultCache?.get(cacheKey); if (cached) { const user = JSON.parse(cached); // 检查是否为空值标记 if (user === null) { return null; } return user; } // 从数据库获取 const user = await dataSource.getRepository(User).findOne({ where: { id: userId }, }); // 存入缓存(包括空值) await dataSource.queryResultCache?.store( cacheKey, JSON.stringify(user), 60000 ); return user; }
2. 缓存雪崩防护
typescript// 使用随机缓存时间防止缓存雪崩 async function getProduct(productId: number) { const cacheKey = `product:${productId}`; // 尝试从缓存获取 const cached = await dataSource.queryResultCache?.get(cacheKey); if (cached) { return JSON.parse(cached); } // 从数据库获取 const product = await dataSource.getRepository(Product).findOne({ where: { id: productId }, }); // 使用随机缓存时间(30-60 秒) const cacheTime = 30000 + Math.random() * 30000; // 存入缓存 await dataSource.queryResultCache?.store( cacheKey, JSON.stringify(product), cacheTime ); return product; }
3. 缓存击穿防护
typescript// 使用互斥锁防止缓存击穿 const locks = new Map<string, Promise<any>>(); async function getHotProduct(productId: number) { const cacheKey = `product:${productId}`; // 尝试从缓存获取 const cached = await dataSource.queryResultCache?.get(cacheKey); if (cached) { return JSON.parse(cached); } // 检查是否有锁 if (locks.has(cacheKey)) { return await locks.get(cacheKey); } // 创建锁 const promise = (async () => { try { // 从数据库获取 const product = await dataSource.getRepository(Product).findOne({ where: { id: productId }, }); // 存入缓存 await dataSource.queryResultCache?.store( cacheKey, JSON.stringify(product), 60000 ); return product; } finally { // 释放锁 locks.delete(cacheKey); } })(); locks.set(cacheKey, promise); return await promise; }
TypeORM 的缓存机制提供了强大的性能优化能力,合理使用缓存可以显著提高应用性能。