万字长文:从缓存文章聊聊Redis OM是怎么个事儿 · 上

这个起因是因为在作者群里聊作弊判重的事情,小编打算用Redisearch来实现一下结果没成功。但是在整理资料的过程中意外发现这个项目,感觉还挺有意思的。发出来,和大家探讨一下~

写在前面

Redis OM 是什么

Redis OM(Object Mapping)是一个Redis官方库@reids-om-node,旨在简化与 Redis 数据库交互的过程。它提供了一个面向对象的接口,使开发者能够以更高层次的抽象来操作 Redis 数据。Redis OM 可以自动处理对象与 Redis 数据结构之间的映射和序列化/反序列化,使得代码更简洁、更易读、更易维护。

主要特性

  1. 对象映射: Redis OM 允许你定义数据模型(实体),并将这些模型映射到 Redis 数据结构中。这样,你可以以面向对象的方式来操作 Redis 数据,而不需要直接处理 Redis 命令。

  2. 自动序列化和反序列化: Redis OM 自动处理对象与 Redis 数据结构之间的转换。你不需要手动将对象转换为字符串或其他 Redis 数据类型,这减少了重复代码和潜在的错误。

  3. 高级查询和过滤: Redis OM 提供了更高级的查询和过滤功能,使得你可以更方便地进行复杂的数据查询,而不需要手动编写复杂的 Redis 命令。

  4. 事务和批处理: Redis OM 提供了简化的事务管理和批处理功能,使得你可以更方便地进行原子操作和批量操作。

  5. 模型定义: 你可以通过定义清晰的数据模型来管理 Redis 数据。这使得代码更加结构化和可读,特别是在处理复杂数据结构时更加明显。

Redis OM 提供了一种更高效、更结构化的方式来管理和操作 Redis 数据。它通过对象映射、自动序列化/反序列化、高级查询和事务管理等特性,显著提高了开发效率和代码可维护性。对于需要频繁与 Redis 交互的应用,Redis OM 是一个非常有用的工具。

Redis OM 这个OMORM有什么异同?

Redis OM 和 ORM(Object-Relational Mapping)在功能和应用场景上有一些相似之处,但也有显著的不同。下面是它们之间的主要异同点:

相同点:

  1. 抽象层:两者都提供了一种抽象层,使得开发者可以使用面向对象的方式来操作数据库,而不需要直接编写复杂的查询语句。
  2. 模型定义:两者都允许开发者定义数据模型(实体),然后通过这些模型来进行数据的增删改查操作。

不同点:

  1. 底层数据库类型

    • ORM:主要用于关系型数据库(如 MySQL、PostgreSQL、Oracle 等)。它将对象映射到数据库中的表,利用 SQL 进行数据操作。
    • Redis OM:专为 Redis 数据库设计,Redis 是一个内存中的数据结构存储系统,通常用于缓存、会话管理、实时分析等场景。
  2. 数据模型

    • ORM:映射的是关系型数据库的表结构,通常需要定义表之间的关系(如一对一、一对多、多对多等)。
    • Redis OM:映射的是 Redis 的数据结构(如字符串、哈希、列表、集合、有序集合等),并且可以利用 Redis 的特性,如 TTL(生存时间)、Pub/Sub(发布/订阅)等。
  3. 查询语言

    • ORM:使用 SQL 或者类似 SQL 的查询语言(如 JPQL)来进行数据操作。
    • Redis OM :使用 Redis 提供的命令和查询方式,如 GETSETHGETHSET 等。
  4. 性能和用途

    • ORM:适用于需要复杂查询和事务支持的场景,通常用于持久化数据存储。
    • Redis OM:适用于高性能、低延迟的场景,通常用于缓存、会话存储、实时数据处理等。
  5. 事务支持

    • ORM:关系型数据库通常支持 ACID(原子性、一致性、隔离性、持久性)事务,ORM 也提供了事务管理功能。
    • Redis OM:Redis 支持简单的事务(MULTI/EXEC),但不提供完整的 ACID 事务支持,更偏向于高性能和可用性。

