暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Redis 可以用作关系数据库吗?

原创 Ellison 2023-08-14
266

探索在 Java + Spring 中使用 Redis 是多么容易,并发现关系数据库的五个区别。

让我们从这个问题开始,“如何使用 Redis?我相信大多数人将其用作服务的缓存。我希望你知道它可以做的不仅仅是这些。最近,我在一次会议上发表了一份报告,介绍了我们如何将部分数据移动到 Redis 并首先将请求发送到它。现在我想告诉你的不是我们如何应用它,而是关于在使用 Spring 及其抽象时,你可能不会立即注意到替换的事实。

让我们尝试编写一个小型的Spring应用程序,它将使用两个PostgreSQL和Redis数据库。我想指出的是,我们将在数据库中存储的不是某种平面对象,而是来自具有嵌套字段(内连接)的关系数据库的完整对象。为此,我们需要在 Redis 中安装一些插件,例如 RedisJSON 和 RediSearch。第一个允许我们以 JSON 格式存储我们的对象,第二个允许我们按对象的任何字段进行搜索,甚至是嵌套字段。

要使用关系数据库,我们将选择 Spring Data JPA。为了使用 Redis,我们将使用优秀的 Redis OM Spring 库,它允许您在抽象级别使用数据库。这是数据JPA的模拟。在后台,Redis OM Spring 拥有 Spring 和 Jedis 使用数据库所需的所有依赖项。我们不会详述细节,因为本文与此无关。

让我们编写代码

因此,让我们编写代码。假设我们需要编写一个调用到数据库的某个实体。在此实体中,我添加了其他对象,例如 、 和其他对象。"downtime""place""reason"

关系数据库的实体:


1 @Entity

2@Table(schema = "test", name = "downtime")

