我们要求每秒存储500次测量,来自多个设备.每个度量由时间戳,数量类型和几个向量值组成.现在每次测量有8个矢量值,我们可能会认为这个数字对于原型项目的需求是恒定的.我们正在使用HNibernate.测试是在SQLite(磁盘文件db,而不是内存)中完成的,但生产可能是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 IListVectors { 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服务器并停止假设,对吧?我会在测试后立即更新我的帖子.
[更新]
我已经转移到SQL服务器并使我的层次结构扁平化,我通过存储3000次测量/秒几个小时来测试它,它似乎工作正常.
就个人而言,我会说它:denormalize,然后创建一个ETL过程,将这些数据转换为更加规范化的格式,以便进行分析/定期使用.
基本上,理想的情况可能是拥有一个单独的数据库(或者甚至只需要在同一个数据库中使用单独的表),这些数据库将数据采集视为完全独立的事情,而不是以您需要处理的格式它.
这并不意味着你需要丢弃你在当前数据库结构周围创建的实体:只是你还应该创建那些非规范化表并创建一个ETL来引入它们.你可以使用SSIS(虽然它仍然是非常麻烦和烦躁)定期将数据带入规范化的表格集,甚至是C#app或其他批量加载过程.
编辑:当然,这是假设你的分析不需要实时完成:只是数据的集合.通常,人们不需要(有时甚至实际上不希望)实时更新分析数据.这是纸上听起来不错的事情之一,但在实践中它是不必要的.
如果分析这些数据的某些人需要实时访问,您可以根据需要构建一个针对"裸机"非规范化事务数据的工具集:但是当您真正深入了解需求时,执行分析的人员不需要真正实时(在某些情况下,他们更愿意使用更静态的数据集来处理!):在这种情况下,定期ETL可以很好地工作.您只需与目标用户聚在一起,找出他们真正需要的东西.