小结:

  • ORM 是用于关系型数据库的对象映射工具,适合需要复杂查询和事务支持的场景。
  • Redis OM 是用于 Redis 数据库的对象映射工具,适合高性能、低延迟的场景,利用 Redis 的特性进行数据操作。

Redis OM适合做什么?在哪些场景下推荐使用?

Redis OM 适合在需要高性能、低延迟的数据访问和处理的场景中使用。以下是一些推荐使用 Redis OM 的具体场景:

  • 缓存:Redis 最常见的用途之一是作为缓存层。使用 Redis OM,可以轻松地将数据模型映射到 Redis 中,并实现高效的缓存读写操作。
  • 会话存储:在 Web 应用中,Redis 常用于存储用户会话数据。利用 Redis OM,可以方便地管理会话数据,并使用 Redis 的 TTL(生存时间)特性自动过期会话。
  • 实时分析和统计:Redis 的内存存储和高性能特性使其非常适合实时数据分析和统计。通过 Redis OM,可以快速存储和查询实时数据。
  • 消息队列:Redis 支持发布/订阅(Pub/Sub)模式和流(Streams)等特性,可以用来实现消息队列。Redis OM 可以帮助管理消息队列的数据模型。
  • 排行榜和计数器:Redis 的有序集合(Sorted Sets)非常适合实现排行榜功能,计数器也可以利用 Redis 的原子自增操作。使用 Redis OM,可以方便地操作这些数据结构。
  • 地理位置数据:Redis 提供了地理空间索引(Geo)功能,可以用来存储和查询地理位置数据。Redis OM 可以帮助管理和查询这些地理数据。
  • 实时聊天和通知系统:Redis 的发布/订阅(Pub/Sub)特性使其非常适合构建实时聊天和通知系统。Redis OM 可以简化这些系统的数据管理。
  • 短链接服务:Redis 的高性能和 TTL 特性使其适合用于短链接服务,快速生成和解析短链接,并自动过期。
  • 任务队列:Redis 可以用来实现分布式任务队列,通过 Redis OM,可以方便地管理任务队列的数据模型。
  • 临时数据存储:在某些场景下,可能需要存储一些临时数据(如验证码、临时文件链接等),Redis 的内存存储和 TTL 特性非常适合这种需求。

直接使用原生Redis一样 Redis OM 适合在需要快速读写、高并发、低延迟的场景中使用,特别是那些可以利用 Redis 特性的应用场景,如缓存、会话存储、实时分析、消息队列等。通过 Redis OM,可以更方便地管理和操作 Redis 数据,提升开发效率。

我原生实现或者利用ioredis实现不是也可以吗?为什么还要用Redis OM呢?

使用 Redis OM 相对于直接使用原生 Redis 或者 ioredis 确实有一些额外的优势,特别是在开发效率和代码可维护性方面。以下是使用 Redis OM 的理由:

  • 简化数据操作:Redis OM 提供了更高层次的抽象,使得你可以通过面向对象的方式来操作 Redis 数据,而不需要直接处理 Redis 命令。这可以简化代码,减少错误,提高开发效率。
  • 自动映射和序列化:Redis OM 自动处理对象与 Redis 数据结构之间的映射和序列化/反序列化工作。你不需要手动将对象转换为字符串或其他 Redis 数据类型,这减少了重复代码和潜在的错误。
  • 模型定义:Redis OM 允许你定义数据模型(实体),并通过这些模型来进行数据操作。这使得代码更加结构化和可读,特别是在处理复杂数据结构时更加明显。
  • 查询和过滤:Redis OM 提供了更高级的查询和过滤功能,使得你可以更方便地进行复杂的数据查询,而不需要手动编写复杂的 Redis 命令。
  • 事务和批处理:虽然 Redis 本身支持简单的事务(MULTI/EXEC),但 Redis OM 可以提供更方便的事务管理和批处理功能,使得代码更加简洁和易于维护。
  • 代码可读性和可维护性:使用 Redis OM 可以使代码更具可读性和可维护性。你可以通过定义清晰的数据模型和使用面向对象的操作方式,使得代码更容易理解和维护。
  • 减少重复代码:Redis OM 提供了一些常见操作的封装,如增删改查、索引、TTL 设置等。使用这些封装可以减少重复代码,提高开发效率。
  • 集成和扩展:Redis OM 通常与其他工具和框架有良好的集成和扩展性,使得你可以更方便地将 Redis OM 与现有的项目结合使用。

