node 使用 Redis 缓存

缓存是什么?

高并发下,一个项目最先出问题的,并不是程序本身,而是数据库最先承受不住。

在数据库上我们可以做很多优化,例如优化 SQL 语句,优化索引,如果数据量大了,还可以分库、分表等等。

一个项目中,大多数使用场景都是查询操作,而不是其他新增、修改和删除。那我们思考一下,能不能在第一次查询后,将数据存储在某个地方。下次再查询的时候,就直接读取之前保存的。如果数据发生改变了,就保存的数据删掉,重新保存最新的。这样不就可以大幅度的减少数据库的查询吗?

缓存就是这个原理。

Redis 又是什么?

Redis是一个开源的、基于内存的数据存储系统,可以用作数据库、缓存和消息中间件。Redis的数据都存储在内存中,这样它的读写速度就非常快。而且还会将内存中的数据写入本地硬盘,可以在服务器重启后,自动将数据恢复到内存中。

学习Redis是大家成为高手的必经之路。它的功能非常强大,在我们这节课中,只会用到Redis的缓存能力。

如何安装 Redis 的服务端与客户端?

1、安装 Redis 服务端 Windows Docker Desktop安装及使用 Docker 运行 MySQL

运行Redis,最推荐的方式依然是使用Docker。打开项目根目录的docker-compose.yml文件,添加Redis的相关配置。大家注意下格式,缩进非常重要,一定是和之前的MySQL配置对齐的

js 复制代码
services:
  mysql:
    # ....
  redis:
    image: redis:7.4
    ports:
      - "6379:6379"
    volumes:
      - ./data/redis:/data

完成后,命令行,再次运行

js 复制代码
docker-compose up -d

这样Docker,就会自动下载好Redis,并在6379端口启动它。

2、安装 Redis 客户端

服务端启动好了,还需要用客户端进行管理。所谓客户端,就是像 Navicat、Sequel Ace 一样用来管理数据库的工具。这里推荐大家使用的客户端,叫做Redis Insight,它是 Redis 官方发布的管理工具。它是完全免费的,非常好用。
Redis Insight官网


双击一下,就可以连进去了。

目前里面还没有存任何东西进去,所以都是空白的。好了,先丢一边,一会儿我们再来继续用它。

如何在 Node 项目中,使用 Redis 缓存数据?

Redis已经安装,并启动好了以后,现在需要在Node项目中来操作它。

1、安装 node-redis

项目根目录

js 复制代码
npm i redis

2、Redis 配置文件

接着看官方文档

  • 这里告诉我们了,使用前,要先用connect方法连接上去。
  • set来写入数据。
  • get来读取数据。
  • key,就是给缓存数据,取了个名字。无论写入,还是读取,都是用这个名字,就能找到它。
  • 不用了以后,可以用disconnect方法来断开连接。
    新建utils/redis.js,内容我已经写好了
js 复制代码
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);
};

/**
 * 获取匹配模式的所有键名
 * @param pattern
 * @returns {Promise<*>}
 */
const getKeysByPattern = async (pattern) => {
  if (!client) await redisClient();
  return await client.keys(pattern);
}

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

module.exports = {redisClient, setKey, getKey, delKey, getKeysByPattern, flushAll};
  • 先定义了一个client变量,里面会创建全局的Redis客户端实例。
  • 第一个方法,是用来连接到了Redis的。这里有一个判断,如果已经存在了,就直接返回,这样可以避免重复的创建 Redis 实例。
  • 接着我们自定义了写入数据的方法,需要注意的是,在存储之前,先要用JSON.stringify()方法将 JS 对象或数组转换成JSON 字符串
  • 下面还有过期时间的设置,不传就是永久的。如果传了,以秒为单位,如果时间超出后,Redis会自动清理掉存储的数据。
  • 读取方法里,使用JSON.parse()方法将JSON 字符串解析回原来的数据结构。
  • 最下面是删除缓存的方法,调用del方法,即可删掉缓存数据。

这样做好封装好后,用起来就非常简单了,而且就算是数组或对象,也可以顺利的存取了。

PS: 如果您的 Redis 服务端,运行在其他服务器上或者你在宝塔中不想使用docker,可以调整连接格式为:

js 复制代码
client = await createClient({
  url: 'redis://<服务器IP>:<端口号>' ,
  // password:'',  有无密码
})

3、通过utils/redis.js使用

js 复制代码
// 引入
const { setKey, getKey } = require('../utils/redis');
// 单条数据存储 key:键名  data:数据
setKey(key, data);
// 带有效期存储 key:键名  data:数据 ttl:时间s为单位
setKey( key, data, ttl);
// 带分页的缓存 articles:默认为路由名 后用:链接 当前页和当页数量 (预定俗称以:为连接)
const cacheKey = `articles:${currentPage}:${pageSize}`;
setKey(cacheKey, data);
// 带id 的缓存  articles:默认为路由名 后用:链接 当前id (预定俗称以:为连接)
setKey(`article:${id}`, data)

// 单条数据删除 key:键名
await delKey(key);

// 复杂缓存删除
/**
 * 清除缓存
 * @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);
  }
}
相关推荐
小陈工3 小时前
Python Web开发入门(十七):Vue.js与Python后端集成——让前后端真正“握手言和“
开发语言·前端·javascript·数据库·vue.js·人工智能·python
科技小花7 小时前
数据治理平台架构演进观察:AI原生设计如何重构企业数据管理范式
数据库·重构·架构·数据治理·ai-native·ai原生
一江寒逸7 小时前
零基础从入门到精通MySQL(中篇):进阶篇——吃透多表查询、事务核心与高级特性,搞定复杂业务SQL
数据库·sql·mysql
D4c-lovetrain7 小时前
linux个人心得22 (mysql)
数据库·mysql
阿里小阿希8 小时前
CentOS7 PostgreSQL 9.2 升级到 15 完整教程
数据库·postgresql
荒川之神8 小时前
Oracle 数据仓库雪花模型设计(完整实战方案)
数据库·数据仓库·oracle
做个文艺程序员8 小时前
MySQL安全加固十大硬核操作
数据库·mysql·安全
不吃香菜学java8 小时前
Redis简单应用
数据库·spring boot·tomcat·maven
一个天蝎座 白勺 程序猿8 小时前
Apache IoTDB(15):IoTDB查询写回(INTO子句)深度解析——从语法到实战的ETL全链路指南
数据库·apache·etl·iotdb
不知名的老吴9 小时前
Redis的延迟瓶颈:TCP栈开销无法避免
数据库·redis·缓存