我们需要每秒存储来自多个设备的 500 个测量值。每个测量都包含一个时间戳、一个数量类型和几个矢量值。现在每次测量有 8 个矢量值,我们可以认为这个数字是常数,以满足我们原型(prototype)项目的需要。我们正在使用 HNibernate。测试是在 SQLite 中完成的(磁盘文件数据库,而不是内存中),但生产可能是 MsSQL。
我们的 Measurement 实体类是包含单个测量值的类,如下所示:
public class Measurement
{
public virtual Guid Id { get; private set; }
public virtual Device Device { get; private set; }
public virtual Timestamp Timestamp { get; private set; }
public virtual IList<VectorValue> Vectors { get; private set; }
}
向量值存储在单独的表中,因此它们中的每一个都通过外键引用其父测量值。
我们做了几件事来确保生成的 SQL 是(合理的)高效的:我们使用 Guid.Comb 生成 ID,我们在单个事务中刷新大约 500 个项目,ADO.Net 批处理大小设置为100(我觉得SQLIte不支持批量更新?不过以后可能会有用)。
问题
现在我们每秒可以插入 150-200 个测量值(这还不够快,尽管我们正在谈论的是 SQLite)。查看生成的 SQL,我们可以看到在单个事务中我们插入了(如预期的那样):
- 1个时间戳
- 1 次测量
- 8 个向量值
这意味着我们实际上执行了 10 倍以上的单表插入操作:每秒 1500-2000 次。
如果我们将所有内容(所有 8 个向量值和时间戳)放入测量表(添加 9 个专用列),似乎我们可以将插入速度提高 10 倍。
切换到 SQL 服务器将提高性能,但我们想知道是否有一种方法可以避免与当前数据库组织方式相关的不必要的性能成本。
[编辑]
使用内存中的 SQLite,我得到大约 350 个项目/秒(3500 个单表插入),我相信这与使用 NHibernate 时一样好(引用这篇文章:http://ayende.com/Blog/archive/2009/08/22/nhibernate-perf-tricks.aspx)。
但我也可以切换到 SQL Server 并停止假设,对吗?我会在测试后立即更新我的帖子。
[更新]
我已经转移到 SQL Server 并扁平化了我的层次结构,我通过存储 3000 个测量值/秒来测试它几个小时,它似乎工作正常。
最佳答案
就我个人而言,我会说去做:非规范化,然后创建一个 ETL 过程,将这些数据转换为更规范化的格式,以供分析/常规使用。
基本上,您的理想情况可能是拥有一个单独的数据库(或者如果需要,甚至只是同一数据库中的单独表)将数据的获取视为完全独立的事情,而不是将其以您所使用的格式需要对其进行处理。
这并不意味着您需要丢弃围绕当前数据库结构创建的实体:只是您还应该创建那些非规范化表并制作 ETL 以将它们引入。您可以使用 SSIS (尽管它仍然有很多错误和烦躁)定期将数据放入规范化的表集中,甚至是 C# 应用程序或其他批量加载过程。
编辑:当然,这是假设您的分析不需要实时完成:只需收集数据。很多时候,人们不需要(有时甚至不希望)实时更新分析数据。这是在纸面上听起来不错的事情之一,但在实践中却没有必要。
如果分析此数据的某些人需要实时访问,您可以根据需要针对“裸机”非规范化事务数据构建一个工具集:但通常当您真正深入挖掘需求时,执行分析的人员并不需要真正的实时(在某些情况下,他们更愿意使用一组更静态的数据!):在这种情况下,周期性 ETL 会工作得很好。您只需要与您的目标用户聚在一起,找出他们真正需要的东西。
关于.net - 我们应该对数据库进行反规范化以提高性能吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2757590/