什么时候选择原生实现或 ioredis?

当然,在某些情况下,直接使用原生 Redis 或 ioredis 可能更合适:

  1. 性能要求极高:在对性能要求极高的场景下,直接使用原生 Redis 命令可能会更加高效,避免了额外的抽象层带来的开销。
  2. 简单操作:如果你的 Redis 操作非常简单,直接使用 ioredis 可能更加直接和高效。
  3. 特殊需求:如果你有一些 Redis OM 不支持的特殊需求,直接使用 ioredis 可以提供更多的灵活性。

总的来说虽然直接使用原生 Redis 或 ioredis 也可以实现大部分功能,但 Redis OM 提供了更高层次的抽象和便利性,特别是在数据模型管理、自动映射、复杂查询和事务管理等方面。选择使用 Redis OM 可以显著提高开发效率和代码可维护性,特别是在处理复杂数据结构和操作时。

环境准备

docker命令方式启动redis服务

启动 Redis 服务

sh 复制代码
docker run -d \
  --name redis \
  --restart always \
  -p 6379:6379 \
  -v redis-data:/data \
  redis:latest

启动 RediSearch 服务

sh 复制代码
docker run -d \
  --name redisearch \
  --restart always \
  -p 6380:6379 \
  -v redisearch-data:/data \
  redislabs/redisearch:latest

创建 Docker 卷

在启动容器之前,你需要确保 Docker 卷已经创建。你可以使用以下命令来创建卷:

sh 复制代码
docker volume create redis-data
docker volume create redisearch-data

检查运行状态

你可以使用以下命令来检查容器的运行状态:

sh 复制代码
docker ps

查看日志

如果你需要查看某个容器的日志,可以使用以下命令:

sh 复制代码
docker logs -f redis
docker logs -f redisearch

停止并删除容器

如果你需要停止并删除这两个容器,可以使用以下命令:

sh 复制代码
docker stop redis redisearch
docker rm redis redisearch

删除 Docker 卷

如果你还需要删除 Docker 卷,可以使用以下命令:

sh 复制代码
docker volume rm redis-data redisearch-data

docker-compose方式启动redis服务

如果你本地还有docker-compose,那可以使用这个yaml文件来启动

yaml 复制代码
version: "3.8"

services:
  redis:
    image: redis:latest
    container_name: redis
    restart: always
    ports:
      - "6379:6379"
    volumes:
      - redis-data:/data

  redisearch:
    image: redislabs/redisearch:latest
    container_name: redisearch
    restart: always
    ports:
      - "6380:6379"
    volumes:
      - redisearch-data:/data

volumes:
  redis-data:
    driver: local
  redisearch-data:
    driver: local

启动

sh 复制代码
docker-compose up -d

停止

sh 复制代码
docker-compose down

查看日志

sh 复制代码
docker-compose logs -f

注意!注意!注意!

docker命令和docker-compose用一个就行

1. 引言

1.1 文章系统需求和设计

在现代的内容管理系统中,文章系统是一个非常重要的组成部分。一个典型的文章系统需要具备以下功能:

  • 创建文章:允许用户撰写并发布新文章。
  • 读取文章:用户可以浏览并阅读已发布的文章。
  • 更新文章:作者可以编辑并更新已发布的文章。
  • 删除文章:作者或管理员可以删除不再需要的文章。

为了实现这些功能,文章系统需要有一个合理的数据组织和设计。通常,每篇文章会包含以下信息:

  • 文章标题(title)
  • 文章作者(author)
  • 发表日期(publishDate)
  • 文章内容(content)
  • 标签或分类(tags)

