【问题标题】:Spring Redis: Range query "greater than" on a fieldSpring Redis:范围查询“大于”字段
【发布时间】:2021-04-25 14:53:57
【问题描述】:

我正在使用 Redis 存储一些数据,然后查询它并使用最新信息对其进行更新。

考虑一个例子:

我收到文件数据,其中包含有关文件的信息以及该文件的物理存储位置。

  • 一个架子有多个架子,每个架子可以有多个文件。
  • 每个文件都有一个版本字段,并在对文件执行操作时更新(递增)。

我打算如何存储?

  • 我需要根据“shelfID + rack ID”查询——获取所有文件。
  • 我需要根据“shelfID + rack ID + version > XX”查询--获取所有超过指定版本的文件。

现在,要获取属于架子和机架的所有文件,可以在 Spring Data Redis 中实现。 我创建了一个由 2 个 ID 组合而成的键,然后基于此键进行查询。

  private <T> void save(String id, T entity) {
    redisTemplate.opsForValue().set(id, entity);
  }

但是,如何查询版本字段? 我将“版本”字段保留为@Indexed,但 spring 存储库查询不起作用。

@RedisHash("shelves")
public class ShelfEntity {

  @Indexed
  @Id
  private String id;

  @Indexed
  private String shelfId;

  @Indexed
  private String rackId;

  @Indexed
  private String fileId;

  @Indexed
  private Integer version;

  private String fileName;    

  // and other updatable fields
}         

存储库方法:

List<ShefEntity> findAllByShelfIdAndRackIdAndVersionGreaterThan(String centerCd,
      String floorCd, int version);

上面,给出错误:

java.lang.IllegalArgumentException: GREATER_THAN (1): [IsGreaterThan, Redis 查询派生不支持 GreaterThan]

  • 问。如何根据版本大于查询?
  • 问。 Spring Data Redis 甚至可以实现吗?
  • 问。如果可能,我应该如何对数据进行建模(到什么数据结构中),以便进行此类查询?
  • 问。如果我们不使用 Spring,如何在 Redis 中使用 redis-cli 和数据结构来做到这一点?

可能是这样的:


我不确定如何在 Redis 中建模?


更新 2: 一个架子可以有N个架子。 一个机架可以有 N 个文件。 每个文件对象都有一个版本。 此版本已更新(o -> 1 -> 2....)

我只想存储文件的最新版本。 所以,如果我们有 1 个文件对象 货架编号 - 1 机架 ID - 1 文件 ID - 1 版本 - 0 ....更新版本...我们仍然应该有 1 个文件对象。 版本 - 1

我尝试将 key 作为shelfId + rackId 的MD5 散列保存在散列数据结构中。 但无法查询版本。

我也尝试过使用 ZSet。

这样保存:

  private void saveSet(List<ShelfEntity> shelfInfo) {
    for (ShelfEntity item : shelfInfo) {
      redisTemplate.opsForZSet()
          .add(item.getId(), item, item.getVersion());
    }
  }

所以,版本就变成了乐谱。

但问题是我们无法更新集合中的项目。 所以对于一个fileId,有多个版本。 当我查询时,我得到了重复。

获取代码:

Set<ShelfEntity> objects = (Set<ShelfEntity>) (Object) redisTemplate.opsForZSet()
        .rangeByScore(generateMd5Hash("-", shelfId, rackId), startVersion,
            Double.MAX_VALUE);

现在,这是模仿版本> XX的尝试

【问题讨论】:

  • 不,直接使用 Redis 存储库是不可能的。为什么不存储在 RDMS 中做复杂的查询?
  • 你能列出你所有的访问模式吗?
  • @sonus21 感谢您的评论。我可以知道什么访问模式?我可能需要对上述内容进行查询:findAllByShelfIdAndRackIdAndVersionGreaterThan
  • 您还想对该 ShelfEntity 集合/项目/表执行哪些其他查询?
  • Prime 搜索是针对版本的。如前所述:通过shelfId、rackId和version > XX查找。除此之外,它将通过shelfId + rackId + fileId 找到(以获取确切的文件)

标签: spring redis spring-data spring-data-redis


【解决方案1】:

为每个 shelfIdrackId 组合创建 ZSET

在Redis中使用两种方法保存和更新记录

// this methods stores all shelf info in db
public void save(List<ShelfEntity> shelfInfo) {
    for (ShelfEntity item : shelfInfo) {
      redisTemplate.opsForZSet()
          .add(item.getId(), clonedItem, item.getVersion());
    }
}

使用 update 删除旧的并插入新的,Redis 不支持 key 更新,因为它是一张表,所以你需要删除现有的并添加新记录

public void update(List<ShelfEntity> oldRecords, List<ShelfEntity> newRecords) {
    if (oldRecords.size() != newRecords.size()){
       throw new IlleagalArgumentException("old and new records must have same number of entries");
    }
    for (int i=0;i<oldRecords.size();i++) {
      ShelfEntity oldItem = oldRecords.get(i);
      ShelfEntity newItem = newRecords.get(i);
      redisTemplate.opsForZSet().remove(oldItem.getId(), oldItem); 
      redisTemplate.opsForZSet()
          .add(newItem.getId(), newItem, newItem.getVersion());
    }
}

从 ZSET 中读取带有分数的项目。

List<ShefEntity> findAllByShelfIdAndRackIdAndVersionGreaterThan(String shelfId,
      String rackId, int version){
    Set<TypedTuple<ShelfEntity>> objects = (Set<TypedTuple<ShelfEntity>>)  redisTemplate.opsForZSet()
        .rangeWithScores(generateMd5Hash("-", shelfId, rackId), new Double(version),
            Double.MAX_VALUE);


    List<ShelfEntity> shelfEntities = new ArrayList<>();
    for (TypedTuple<ShelfEntity> entry:  objects) {
      shelfEntities.add(entry.getValue().setVersion( entry.getScore().intValue()));
    }
    return shelfEntities;
  }

【讨论】:

  • 啊!我使用 ZSet 取得了一些进展,但是将版本设置为 null 作为分数可用的部分在我的脑海中消失了。这是我使用 Redis 的第二天,但这是一次学习。我会用这个更新我的代码并尝试!
  • 但是,如果实体中的数据被更新,我将在 ZSet 中获得相同的shelfId、rackId 和fileId 的重复记录。我忘了在实体中添加更多字段,但正如我提到的,执行操作时会更新版本(假设有人更改了文件名)。现在我有 2 条记录,用于具有 2 个文件名的同一个文件。
  • 这会导致 Get 查询返回重复记录。 (我不想在 App 代码中过滤)
  • 不确定这个文件的其他内容,你应该为每个文件分配修复fileId,在每次更新时,只有版本应该在 Redis 中更改,而不是 fileId。实际的fileId 可能是fileId#version,您需要更改处理文件更改事件的方式。
  • 这与任何 ID 无关。对整个对象的任何数据(字段)更新都会在 ZSet 中创建一条新记录。不仅仅是fileId#version。文件名的更新,不会更新 fileId。但是版本(分数)会改变。而且我还需要存储新的更新名称。这不是更新应该做的吗?
猜你喜欢
  • 2020-05-10
  • 2011-08-30
  • 2016-12-24
  • 2016-03-05
  • 2020-05-10
  • 1970-01-01
  • 1970-01-01
  • 2021-08-17
  • 1970-01-01
相关资源
最近更新 更多