时间和源依赖日志数据的最佳数据结构,以便快速浏览?

时间:2011-02-19 15:00:31

标签: c++ algorithm

我有现场总线数据,这些数据以数据包形式发送,并包含来自数据源的数据(例如浮点数)。

=>我得到了带有源ID和值的时间戳。

现在我想创建一个小程序(实际上是C ++中的日志记录守护程序,通过HTTP提供查询接口以在绘图中显示数据),用户可以在其中选择一些来源和有趣的时间范围,然后获取数据。 这个deamon将在基于Linux的嵌入式系统下运行。

所以我的问题是:该数据的最有效(查询性能和内存消耗)数据存储方案是什么?


附录#1:

虽然我认为算法问题非常有趣,但我会提供一些关于导致这个问题的问题的信息:

  • 数据速率通常为3个数据包/秒(通常为突发最多30个/秒)
  • 有趣的数据可能只有一个月(越多越好;算法可能会使用允许最后一天超快速查找的层次结构,上周的快速查找以及旧数据的可接受查找)
  • ID(目前)为32位宽。
  • 使用了很多1000个ID - 但事先并不知道哪个ID和用户可能随时使用其他ID
  • 存储的值将具有不同的数据类型(布尔值,整数,浮点数 - 甚至可以使用14个字节宽的字符串)

做一些数学运算:

  • 假设32位时间戳+ 32位ID + 32位值平均会创建一个存储12个字节的数据
  • 这将是一个月12 * 3 * 60 * 60 * 24 * 30 =大约100 MB的数据来过滤低谷(实时使用500 MHz Geode CPU)
  • 显示最后一天的情节将过滤掉1/30的数据 - 这将留下3 MB进行过滤。
  • 通过仅显示相关ID,将3 MB减少到1/1000(= 3 KB)。

附录#2:

这个问题基本上问我如何将2D数据集(时间和ID是维度)传输到内存中(并从那里将其序列化为文件)。而约束是两个维度都将被过滤。

建议的时间排序数组是处理时间维度的明显解决方案。 (为了提高查询性能,可能会使用基于树的索引。二进制搜索本身并不容易,因为每个条目可能具有不同的大小 - 但索引树很好地涵盖了它,并且基本上具有相同的基本思想。)

走这条路线(第一个维度(时间),然后是另一个)会导致ID过滤性能不佳(我担心),因为我必须使用暴力查找。

3 个答案:

答案 0 :(得分:3)

您可以将数据存储在SQLite中,让您的网络服务器针对它运行SQL查询。使用现有工具,您可以快速进行原型设计,并了解它的适用范围。

答案 1 :(得分:1)

  

效率最高(查询性能和   记忆消耗)

通过这个你可能意味着两者之间很平衡的东西。另外,我认为数据插入必须很快。

最简单且可能足够的解决方案是使用普通数组IMO,因为它是可以存储数据的大多数内存有效的非压缩形式。因此,每个数组元素都包含timestamp, id and value

使用两个时间戳beginend查询数据时,可以使用binary search确定数组中时间戳的位置。然后,遍历所有元素并仅获取您感兴趣的具有id-s数据源的元素。数组元素当然必须按时间戳排序。

  • 数据采用O(n)内存,其中日志条目的数量为n。
  • 数据插入是O(1)
  • 数据检索应该类似于O(2 * log(n)+ n * m),其中n是元素的数量。如果要在查询中包含更多数据源,则可以将数据源ID-s存储在集合中,因此复杂度为O(2 * log(n)+ n * log(m))。

当然还有其他可能涉及将事务存储在树,哈希表或将这些事件与列表混合在一起的事情,以在性能/内存消耗之间获得更详细的平衡。

此外,当日志量很大时,会出现问题。在这种情况下,您应该将数组拆分为文件并存储文件包含日志的开始/结束时间戳。然后实现变得有点复杂。

希望这有助于您确定解决方案的最佳数据结构/实现。

答案 2 :(得分:1)

这实际上取决于具体情况,但我认为可能的解决方案是将事件存储在页面中并将内容保存在页面目录中:

struct Page
{
    int id;
    int timestamp0, timestamp1;
    int bytes_used;
};

每个页面只有特定ID的事件,所有页面大小相同(例如4K)。 收到活动时,如果适合,请将其添加到特定页面,否则为该活动ID分配新页面。

在进行搜索时,您可以通过查看索引来快速决定数据文件中的哪些页面值得处理,而您不必处理整个文件。

用于添加事件的伪代码是:

  1. 找到ID x的最后一页
  2. 如果事件不适合页面分配新的新页面
  3. 存储事件并更新页面的索引条目
  4. 进行搜索:

    1. 对于索引中的每个条目
    2. 如果条目是关于您不关心的ID,那么请转到下一个
    3. 如果(page.timestamp0 >= tsmax || page.timestamp1 < tsmin),则该页面不包含任何有趣的事件,请转到下一个
    4. 此页面至少包含相关事件;加载页面并处理您感兴趣的句点tsmin ... tsmax中包含的事件。
    5. 如果每页添加一次ID字段,也可以避免将索引存储在文件中(使事件记录操作更快)。就在启动服务器时,需要对所有数据进行全面扫描......这可能是否是一个有趣的选择。

      您还可以轻松决定要保留多少数据......当没有更多免费页面可用时,您可以重复使用(可能是在存储压缩副本进行存档之后)所有仅包含特定日期之前的事件的页面。