系统设计还需要考虑以下几个方面:

  • 数据存储:选择合适的数据库来存储文章数据。
  • 数据模型:定义文章的数据模型,包括字段和数据类型。
  • 接口设计:设计API接口,用于前端与后端的交互。
  • 用户权限:确保只有授权用户才能进行某些操作,如编辑和删除文章。

1.2 缓存机制的必要性

在高并发和大数据量的情况下,缓存机制是提升系统性能的关键。主要原因包括:

  • 减少数据库负载:通过缓存热点数据,可以减少对数据库的直接访问,降低数据库的压力。
  • 提高响应速度:缓存可以显著减少数据读取的时间,提高系统的响应速度,提升用户体验。
  • 应对高并发:缓存可以帮助系统更好地应对高并发请求,避免数据库成为瓶颈。

缓存机制通常包括以下策略:

  • 缓存数据:将常用的数据存储在缓存中,减少对数据库的访问。
  • 缓存失效:设置缓存的生存时间(TTL),确保缓存中的数据不过时。
  • 缓存更新:当数据发生变化时,及时更新缓存中的数据。

1.3 Redis OM for Node.js 的优势

Redis OM for Node.js 是一个强大的工具,简化了在 Node.js 应用中使用 Redis 的过程。其主要优势包括:

  • 简化数据建模:通过 Schema 定义,可以轻松地将 Redis 数据结构映射到 JavaScript 对象,避免了繁琐的低级命令操作。
  • 强大的搜索功能:内置的 RediSearch 支持全文搜索、排序、过滤等功能,极大地方便了数据查询。
  • 灵活的数据存储:支持将数据存储为 JSON 文档或 Hashes,提供了更大的灵活性。
  • 易于集成:与 Node Redis 无缝集成,提供了简单易用的接口,方便开发者快速上手。

通过 Redis OM for Node.js,我们可以更高效地构建一个功能强大的文章系统,并通过缓存机制提升系统性能。

2. 定义 Schema 和 实体

在构建文章系统时,首先需要定义数据的 Schema 和实体。Schema 定义了数据的结构和类型,而实体则是 Schema 的具体实例。在使用 Redis OM for Node.js 时,我们需要先连接到 Redis,然后定义文章和缓存的 Schema。

2.1 使用 Node Redis 连接到 Redis

在使用 Redis OM 之前,我们需要使用 Node Redis 库连接到 Redis 实例。Node Redis 是一个强大的 Redis 客户端,提供了丰富的功能来与 Redis 进行交互。

示例代码

首先,安装 Node Redis 和 Redis OM:

bash 复制代码
$ npm install redis
$ npm install redis-om

然后,创建一个连接到 Redis 的客户端:

javascript 复制代码
import { createClient } from "redis";

const redis = createClient({ url: "redis://localhost:6380" });
redis.on("error", (err) => console.log("Redis Client Error", err));

redis.connect().then(() => {
  console.log("redis connected");
});

在这段代码中,我们创建了一个 Redis 客户端,并处理了连接错误。await redis.connect() 用于连接到 Redis 实例。

注意,我本地有两个redis容器,本例6380是用的官方提供的RediSearch镜像

2.2 定义文章的 Schema

使用 Redis OM,我们可以轻松地定义文章的 Schema。Schema 定义了文章的结构和类型,包括标题、作者、内容等字段。

示例代码

javascript 复制代码
import { Schema } from 'redis-om';

const articleSchema = new Schema('article', {
  title: { type: 'text' },
  author: { type: 'string' },
  publishDate: { type: 'date' },
  content: { type: 'text' },
  tags: { type: 'string[]' }
});

在这段代码中,我们定义了一个名为 article 的 Schema,包含以下字段:

  • title:文章标题,类型为 text
  • author:文章作者,类型为 string
  • publishDate:发表日期,类型为 date
  • content:文章内容,类型为 text
  • tags:标签,类型为 string[]

2.3 定义缓存的 Schema

对于缓存数据,我们也需要定义相应的 Schema。缓存的数据结构通常比较简单,只需包含缓存的键和值。

示例代码

