依赖npm包 https://www.npmjs.com/package/redis  看下官网的基础例子:

Basic Example

import { createClient } from "redis";

const client = await createClient()
  .on("error", (err) => console.log("Redis Client Error", err))
  .connect();

await client.set("key", "value");
const value = await client.get("key");
client.destroy();

如果redis设置了用户密码,连接时需要修改:

createClient({
  url: 'redis://user:secret@localhost:6379/0'
});

为了方便存储JS的数组和对象,需要将值换为JSON字符串,同时进行一些封装:

const { createClient } = require('redis');

// 创建全局的 Redis 客户端实例
let client;

/**
 * 初始化 Redis 客户端
 */
const redisClient = async () => {
  if (client) return; // 如果客户端已经初始化,则不再重复初始化

  client = await createClient()
    .on('error', err => console.log('Redis 连接失败', err))
    .connect();
};

/**
 * 存入数组或对象,并可选地设置过期时间
 * @param key 键名
 * @param value 要存储的值
 * @param ttl 可选,以秒为单位的过期时间,默认不设置
 */
const setKey = async (key, value, ttl = null) => {
  if (!client) await redisClient(); // 确保客户端已初始化
  value = JSON.stringify(value); // 将对象转换为JSON字符串
  await client.set(key, value);

  // 如果提供了ttl,则设置过期时间
  if (ttl !== null) {
    await client.expire(key, ttl);
  }
};

/**
 * 读取数组或对象
 * @param key 键名
 * @returns {Promise<any>} 解析后的JSON对象或数组
 */
const getKey = async (key) => {
  if (!client) await redisClient(); // 确保客户端已初始化
  const value = await client.get(key); // 将获取到的JSON字符串转换回对象
  return value ? JSON.parse(value) : null; // 如果value为空,返回null而不是抛出错误
};

/**
 * 清除缓存数据
 * @param key
 * @returns {Promise<void>}
 */
const delKey = async (key) => {
  if (!client) await redisClient(); // 确保客户端已初始化
  await client.del(key);
};

module.exports = { redisClient, setKey, getKey, delKey };

通过以上封装,我们使用起来就非常方便:

// 缓存的 key,定义为:setting
const cacheKey = 'setting';

// 读取缓存中的数据
let setting = await getKey(cacheKey);

// 如果缓存中没有数据,则从数据库中读取数据
if (!setting) {
  setting = await Setting.findOne();
  if (!setting) {
    throw new NotFound('未找到系统设置,请联系管理员。')
  }

  // 并将数据写入缓存
  await setKey(cacheKey, setting);
}

success(res, '查询系统信息成功。', { setting });

在使用的时候我们还需要考虑在数据修改时,及时的去删除缓存:

....数据被修改时
// 删除缓存
await delKey('setting');

封装好后,使用缓存时还需要一些策略:

1.对列表类接口数据做增删改时,需要删除缓存。

2.对于更新较为频繁,但实时性要求不高的接口,可以设置一个缓存有效期,一段时间后更新缓存;

await setKey('index', data, 10);

3.分页接口的缓存,需要根据当前是第几页,每页显示几条来进行缓存,可以定义一个带有当前页码和每页条数的key,作为缓存的键。

// 定义带有「当前页码」和「每页条数」的 cacheKey 作为缓存的键
const cacheKey = `articles:${currentPage}:${pageSize}`;   //注意使用中间冒号的连接格式,符合官方约定的格式
let data = await getKey(cacheKey);
if (data) {
  return success(res, '查询文章列表成功。', data);
}

当分页的数据新增、修改、删除文章后,要清空所有的文章列表相关的所有分页缓存。这里就比较难办了,Redis并没有提供哪个方法,能直接将所有以articles开头的数据全部删掉。正确的做法是,先用articles:*,这里用了一个*号作为通配符,查询到所有以articles开头的key。然后再以查到的所有key,删掉对应的数据。新增一个方法,用来查询带通配符的key,再通过查出的key来删除缓存。

/**
 * 获取匹配模式的所有键名
 * @param pattern
 * @returns {Promise<*>}
 */
const getKeysByPattern = async (pattern) => {
  if (!client) await redisClient();
  return await client.keys(pattern);
}
/**
 * 清除缓存
 * @returns {Promise<void>}
 */
async function clearCache() {
  // 清除所有文章列表缓存
  const keys = await getKeysByPattern('articles:*');
  if (keys.length !== 0) {
    await delKey(keys);
  }
}

4.带id的缓存,要点是将id设置到key里。清理的时候,也是通过带id的key来清除。

const { id } = req.params;
let article = await getKey(`article:${id}`);
if (!article) {
  article = await Article.findByPk(id);
  if (!article) {
    throw new NotFound(`ID: ${id}的文章未找到。`)
  }
  await setKey(`article:${id}`, article)
}
/**
 * 清除缓存
 * @param id
 * @returns {Promise<void>}
 */
async function clearCache(id = null) {
  // 清除所有文章列表缓存
  let keys = await getKeysByPattern('articles:*');
  if (keys.length !== 0) {
    await delKey(keys);
  }

  // 如果传递了id,则通过id清除文章详情缓存
  if (id) {
    // 如果是数组,则遍历
    const keys = Array.isArray(id) ? id.map(item => `article:${item}`) : `article:${id}`;
    await delKey(keys);
  }
}

5.当一个接口关联的数据较为分散,可以针对每个查询,进行分散缓存

let course = await getKey(`course:${id}`);
let category = await getKey(`category:${course.categoryId}`);
let user = await getKey(`user:${course.userId}`);

6.清理所有缓存

/**
 * 清空所有缓存数据
 * @returns {Promise<void>}
 */
const flushAll = async () => {
  if (!client) await redisClient();
  await client.flushAll();
}


点赞 (3)

欢迎转载:转载时请注明本文出处及文章链接