使用 zrange 按周期查询 Redis 时间序列

sof*_*2er 1 caching redis

我已经搜索并做了一些阅读,但是找不到答案。我相信我可能对缓存实现有错误的想法。

我有一组历史和当前的金融股票数据,我想将它们缓存起来以便快速访问 API,这些数据遵循以下格式:

id: uuid
timestamp: unix_timestamp
data: {...}
Run Code Online (Sandbox Code Playgroud)

目的是允许用户请求缓存提供的数据,但是我希望用户能够提供一个时间范围,例如 1483142400 - 1493510400秒的形式(例如 300、900 、1800、3600、86400)。

我现在在如何处理这个问题上进退两难,我最初的想法是将所有时间戳存储zadd在每个股票的单个排序集中,然后使用zrange. 然而,这将是昂贵的,因为我会查询太多不必要的数据,除非 Redis 能够在一段时间内“逐步”通过(如果我可以提供所有密钥,因为它们遵循非随机的一致格式)。

我的第二个想法是改用单独的密钥,因为我的财务数据是针对股票 XYZ 的:

ZADD XYZ_300 1501200300 'data' 1501200600 'data'
ZADD XYZ_900 1501200900 'data' 1501201800 'data'
ZADD XYZ_1800 1501201800 'data' 1501203600 'data'
ZADD XYZ_3600 1501203600 'data' 1501207200 'data'
Run Code Online (Sandbox Code Playgroud)

然而,这意味着我有一些冗余数据正在进行,因为所有集合都将包含在 300 秒表中也可用的数据。

此外,我可能有一些股票,例如XYZABCDEF。我希望能够每小时查询 3 天前到 2 天前的所有上述所有股票的数据,我不确定是否应该使用某种ZUNIONSTORE或某种管道来有效地执行此操作一次查询多个范围。

现在唯一的保证是我确切地知道我的密钥是什么,因为它们总是四舍五入到最接近的 00:00、05:00、15:00 和 30:00 分钟 unix 时间戳。正在从运行 Django(存储 json)的 Python 中设置/查询缓存本身。可能我最好将其存储在文件而不是缓存中(因为我使用的是 AWS,我可以利用新的 EFS 存储系统让多个服务器共享相同的底层“硬盘/卷”)

小智 5

据我了解,您有 2 个基本要求:1. 能够在任何随机时间范围内进行查询 2. 按时间跨度查询结果聚合

ZRANGE 基本上是一个排序集,具有(非常)高效的查询运行时。从Redis手册

O(log(N)+M) 其中 N 是排序集中的元素数,M 是返回的元素数。

许多人的一个常见用例是将时间序列数据保存在一个排序集中:

  • 每个时间戳添加一个数据键(哈希类型)以及值及其标识(例如股票)。
  • 每个 ZSET 项数据必须是唯一的,因此必须将数据保存在单独的哈希键中
  • 按键查询具体股票,用ZRANGE获取具体时间,性能相当不错。可以在此处找到有关此技术的好文章:https : //www.infoq.com/articles/redis-time-series

关于性能,我们需要同时查看插入和读取:

  1. 写入性能:ZADD是 O(log(N)) + Hash 写入性能 O(N) 其中 N 是数据键的数量(例如股票)。
  2. 读取性能:ZRANGE O(log(N)+M) + HGET for a specific key O(1) 如果您有很多样本 (M),您的读取性能将会降低。

关于要求 #2:在 redis 中没有简单的方法进行聚合。

总而言之,时间序列没有最佳的数据结构,您可以使用 ZSET(很多人都这样做)它可以工作,但不是非常优化,也没有空间效率。

您可以通过以下方式改进:

  • 使用 LUA 代码查询原始排序集并在不将大量数据移动到客户端的情况下执行一些魔术。
  • 使用新的模块 API编写您自己的数据结构。

或者您可以尝试使用我编写和维护的模块:https : //github.com/danni-m/redis-timeseries。特点是:

  • 时间序列的高效数据存储(目前每个样本为 16 字节,我正在研究将降低空间的大猩猩压缩)
  • 按时间段聚合查询(要求 #1)
  • 使用不同聚合方法(最大值、最小值、平均值、总和)的自动压缩/下采样(一对多)
  • 用于添加和查询时间序列的非常简单的命令。