在加入 Redis 之前,我曾在一家咨询公司工作,涉及物联网 (IoT) 等技术。如果你不熟悉这个概念,物联网 (IoT) 就是关于连接到互联网的智能设备。许多这些设备包括传感器,可以产生关于现实世界的指标。一个值得注意的物联网用例例如是共享单车公司,单车本身连接到互联网,并持续向其所有者通知其位置和其他状况。但也许物联网技术最大、最有前景的应用来自工厂,即工业物联网 (IIoT)。
尽管物联网开启了无限可能,市场预测几年内将突破 1 万亿美元,并且预计有 416 亿台物联网设备将产生 79.4 ZB 的数据,但在实现基于物联网的服务时,仍然存在一些挑战需要克服,主要集中在大规模处理物联网数据方面。
回想起我的咨询经验,我记得曾与一家公司的数据科学团队会面,他们遇到了物联网解决方案的问题。该团队面临的主要难题是关于将数据存储在 NoSQL 文档数据库中。他们试图在两种数据模型之间做出选择,一种会优化查询速度,而另一种会优化空间效率。
第一个模型是将每个数据点存储为一个文档,像这样
{ “Id”: “1-2-3”, “Time”: 123123123, “Longitude”: 0.123, “Latitude”: 0.123, “Battery”: 0.66 }
这种方法会带来最快的查询速度,但缺点是会占用大量存储空间。另一个旨在更有效地利用空间的模型看起来像这样
{ “ChunkId”: “abc”, “ChunkStartTime”: 123123123, “Chunk”:[ [ “1-2-3”, 123123123, 0.123, 0.123, 0.66 ], [ “4-5-6”, 456456456, 0.456, 0.456, 0.99 ], ... ] }
正如你所见,第二个模型更为复杂:每个文档在一个块中包含多个数据点。这个模型需要更复杂的查询来处理这些块,但作为回报,它节省了一些空间。
当时我没有找到解决这个问题的良策,但现在我意识到,有了 Redis Streams,这个问题根本就不存在了。
当你拥有一个杂乱的数据流,它是面向时间的且本质上不可变(通常物联网数据就是这种情况),Redis Streams 可能是用于初始摄取的正确数据结构。
一个 Redis Stream 键包含一个索引的条目列表。每个索引 ID 是一个毫秒级精度的 timestamp,后跟同一毫秒内发生的事件的 sequence number。每个条目是一系列 field-value 对,几乎与 Redis Hash 相同。“几乎”是因为 Redis Hash 很好地可视化了你可以放入 stream 的内容,但有一个小区别:一个 Redis Stream 条目可以有同一字段的多个实例,而 Redis Hash 中的字段是唯一的。
以下命令展示了如何向 Redis Stream 添加一个条目。查看文档以了解更多信息。
> XADD mystream * time 123123123 lon 0.123 lat 0.123 battery 0.66
Redis Streams 使用基数树 (radix tree) 数据结构进行索引,该结构压缩索引 ID 并允许对条目进行常数时间访问。
Redis Streams 还采用了另一种应用于字段名的智能空间节省机制。
你可能不常考虑这个问题,但你为条目字段选择的名称会影响每个条目占用的空间。使用 SQL 数据库时不会出现这个问题,因为模式在表级别是固定的,但缺点是你失去了灵活性。使用 NoSQL 文档数据库时,你获得了更大的灵活性,但文档会有重复存储代表字段名的字符串的开销。
在 Redis Streams 中,stream 中的每个条目可以有不同的字段集,因此你可以拥有所需的全部灵活性,但如果你保持字段集稳定,Redis 将不会多次存储它们的名称副本。根据你拥有的字段数量,通过此功能避免的开销可能会很大。
举个简单的例子,假设你有 100 万个条目,每个条目有 20 个字段,平均字段名长度为 15 字节。这意味着每 100 万个条目可以避免大约 300 兆字节的开销。
虽然 Redis Streams 非常适合物联网用例,但它们绝不仅限于物联网。反过来说,Redis 中还有许多其他数据结构可以帮助你在大规模数据摄取时节省空间,例如概率数据结构。
掌握 Redis Streams 后,可以考虑使用 RedisTimeSeries 模块来存储从你摄取的原始数据中派生的数值时间序列数据。如果你想了解更多关于 Redis Streams 和 RedisTimeSeries 的信息,请报名参加我们于 2020 年 5 月 12 日至 13 日在线举行的 RedisConf Takeaway 的培训日活动。