javascript 复制代码
const cacheSchema = new Schema('cache', {
  key: { type: 'string' },
  value: { type: 'text' }
});

在这段代码中,我们定义了一个名为 cache 的 Schema,包含以下字段:

  • key:缓存键,类型为 string
  • value:缓存值,类型为 text

通过定义这些 Schema,我们可以使用 Redis OM for Node.js 轻松地管理文章和缓存数据。接下来,我们将介绍如何创建和使用 Repository 来操作这些数据。

3. 创建和使用 Repository

在 Redis OM 中,Repository 提供了保存��读取和删除实体的方法。通过创建 Repository,我们可以方便地操作文章和缓存数据。

3.1 创建文章和缓存的 Repository

首先,我们需要为文章和缓存创建 Repository。Repository 是基于 Schema 的具体实现,用于操作实体数据。

示例代码

javascript 复制代码
import { Repository } from 'redis-om';

// 创建文章的 Repository
const articleRepository = new Repository(articleSchema, redis);

// 创建缓存的 Repository
const cacheRepository = new Repository(cacheSchema, redis);

在这段代码中,我们分别为文章和缓存创建了 Repository。articleSchemacacheSchema 是之前定义的 Schema,redis 是我们连接到的 Redis 客户端。

3.2 保存文章

使用 Repository 保存文章数据非常简单。我们只需创建一个 JavaScript 对象,然后调用 save 方法将其保存到 Redis 中。

示例代码

javascript 复制代码
  const article = {
    title: "Understanding Redis OM",
    author: "John Doe",
    publishDate: new Date(),
    content: "Redis OM for Node.js simplifies data modeling...",
    tags: ["redis", "nodejs", "database"],
  };

  const savedArticle = await articleRepository.save(article);
  console.log(`Article saved with ID: ${savedArticle[EntityId as any]}`);
  console.log("Article value ", savedArticle);

在这段代码中,我们创建了一个 article 对象,并使用 articleRepository.save 方法将其保存到 Redis 中。保存成功后,我们可以获取文章的 ID。

TypeScript是不允许使用Symbol所以使用了类型断言

完整测试代码

typescript 复制代码
import { createClient } from "redis";
import {  EntityId, Repository, Schema } from "redis-om";

const redis = createClient({ url: "redis://localhost:6380" });
redis.on("error", (err) => console.log("Redis Client Error", err));

redis.connect().then(async () => {
  console.log("redis connected");
  const articleSchema = new Schema("article", {
    title: { type: "text" },
    author: { type: "string" },
    publishDate: { type: "date" },
    content: { type: "text" },
    tags: { type: "string[]" },
  });

  const cacheSchema = new Schema("cache", {
    key: { type: "string" },
    value: { type: "text" },
  });

  // 创建文章的 Repository
  const articleRepository = new Repository(articleSchema, redis);

  // 创建缓存的 Repository
  const cacheRepository = new Repository(cacheSchema, redis);

  const article = {
    title: "Understanding Redis OM",
    author: "John Doe",
    publishDate: new Date(),
    content: "Redis OM for Node.js simplifies data modeling...",
    tags: ["redis", "nodejs", "database"],
  };

  const savedArticle = await articleRepository.save(article);
  console.log(`Article saved with ID: ${savedArticle[EntityId as any]}`);
  console.log("Article value ", savedArticle);
});
sh 复制代码
npm run dev
redis connected
Article saved with ID: 01J4228EHEG8CX84WDNESQAQ1K
Article value  {
  title: 'Understanding Redis OM',
  author: 'John Doe',
  publishDate: 2024-07-30T14:56:08.750Z,
  content: 'Redis OM for Node.js simplifies data modeling...',
  tags: [ 'redis', 'nodejs', 'database' ],
  [Symbol(entityId)]: '01J4228EHEG8CX84WDNESQAQ1K',
  [Symbol(entityKeyName)]: 'article:01J4228EHEG8CX84WDNESQAQ1K'
}

3.3 获取文章

从 Repository 获取文章数据同样非常简单。我们可以使用文章的 ID 来获取具体的文章。

