面试题答案
一键面试设计思路
- 缓存策略:
- 缓存时间:可以为每个缓存项设置一个过期时间(TTL,Time To Live)。当缓存项被访问时,检查其是否过期,若过期则从缓存中移除。
- 缓存更新策略:常见策略有写后失效(Write - Through)、写前失效(Write - Behind Caching)和读写失效(Read - Write - Through)。写后失效即在数据更新后使相关缓存失效;写前失效是在更新数据前先失效缓存;读写失效则是读写操作都检查并更新缓存。在 RESTful API 客户端场景下,写后失效较为常用,因为简单直接,能保证数据一致性。
- 使用泛型: 通过泛型可以使缓存机制适用于不同类型的 API 响应数据。定义缓存类或函数时,使用泛型参数表示缓存数据的类型,这样无论 API 返回的数据是简单对象、数组还是其他复杂类型,缓存机制都能适配。
- 高并发场景下缓存一致性:
- 使用互斥锁(如 JavaScript 中的
async/await
结合Promise
实现)来防止多个并发请求同时更新或读取缓存。在读取缓存前,先获取锁,操作完成后释放锁。 - 采用分布式缓存(如 Redis),利用其原子操作特性,如
SETNX
(Set if Not eXists)来保证在分布式环境下缓存的一致性。
- 使用互斥锁(如 JavaScript 中的
核心 TypeScript 代码实现
class Cache<T> {
private cache: Map<string, { value: T; expiration: number }> = new Map();
private mutex: boolean = false;
constructor(private ttl: number) {}
async get(key: string): Promise<T | null> {
while (this.mutex) {
await new Promise(resolve => setTimeout(resolve, 100));
}
this.mutex = true;
try {
const cached = this.cache.get(key);
if (cached && cached.expiration > Date.now()) {
return cached.value;
}
return null;
} finally {
this.mutex = false;
}
}
set(key: string, value: T) {
const expiration = Date.now() + this.ttl;
this.cache.set(key, { value, expiration });
}
invalidate(key: string) {
this.cache.delete(key);
}
}
// 使用示例
async function fetchData<T>(cache: Cache<T>, key: string, apiCall: () => Promise<T>): Promise<T> {
let data = await cache.get(key);
if (!data) {
data = await apiCall();
cache.set(key, data);
}
return data;
}
在上述代码中:
Cache
类封装了缓存逻辑,使用Map
来存储缓存数据,每个缓存项包含数据值和过期时间。get
方法使用互斥锁来处理高并发读取,确保同一时间只有一个请求能读取缓存。set
方法设置缓存项,并根据 TTL 设置过期时间。invalidate
方法用于使指定缓存项失效。fetchData
函数展示了如何结合缓存机制进行 API 数据获取,先尝试从缓存读取,若缓存不存在则调用 API 获取数据并更新缓存。