依赖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)
欢迎转载:转载时请注明本文出处及文章链接