示例代码

javascript 复制代码
const articleId = '01J4228EHEG8CX84WDNESQAQ1K';
const fetchedArticle = await articleRepository.fetch(articleId);

console.log(`Fetched Article: ${fetchedArticle.title} by ${fetchedArticle.author}`);

// 输出:Fetched Article: Understanding Redis OM by John Doe

在这段代码中,我们使用文章的 ID 调用 articleRepository.fetch 方法来获取文章数据,并输出文章的标题和作者。

3.4 删除文章

删除文章数据也非常方便。我们只需使用文章的 ID 调用 remove 方法即可。

示例代码

javascript 复制代码
const articleId = '01J4228EHEG8CX84WDNESQAQ1K';
await articleRepository.remove(articleId);

console.log(`Article with ID: ${articleId} has been deleted.`);

// 输出:Article with ID: 01J4228EHEG8CX84WDNESQAQ1K has been deleted.

在这段代码中,我们使用文章的 ID 调用 articleRepository.remove 方法来删除文章数据,并输出删除成功的消息。

通过创建和使用 Repository,我们可以轻松地管理文章和缓存数据。接下来,我们将介绍如何使用 Redis OM 的高级功能,如搜索和排序。

4. 实现缓存机制

为了提高系统的性能和响应速度,我们可以将文章数据缓存到 Redis 中。缓存机制可以显著减少数据库查询次数,并加快数据访问速度。

4.1 缓存文章数据

将文章数据缓存到 Redis 中,可以使用我们之前定义的 cacheRepository。我们可以将文章的 ID 作为缓存的键,文章内容作为缓存的值。

示例代码

javascript 复制代码
const cacheArticle = async (article) => {
  const cacheKey = `article:${article[EntityId]}`;
  const cacheValue = JSON.stringify(article);

  await cacheRepository.save({ key: cacheKey, value: cacheValue });
  console.log(`Article cached with key: ${cacheKey}`);
};

// 示例文章数据
const article = {
  title: "Understanding Redis OM",
  author: "John Doe",
  publishDate: new Date(),
  content: "Redis OM for Node.js simplifies data modeling...",
  tags: ['redis', 'nodejs', 'database']
};

const savedArticle = await articleRepository.save(article);
await cacheArticle(savedArticle);

在这段代码中,我们定义了一个 cacheArticle 函数,将文章数据保存到缓存中。我们使用文章的 ID 作为缓存键,并将文章对象序列化为 JSON 字符串作为缓存值。

4.2 从缓存中读取数据

从缓存中读取文章数据时,我们可以使用缓存键来获取缓存值。如果缓存中存在该键,我们就可以直接返回缓存的数据;如果不存在,则从数据库中获取数据,并将其缓存。

示例代码

javascript 复制代码
const getArticle = async (articleId) => {
  const cacheKey = `article:${articleId}`;
  const cachedArticle = await cacheRepository.search()
    .where('key').equals(cacheKey)
    .return.first();

  if (cachedArticle) {
    console.log(`Cache hit for key: ${cacheKey}`);
    return JSON.parse(cachedArticle.value);
  } else {
    console.log(`Cache miss for key: ${cacheKey}`);
    const article = await articleRepository.fetch(articleId);
    await cacheArticle(article);
    return article;
  }
};

const articleId = '01FJYWEYRHYFT8YTEGQBABJ43J';
const article = await getArticle(articleId);
console.log(`Fetched Article: ${article.title} by ${article.author}`);

在这段代码中,我们定义了一个 getArticle 函数。首先检查缓存中是否存在该文章数据,如果存在则返回缓存的数据;否则,从数据库中获取文章数据,并将其缓存。

4.3 缓存失效策略

为了避免缓存数据过期或占用过多内存,我们需要设置缓存失效策略。常见的缓存失效策略包括 TTL(生存时间)和 LRU(最近最少使用)。

TTL(生存时间)

TTL 指定缓存数据的有效期,过期后自动删除。可以在保存缓存数据时设置 TTL。

示例代码

