需要数据库建议 - >柱状,嵌入式(如果可能)

时间:2012-11-05 01:19:32

标签: c# sql database api concurrency

编辑:作为答案的结果到目前为止,我喜欢在我喜欢的内容中添加更多关注点:允许在内存中编写的数据库(可能是简单的C#代码),并且存储选项具有持久性,以便访问R. Redis内部的数据到目前为止看起来最有希望。我还考虑实际使用类似于Lockfree ++或ZeroMQ的东西,以避免将数据同时写入数据库,而是通过消息总线/其他实现将所有数据发送到持久化数据,并让一个“actor”处理所有写操作内存数据库或其他解决方案。除了Redis之外还有更多的想法(有些人提到了SQLite,我还需要测试它的性能)。还有其他建议吗?

我正在寻找满足以下大多数要求的理想数据库结构/解决方案,但到目前为止我完全失败了。你能帮忙吗?

我的任务:我在.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不符合这方面的条件; - )

好的,这是我到目前为止所拥有的,而且我得到的全部是因为大多数数据库解决方案在写入性能要求上已经失败了:

  • Infobright和Db4o。我喜欢我到目前为止阅读的内容,但我承认我没有检查任何性能统计数据
  • 自己完成的事情。我可以轻松地以二进制格式存储值类型并通过datetime.ticks索引数据,我只需要以某种方式编写脚本来加载/反序列化Python / R中的数据。但如果我想添加并发,查询引擎和其他好东西,那将是一项艰巨的任务。因此,我寻找已经存在的东西。

5 个答案:

答案 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)。

  • Db4o,写得非常慢:集合中的1mil对象大约需要45秒。后来我对收集结构进行了优化,并且还单独编写了每个对象,这里没有多少爱。
  • InfoBright:同样的事情,在写入速度方面非常慢,这让我感到非常惊讶,因为它以柱状格式组织数据,但我认为"知识树"只有在查询数据时才会启动,而不是在保存平面数据结构/表格式结构时。
  • Redis(通过BookSleeve):.Net的优秀API:完全Redis功能(虽然在Windows机器上运行服务器与Linux或Unix机箱相比存在一些缺点)。性能非常快......超过每秒100万件物品。我使用Protocol Buffers(protobuf-net,都是由Marc Gravell编写)序列化所有对象,仍然需要在库中玩更多,但R和Python都可以完全访问Redis DB,这是一个很大的优点。喜欢它到目前为止。 Marc围绕Redis基本功能编写的Async框架非常棒,非常简洁,到目前为止还可以使用。我想花更多的时间来尝试Redis Lists / Collection类型,因为到目前为止我只是序列化为字节数组。
  • SqLite:我纯粹在内存中运行,并设法在大约3秒内写出100万个值类型元素。纯粹的RDBMS并不坏,显然内存中的选项确实可以加快速度。我只创建了一个连接,一个事务,创建了一个命令,一个参数,只是在循环中调整了参数的值,并在每次迭代时运行了ExecuteNonQuery。然后,事务提交在循环外运行。
  • HDF5:虽然有一个.Net端口,并且还有一个库以某种方式处理R中的HDF5文件,但我强烈反对任何人这样做。这是一场纯粹的噩梦。 .Net端口编写得非常糟糕,哎呀,整个HDF5概念都值得怀疑。它是一个非常古老的,在我看来已经过时的解决方案来存储矢量化/柱状数据。这是2012年而不是1995年。如果不能完全删除存储它们的文件中的数据集和向量,那么我不会称之为烦恼,而是一个主要的设计缺陷。一般的API(不仅仅是.Net)设计非常糟糕,并且写得很好,有大量的类对象没有人在没有花费数小时研究文件结构的情况下理解如何使用。我认为这可以通过非常少量的文档和示例代码来证明。此外,h5r R库是一部戏剧,绝对是一场噩梦。它的编写也很糟糕(通常写入文件由于错误的刷新而没有正确关闭并且它会破坏文件),库甚至可以在32位操作系统上正确安装......并且它会继续运行。我写了最多关于HDF5的内容,因为我把大部分时间花在了这篇文章上......结果最令人沮丧。从R和.Net可以访问的快速柱状文件存储系统的想法是诱人的,但它只是没有提供它在API集成和可用性或缺乏的承诺方面。

更新:我放弃测试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。