我有一项任务是在数据库中存储大量gps数据和一些额外信息,并访问它以进行报告和其他一些非常见的任务。
当我收到来自gps设备的消息时,它可以包含可变数量的字段。例如
消息1:DeviceId Lat Lon Speed课程DIO1 ADC1
消息2:DeviceId Lat课程DIO2 IsAlarmOn
消息3:DeviceId Lat Lon高度课程DIO2 IsAlarmOn等最多20-30个字段
没有办法统一字段数量 - 不同的设备供应商,不同的协议等。 另一个令人头痛的问题是数据库的大小和必要性以支持尽可能多的数据库供应商(使用NHibernate)。
所以我开始想以这样的方式存储消息:
表1 - 曲目
PK - TrackId
TrackStartTime
TrackEndTime
FirstMessageIndex(存储MessageId)
LastMessageIndex(存储MessageId)
DeviceId(不是FK)
表2 - 消息
PK - MessageId
时间戳
FirstDataIndex(存储DataId)
LastDataIndex(存储DataId)
表3 - MessageData
PK - DataId
双数据
短数据类型
所有索引都是hilo的assignet。调整我的查询,以便Nhibernate可以快速处理3000+ k消息(也使用了baching)。 我很满意性能atm。但我不知道它将如何在50 + gb或100 + gb大小下工作。
非常感谢关于我的问题和存储设计的任何提示和提示整体=)
谢谢,阿列克谢
PS.Sorry for my english =)
答案 0 :(得分:3)
简而言之,您的应用程序,特别是从GPS设备收到的消息的异构结构,将您的设计推向一个EAV数据存储结构(其中实体是消息,属性是“MessageData.DataType”,值系统地是双倍。)
这三个表设计了你在问题中的概述,但是似乎与传统的EAV实现略有不同,从某种意义上说,存在MessageData存储方式的隐式序列,所有这些给定消息的数据点按顺序编号(DataId),消息到其数据点的链接将由范围内的DataId驱动。
这是一个坏主意!
许多问题,一个值得注意的问题是,这为插入消息带来了不必要的瓶颈,无法开始插入第二条消息,直到前一条消息的所有数据点都出现。
另一个问题是它使得消息和数据点之间的关系难以索引(底层DBMS在它上面效率不高)。
==>建议:使 MessageId成为MessageData 表中的外键。 (并且可能完全删除MessageData表中的DataId PK,只是为了节省空间,代价是必须使用复合键来引用此表中的特定记录,例如用于维护目的)
另一个建议是将最常见的属性(数据点)存储在消息表的级别。例如,Lat和Long,但也可能是课程或一些警报等。将此信息与消息一起使用的原因是优化对数据的查询(限制MessageData表所需的自连接数。
由于Messages和MessageData表都可能不包含部分消息,因此您可能还想重命名后一个MessageDetail表或某个此类名称。
最后,允许除双重类型之外的数据值可能是个好主意。我预计一些警报只是布尔值等。除了允许您接受不同类型的数据点(比如简短的错误消息字符串......)之外,这还可以让您有机会在多个“详细信息”表中拆分数据点:一个对于双打,一个用于布尔,一个用于字符串等。这种方式使得模式在某种意义上复杂化,然后您需要将这些细节构建为查询的生成方式,但它可以提供一些性能/扩展的潜力增益。
答案 1 :(得分:0)
我试着在回答中详细描述它是如何工作的,因为注释具有固定的长度=)
这是接收顺序:
1.服务接收来自MSMQ的消息(消息数量可以不同,它使用500个消息批量数据包)
2.然后细化不同的设备ID
3.对于每个设备ID,它使用具有结构的MS EntLib隔离存储缓存:
DeviceId - >列出DeviceId是查找键的位置
4.如果我们在缓存中有超过1k条消息 - 在一个序列中将它们写入db并在将“index”写入查找表之后:
指数:
ID
serial_id
index_start_datetime
index_end_datetime
index_first_dataid
index_last_dataid
5.清除此DeviceId的缓存
我还将数据存储在情侣中:
id data1 data2 type
例如lat lon,speed course,adc1 adc2,dio1,dio2
如果没有耦合值:值0
我选择double,因为我可以存储其中发送的每种类型的数据设备。 不发送字符串,但大多数em是csv样式,如1,0,23,50.0000N30.00000,1,2,12,0,1,2等。甚至报警等具有相同类型的数据。 当我需要获取一些数据时,我只需找到给定日期时间窗口和DeviceId的索引,并获知实际数据何时开始和结束。并且没有复杂的查询。只有2个简单的。其他代码使用一些协议“映射”来解释它。 感谢EAV提示。我觉得它很合适。第一个表跟踪是用于聚合消息并快速获取em的后续算法,我之前描述过几个字符串。
答案 2 :(得分:0)
我正在写类似的应用程序。我建议从供应商处识别所有可能的值,并使用所有必填字段创建正确的模式。多亏了这一点,您可以编写高性能/最简单的报告查询。
此外,您可以创建包含指定(长度)数据的字段,这意味着您可以保存位置并提高性能。
我有一个具有已知值的供应商,所以我为此创建了一个表。 该表可以通过本机MS SQL Server机制轻松分区。
因此,我最简单的情况允许我编写一个存储过程来保存数据。没有NHibernate,只有纯粹的ICommand。
其余应用程序使用NHibernate。