javascript 复制代码
const cacheArticleWithTTL = async (article, ttlInSeconds) => {
  const cacheKey = `article:${article[EntityId]}`;
  const cacheValue = JSON.stringify(article);

  await cacheRepository.save({ key: cacheKey, value: cacheValue });
  await redis.expire(cacheKey, ttlInSeconds);
  console.log(`Article cached with key: ${cacheKey} and TTL: ${ttlInSeconds} seconds`);
};

const ttlInSeconds = 3600; // 缓存1小时
await cacheArticleWithTTL(savedArticle, ttlInSeconds);

在这段代码中,我们在保存缓存数据后,使用 redis.expire 方法设置缓存的 TTL。

LRU(最近最少使用)

LRU 策略会自动删除最近最少使用的缓存数据。可以在 Redis 配置中启用 LRU 策略。

示例代码

在 Redis 配置文件 redis.conf 中,启用 LRU 策略:

conf 复制代码
maxmemory <bytes>
maxmemory-policy allkeys-lru

通过设置 maxmemorymaxmemory-policy,Redis 将根据 LRU 策略自动管理内存。

通过实现缓存机制,我们可以显著提高系统的性能和响应速度。接下来,我们将探讨如何利用 Redis OM 的高级功能,如搜索和排序。

完整代码

typescript 复制代码
import { createClient } from "redis";
import { EntityId, Repository, Schema } from "redis-om";

const redis = createClient({ url: "redis://localhost:6380" });
redis.on("error", (err) => console.log("Redis Client Error", err));

redis.connect().then(async () => {
  console.log("redis connected");
  const articleSchema = new Schema("article", {
    title: { type: "text" },
    author: { type: "string" },
    publishDate: { type: "date" },
    content: { type: "text" },
    tags: { type: "string[]" },
  });

  const cacheSchema = new Schema("cache", {
    key: { type: "string" },
    value: { type: "text" },
  });

  // 创建文章的 Repository
  const articleRepository = new Repository(articleSchema, redis);

  // 创建缓存的 Repository
  const cacheRepository = new Repository(cacheSchema, redis);

  // 确保为缓存 schema 创建索引
  await cacheRepository.createIndex();

  const article = {
    title: "Understanding Redis OM",
    author: "John Doe",
    publishDate: new Date(),
    content: "Redis OM for Node.js simplifies data modeling...",
    tags: ["redis", "nodejs", "database"],
  };

  const savedArticle = await articleRepository.save(article);
  console.log(`Article saved with ID: ${savedArticle[EntityId as any]}`);
  console.log("Article value ", savedArticle);

  // 获取文章
  const articleId = savedArticle[EntityId as any].toString();
  const fetchedArticle = await articleRepository.fetch(articleId);

  console.log(
    `Fetched Article: ${fetchedArticle.title} by ${fetchedArticle.author}`
  );

  // 删除文章
  // await articleRepository.remove(articleId);

  // console.log(`Article with ID: ${articleId} has been deleted.`);

  // 缓存文章
  const cacheArticle = async (article: Record<string, any>) => {
    const cacheKey = `article:${article[EntityId as any]}`;
    const cacheValue = JSON.stringify(article);

    await cacheRepository.save({ key: cacheKey, value: cacheValue });
    console.log(`Article cached with key: ${cacheKey}`);
  };

  // 示例文章数据
  await cacheArticle(await articleRepository.save(article));
  const getArticle = async (articleId: string) => {
    const cacheKey = `article:${articleId}`;
    const cachedArticle = await cacheRepository
      .search()
      .where("key")
      .equals(cacheKey)
      .return.first();

    if (cachedArticle) {
      console.log(`Cache hit for key: ${cacheKey}`);
      return JSON.parse(cachedArticle.value);
    } else {
      console.log(`Cache miss for key: ${cacheKey}`);
      const article = await articleRepository.fetch(articleId);
      await cacheArticle(article);
      return article;
    }
  };

  const article1 = await getArticle(articleId);
  console.log(`Fetched Article: ${article1.title} by ${article1.author}`);

  // 缓存策略
  const cacheArticleWithTTL = async (article:any, ttlInSeconds:any) => {
    const cacheKey = `article:${article[EntityId]}`;
    const cacheValue = JSON.stringify(article);

    await cacheRepository.save({ key: cacheKey, value: cacheValue });
    await redis.expire(cacheKey, ttlInSeconds);
    console.log(
      `Article cached with key: ${cacheKey} and TTL: ${ttlInSeconds} seconds`
    );
  };

  const ttlInSeconds = 3600; // 缓存1小时
  await cacheArticleWithTTL(savedArticle, ttlInSeconds);
});