3 public class Downtime {

4

5    @Id

6   private String id;

7    private LocalDateTime beginDate;

8 private LocalDateTime endDate;

9  @ManyToOne(fetch = FetchType.EAGER)

10 @JoinColumn(name = "area")

11  private Place area;

12 @ManyToOne(fetch = FetchType.EAGER)

13  @JoinColumn(name = "cause")

14 private Cause cause;

15 ...





这段代码不需要注释。我们需要为Redis做同样的事情。

Redis的对象:


1

@Document

2

public class DowntimeDoc {

3

4

    @Id

5

    @Indexed

6

    private String id;

7

    @Indexed

8

    private LocalDateTime beginDate;

9

    private LocalDateTime endDate;

10

    @Indexed

11

    private PlaceDoc area;

12

    @Indexed

13

    private CauseDoc cause;

14

    ....





在这种情况下,我们使用 代替 。此注释指示我们的对象是一个实体。它将存储在数据库中的键“包路径 + 类名 + Idx”下。@Entity@Document

注释意味着它将被索引以供搜索。如果未指定此注释,则此字段将保存在数据库中,但搜索它将返回空结果。您可以根据需要添加此注释。数据库中已有的数据将以异步方式编制索引;新数据将同步编制索引。@Indexed

接下来,我们将创建一个存储库,它基本上可以从数据库中获取数据。

关系数据库的示例:


1

public interface DowntimeRepository extends JpaRepository<Downtime, String> {

2

}





瑞迪斯的例子:


1

public interface DowntimeRedisRepository extends RedisDocumentRepository<DowntimeDoc, String> {

2

}





不同之处在于,我们扩展了当前接口,这扩展了 Spring 的标准 CRUD 接口。RedisDocumentRepository

让我们添加一个方法来查找我们指定原因的第一次停机时间。


1

public interface DowntimeRepository extends JpaRepository<Downtime, String> {

2

3

    Downtime findFirstByCauseIdOrderByBeginDate(String causeId);

4

}





雷迪斯也是如此:


1

public interface DowntimeRedisRepository extends RedisDocumentRepository<DowntimeDoc, String> {

2

3

    DowntimeDoc findTopByCause_IdOrderByBeginDateAsc(String causeId);

4

}





正如您所注意到的,如果您通过抽象编写使用数据库的代码那么差异几乎不明显。此外,Redis OM Spring 允许您使用注释自己编写查询,就像在 Spring Data JPA 中一样。@Query

下面是 HQL 查询的示例:


1

@Query("SELECT d FROM Downtime d" +

2

        " JOIN FETCH d.area " +

3

        " JOIN FETCH d.cause" +

4

        " JOIN FETCH d.fixer" +

5

        " JOIN FETCH d.area.level " +

6

        " WHERE d.area IN ?1 AND (d.beginDate BETWEEN ?2 AND ?3 OR d.cause IN ?4) ")

7

List<Downtime> findAllByParams(List<Place> workPlace, LocalDateTime start, LocalDateTime end, List<Cause> causes);




与 Redis 相同:


1

@Query("(@area_id:{$areas} ) & (@beginDate:[$start $end] | @cause_id:{$causes})")

2

Page<DowntimeDoc> findByParams(@Param("areas") List<String> areas,

3

                               @Param("start") long start,

4

                               @Param("end") long end,

5

                               @Param("causes") List<String> causes, Pageable pageable);





对于 Redis 的情况,我们只需指定该部分的条件。没有必要指示需要附加哪些字段,因为它们始终是从数据库中提取的。但是,我们不能拉出所有字段,而是使用附加参数指定我们到底需要什么。您还可以指定排序、限制和偏移 - 顺便说一下,后者在 HQL 中是不可能的。在此示例中,我传递给该方法,它将在数据库级别工作,而不是将所有数据拉入服务,并在其中对其进行修剪(就像 Hibernate 一样)。“WHERE”“returnFields”Pageable

此外,Redis OM Spring 允许您使用 编写查询,这类似于 Stream API。EntityStream

下面是使用 .EntityStream



1

2

entityStream

3

        .of(DowntimeDoc.class)

4

        .filter(DowntimeDoc$.AREA_ID.in(filter.getWorkPlace().toArray(String[]::new)))

5

        .filter(between + " | " + causes)

6

        .map(mapper::toEntity)

7

        .collect(Collectors.toList());





在此示例中,我使用一个使用元模型的筛选器,将参数作为字符串传递给第二个筛选器,以显示这两个选项都有效。你猜对了:接受一组中间操作,并在调用终端操作时执行这组。EntityStream

雷迪斯OM春天的细微差别

让我告诉你使用Redis OM Spring的一些细微差别:

  • 您将无法使用 UUID 作为主键。您可以将其指定为字符串,它将被索引。但是在搜索时,您将需要 转义空格 :@id


{2e5af82m\-02af\-553b\-7961\-168878aa521е}


还有一件事:如果您搜索存储库,则没有任何效果,因为代码中有这样一个表达式可以删除所有屏幕:RedisDocumentRepository


String regex = "(\\$" + key + ")(\\W+|\\*|\\+)(.*)";


因此,为了按此类字段进行搜索,您必须直接在 RediSearch 中编写查询。我有一个如何在演示项目中执行此操作的示例。

  • 在搜索方法时,如果需要集合,则必须传递指示预期行的大小或在 ;否则,您将收到最多 10 条记录。RedisDocumentRepositoryPageable@Query
  • 该方法仅支持一个参数进行排序。这是通过 编写查询来解决的。FT.SEARCH (@Query)FT.AGGREGATE (@Aggregation)

以上列表并非详尽无遗。在使用这些库时,我发现了许多不同的东西,但这只是数据库实现的一个特殊性。最后,我没有在这篇文章中放关于 Redis 插件的信息,也没有谈论 Redis OM Spring 的所有功能;否则,这篇文章将是巨大的,不可读。

结论

我展示了目前,Redis 允许您存储具有大型嵌套的对象,并允许您搜索该对象的字段。如果你通过存储库中的抽象来处理数据,那么有些人可能看不出与Spring Data JPA有任何区别,特别是如果你使用一些简单的查询,如、、等,以及通过方法名称进行的查询。SavedeletefindAllBy

示例可以在 GitHub 上找到。

一切成功。

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论