我正在寻找满足以下大多数要求的理想数据库结构/解决方案,但到目前为止我完全失败了。你能帮忙吗?
我的任务:我在.Net 4.5(C#)中运行一个进程,并生成(通常)我希望在其他应用程序中进行进一步分析的值类型,因此要么保留内存,要么保留在磁盘上。更多下面。数据在不同的任务/线程内生成,因此基于行的数据格式不能很好地匹配这种情况(因为在不同的线程中生成的数据是在不同的时间生成的,因此不对齐)。因此我认为柱状数据结构可能是合适的,但如果我错了,请纠正我。
示例:
任务/线程#1在给定时间戳生成以下数据
datetime.ticks /输出数据的值
1000000001 233.23
1000000002 233.34
1000000006 234.23 ...
Taks / Thread#2在给定时间戳记下生成以下数据
datetime.ticks /输出数据的值
1000000002 33.32
1000000005 34.34
1000000015 54.32 ...
我不需要在.Net运行时对齐时间戳,我首先保留数据并在稍后处理R或Python中的数据。
我的要求:
快速写入,快速写入,快速写入:可能发生的情况是,我每秒生成100,000到1,000,000个数据点,需要保持(最坏情况)或保留在内存中的数据。可以在自己的线程上运行写操作,因此这个过程可能会滞后于数据生成过程,但限制是16gb RAM(64位代码),更低于。
首选是列式数据库格式,因为它非常适合我以后想要查询数据的方式,但如果它对上面的示例有意义,我可以接受任何其他结构(文档/键值)如果满足所有其他要求,也可以,特别是在写入速度方面)。
可以从.Net中引用的API。示例:HDF5可能被一些人认为是有能力的,但我发现他们的.Net端口很糟糕。支持.Net好一点的东西会有好处但是如果满足所有其他要求那么我可以处理类似于HDF5 .Net端口的东西。
如果可能的话,并发写:如前所述,我喜欢从不同的任务/线程中同时写入数据。
我受16gb内存的约束(在64位运行.Net进程),因此我可能会寻找一些不纯粹在内存中的东西,因为我有时会生成更多的数据。内存中有时会持续存在或纯粹的持久性模型可能更可取。
嵌入式的首选项,但如果客户端/服务器解决方案中的服务器可以作为Windows服务运行,则没有问题。
在数据访问方面,我非常喜欢已经存在R和Python接口的数据库解决方案,因为我喜欢在Python中使用Panda库进行时间序列对齐和其他分析并在R中运行分析
如果API /库支持SQL / SQL-like / Linq / like查询,那将是非常好的但通常我只需要绝对的简单骨骼,例如在开始和结束日期之间加载柱状数据(给定“key”/ index就是这种格式),因为我在R / Python中分析和运行查询。
如果它带有管理控制台或数据可视化工具,那将是一个加号但不是必须的。
应该是开源的或者在“达到”范围内定价(不,KDB不符合这方面的条件; - )
好的,这是我到目前为止所拥有的,而且我得到的全部是因为大多数数据库解决方案在写入性能要求上已经失败了:
答案 0 :(得分:13)
我不能评论 - 低代表(我是新来的) - 所以你得到一个完整的答案......
首先,您确定需要一个数据库吗?如果快速写入速度和R的可移植性是您最关心的问题,那么您是否考虑过平面文件机制?根据你的评论,你愿意批量写出,但你需要坚持;如果这些是我的要求我会编写一个快速闪存的直接缓冲系统然后构建一个单独的任务,定期获取磁盘文件并将它们移动到R的数据存储中,这只有在R读取平面时文件还不够。
如果您可以事后进行对齐,那么您可以将线程编写为主并行循环中的单独文件,每隔一段时间关闭一个文件,并将对齐和数据库加载到子进程。
所以(在蹩脚的伪代码中),构建一个你用后台工作者调用的线程进程,并包含一个唯一标识每个worker的线程名字符串,从而识别每个文件流(任务/线程):
file_name = threadname + '0001.csv' // or something
open(file_name for writing)
while(generating_data) {
generate_data()
while (buffer_not_full and very_busy) {
write_data_to_buffer
generate_data()
}
flush_buffer_to_disk(file_name)
if(file is big enough or enough time has passed or we're not too busy) {
close(file_name)
move(file_name to bob's folder)
increment file_name
open(file_name for writing)
}
)
高效且快速的文件I / O和缓冲为a straightforward and common problem。没有比这更快的了。然后你可以编写另一个进程来完成数据库加载,而不是在那里表现出色:
while(file_name in list of files in bob's folder sorted by date for good measure)
{
read bob's file
load bob's file to database
align dates, make pretty
}
我不会在C#中编写该部分,我会批量编写脚本并使用数据库的本机加载器,这将与您从头开始构建的任何内容一样快。
如果你在同一个硬件上运行,你必须确保这两个循环不会干扰太多。也就是说,以更高的优先级运行任务线程,或者构建一些互斥或性能限制器,以便在线程运行时数据库负载不会占用资源。我肯定会将数据库服务器和硬件隔离开来,这样平面文件的文件I / O就不会受到损害。
如果你在Unix上,FIFO队列就可以工作,但你不是。 : - )
另外,我想,硬件对数据库引擎的性能影响会更大。如果你的预算有限,我猜你是在使用COTS硬件,那么弹出固态驱动器可能会相当便宜地提高性能。正如我所说的那样,将数据库存储与平面文件存储分开会有所帮助,R,数据库和线程的CPU / RAM都应该理想地隔离。
我所说的是,DB供应商的选择可能不是您最大的问题,除非您有很多钱可花。否则你将在大多数情况下受到硬件限制。数据库调优是一门艺术,虽然您可以在高端获得较小的性能提升,但拥有一名优秀的数据库管理员将使大多数数据库保持同一性能。我会看看R和Python支持的内容以及您对此感到满意。如果你以柱状方式思考,那么看看R和C#对Cassandra(我的投票),Hana,Lucid,HBase,Infobright,Vertica和其他人的支持,并根据价格和支持选择一个。对于单个商品机器上的传统数据库,我还没有看到MySQL无法处理的任何内容。
答案 1 :(得分:2)
这不是为了回答我自己的问题,而是为了跟踪我到目前为止测试的所有数据库以及为什么他们还没有达到我的要求(还有):每次我尝试编写100万个单个对象(1个长, 2浮动)到数据库。对于ooDB,我将对象粘贴到集合中并编写集合本身,类似于关键/值的故事,如Redis,但也尝试将简单的整数(1mil)写入柱状dbs(如InfoBright)。
更新:我放弃测试velocityDB只是因为似乎没有任何适配器可以从R内部访问数据库。我目前正在考虑使用图表库编写自己的GUI,该库可以从写入的二进制文件访问生成的数据,也可以通过无代理消息总线(zeroMQ)发送或通过LockFree ++发送给"演员" (我的gui)。然后我可以在C#中调用R并将结果返回到我的GUI。这可能会让我获得最大的灵活性和自由度,但显然也是编码最乏味的。我在测试期间遇到越来越多的限制,每次数据库测试我越来越多地与这个想法交往。
结果:感谢您的参与。最后,我向Chipmonkey颁发了赏金点,因为他部分地提出了我认为解决问题的重要点(尽管我最终选择了自己的,不同的解决方案)。 我最终得到了Redis在内存存储和从.Net到R.dll的直接调用之间的混合。 Redis允许通过不同的进程访问存储在内存中的数据。这使得它成为一种方便的解决方案,可以快速将数据作为键/值存储在Redis中,然后从R中访问相同的数据。此外,我通过其.dll和优秀的R.Net库直接发送数据并调用R中的函数。将一百万个值类型的集合传递给R在我的机器上花费大约2.3秒,这足够快,因为我可以方便地传入数据,在.Net环境中调用R内的计算功能并获得结果同步或异步。
答案 2 :(得分:0)
请注意:我曾经在delphi论坛上发过类似的问题。我可以用他当时写的一个简单的ID-key-value数据库后端(一种NoSQL引擎)来帮助他。基本上,它使用B树来存储三元组(32位ObjectID,32位PropertyKey,64位Value)。我可以设法实时保存大约500k / sec的值(大约5年前)。当然,数据是在所有三个值(ID,属性ID和值)上编制索引的。您可以通过忽略值索引来优化它。
我仍然拥有的源代码是Delphi,但我会考虑使用C#实现类似的东西。我无法告诉你它是否能满足你的表现需求,但如果一切都失败了,试一试。使用缓冲写入也应该可以显着提高性能。
答案 3 :(得分:0)
我会采用结合持久性存储的方式(我个人更喜欢db4o,但你可以使用上面提到的文件)并以这种方式将对象存储到内存中:
使用BlockingCollection< T>将对象存储在内存中(我相信你将获得更好的性能,然后1000000 / s来将对象存储在内存中),并且拥有一个或多个处理线程来消耗对象并将它们存储到持久数据库中
// Producing thread
for (int i=0; i<1000000; i++)
blockingCollection.Add(myObject);
// Consuming threads
while (true)
{
var myObject = blockingCollection.Take();
db4oSession.Store(myObject); // or write it to the files or whathever
}
BlockingCollection几乎解决了Producer-Consumer工作流程,如果您将使用它们的多个实例并使用AddToAny / TakeFromAny,您可以达到任何类型的多线程性能
每个消费线程可以使用不同的db4o会话(文件)来达到所需的性能(db4o是单线程的)。
答案 4 :(得分:-1)
既然你想使用ZeroMQ,为什么不在Redis上使用memcache?
据我所知,ZeroMQ没有提供持久性。 Memcache也没有持久性,并且比Redis快一点
或者另一种方式,如果你使用Redis为什么不使用beanstalk MQ?
如果要使用Redis(用于持久性),您可能希望从ZeroMQ切换到beanstalk MQ(也是快速的内存队列,但也通过日志记录具有持久性)。 Beanstalk也有C#libs。