输出

shell 复制代码
# npm run dev
redis connected
Article saved with ID: 01J4287PHS8WJ3PFBZ7CPTBX3E
Article value  {
  title: 'Understanding Redis OM',
  author: 'John Doe',
  publishDate: 2024-07-30T16:40:35.641Z,
  content: 'Redis OM for Node.js simplifies data modeling...',
  tags: [ 'redis', 'nodejs', 'database' ],
  [Symbol(entityId)]: '01J4287PHS8WJ3PFBZ7CPTBX3E',
  [Symbol(entityKeyName)]: 'article:01J4287PHS8WJ3PFBZ7CPTBX3E'
}
Fetched Article: Understanding Redis OM by John Doe
Article cached with key: article:01J4287PHXV0TVX5MGWZ7CWGNN
Cache miss for key: article:01J4287PHS8WJ3PFBZ7CPTBX3E
Article cached with key: article:01J4287PHS8WJ3PFBZ7CPTBX3E
Fetched Article: Understanding Redis OM by John Doe
Article cached with key: article:01J4287PHS8WJ3PFBZ7CPTBX3E and TTL: 3600 seconds

总结

本文详细探讨了如何使用 Redis OM for Node.js 构建一个高效的文章管理系统,并通过缓存机制提升系统性能。文章首先介绍了 Redis OM 的主要特性和优势,包括对象映射、自动序列化和反序列化、高级查询和过滤、事务和批处理等,使开发者能够以更高层次的抽象来操作 Redis 数据。接着,文章展示了如何通过 Docker 和 Docker Compose 启动 Redis 和 RediSearch 服务,并使用 Node Redis 库连接到 Redis 实例。

在具体实现方面,本文定义了文章和缓存的 Schema,并创建了相应的 Repository 来管理数据。通过示例代码,展示了如何保存、获取和删除文章数据,以及如何将文章数据缓存到 Redis 中,以减少数据库查询次数和提高响应速度。此外,文章还介绍了缓存失效策略,包括 TTL(生存时间)和 LRU(最近最少使用),以确保缓存数据的有效性和内存管理。

相关推荐
码爸41 分钟前
flink 批量压缩redis集群 sink
大数据·redis·flink
Adolf_19931 小时前
Flask-JWT-Extended登录验证, 不用自定义
后端·python·flask
叫我:松哥1 小时前
基于Python flask的医院管理学院,医生能够增加/删除/修改/删除病人的数据信息,有可视化分析
javascript·后端·python·mysql·信息可视化·flask·bootstrap
海里真的有鱼1 小时前
Spring Boot 项目中整合 RabbitMQ,使用死信队列(Dead Letter Exchange, DLX)实现延迟队列功能
开发语言·后端·rabbitmq
工业甲酰苯胺1 小时前
Spring Boot 整合 MyBatis 的详细步骤(两种方式)
spring boot·后端·mybatis
微刻时光2 小时前
Redis集群知识及实战
数据库·redis·笔记·学习·程序人生·缓存
新知图书2 小时前
Rust编程的作用域与所有权
开发语言·后端·rust
丁总学Java2 小时前
如何使用 maxwell 同步到 redis?
数据库·redis·缓存
蘑菇蘑菇不会开花~2 小时前
分布式Redis(14)哈希槽
redis·分布式·哈希算法
爱吃南瓜的北瓜2 小时前
Redis的Key的过期策略是怎样实现的?
数据库·redis·bootstrap