9

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

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

我的任务:我在 .Net 4.5 (C#) 中运行一个进程并生成(通常)我想在其他应用程序中用于进一步分析的值类型,因此喜欢保留在内存中或保留在磁盘上。更多内容如下。数据是在不同的任务/线程中生成的,因此基于行的数据格式不能很好地匹配这种情况(因为在不同的线程中生成的数据是在不同的时间生成的,因此没有对齐)。因此,我认为列式数据结构可能是合适的,但如果我错了,请纠正我。

例子:

Tasks/Thread #1 在给定的时间戳生成以下数据

datetime.ticks / 输出数据的值

1000000001 233.23

1000000002 233.34

1000000006 234.23 ...

任务/线程#2 在给定的时间戳生成以下数据

datetime.ticks / 输出数据的值

1000000002 33.32

1000000005 34.34

1000000015 54.32 ...

我不需要在 .Net 运行时对齐时间戳,我首先是在保存数据并稍后在 R 或 Python 中处理数据之后。

我的要求:

  • 快速写入、快速写入、快速写入:可能会发生我每秒生成 100,000-1,000,000 个数据点并且需要持久化(最坏情况)或将数据保留在内存中的情况。可以在自己的线程上运行写入,因此该过程可以滞后于数据生成过程,但限制是 16gb RAM(64 位代码),更多信息如下。

  • 首选是柱状 db 格式,因为它很适合我以后如何查询数据,但如果对上面的示例有意义,我对任何其他结构持开放态度(如果所有其他要求,文档/键值也可以满足,尤其是在写入速度方面)。

  • 可以从 .Net 中引用的 API。示例:HDF5 可能被某些人认为是有能力的,但我发现他们的 .Net 端口很糟糕。支持 .Net 的东西会更好一点,但如果满足所有其他要求,那么我可以处理类似于 HDF5 .Net 端口的东西.

  • 尽可能并发写入:如前所述,我喜欢从不同的任务/线程并发写入数据。

  • 我受到 16gb 内存的限制(以 64 位运行 .Net 进程),因此我可能会寻找并非纯粹在内存中的东西,因为有时我可能会生成比这更多的数据。有时会持续存在的内存中的东西或纯粹的持久性模型可能更可取。

  • 首选嵌入式,但如果客户端/服务器解决方案中的服务器可以作为 Windows 服务运行,则没有问题。

  • 在数据访问方面,我非常喜欢已经存在 R 和 Python 接口的数据库解决方案,因为我喜欢在 Python 中使用 Panda 库进行时间序列对齐和其他分析,并在 R 中运行分析。

  • 如果 API/库还支持 SQL/SQL/Linq/ 之类的查询,这将是非常棒的,但通常我只需要绝对的基本框架,例如在开始日期和结束日期之间加载列数据(给定“键”/索引是这种格式),因为我在 R/Python 中分析和运行查询。

  • 如果它带有管理控制台或数据可视化器,那将是一个加分项,但不是必须的。

  • 应该是开源的或在“范围内”定价(不,KDB 在这方面不符合条件;-)

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

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

5 回答 5

13

我无法评论——低代表(我是新来的)——所以你会得到一个完整的答案......

首先,你确定你需要一个数据库吗?如果快速写入速度和对 R 的可移植性是您最关心的问题,那么您是否考虑过平面文件机制?根据您的意见,您愿意批量写入,但需要持久性;如果这些是我的要求,我会编写一个闪电般快速的直接到磁盘缓冲系统,然后构建一个单独的任务,定期获取磁盘文件并将它们移动到 R 的数据存储中,并且只有当 R 读取平面文件首先是不够的。

如果您可以事后进行对齐,那么您可以编写线程以在主并行循环中分隔文件,每隔一段时间切断每个文件,并将对齐和数据库加载留给子进程。

因此(在蹩脚的伪代码中),构建一个线程进程,您将使用 backgroundworker 或类似的方法调用它,并包含一个唯一标识每个工作人员以及每个文件流(任务/线程)的线程名字符串:

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 和缓冲是一个简单而常见的问题。没有什么比这更快了。然后,您可以编写另一个进程来执行数据库加载,而不用担心那里的性能:

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 硬件,因此选择固态硬盘可能会相当便宜地提高性能。正如我所说,将 DB 存储与平面文件存储分开会有所帮助,并且 R、数据库和线程的 CPU/RAM 都应该理想地隔离。

我的意思是选择数据库供应商可能不是你最大的问题,除非你有很多钱可以花。否则,您大部分时间都会受到硬件的约束。数据库调优是一门艺术,虽然您可以在最高端获得较小的性能提升,但拥有一个优秀的数据库管理员将使大多数数据库的性能保持在同一个范围内。我会看看 R 和 Python 支持什么以及你对什么感到满意。如果您以柱状方式思考,请查看 R 和 C# 对 Cassandra(我的投票)、Hana、Lucid、HBase、Infobright、Vertica 等的支持,然后根据价格和支持选择一个。对于单一商品机器上的传统数据库,我还没有看到任何 MySQL 无法处理的东西。

于 2012-11-08T16:01:38.917 回答
2

这不是要回答我自己的问题,而是要跟踪我迄今为止测试过的所有数据库以及为什么它们还没有满足我的要求:每次我尝试编写 100 万个单个对象(1 个长对象,2 个浮点数)到数据库。对于 ooDB,我将对象放入一个集合并编写集合本身,类似于 Redis 等键/值的故事,但也尝试将简单的整数(1 百万)写入 InfoBright 等柱状数据库。

  • Db4o,写入速度非常慢:一个集合中的 100 万个对象大约需要 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 列表/集合类型,因为到目前为止我只序列化为字节数组。
  • SqLite:我纯粹在内存中运行,并设法在大约 3 秒内写入 100 万个值类型元素。对于纯 RDBMS 来说还不错,显然内存选项确实加快了速度。我只创建了一个连接、一个事务、创建了一个命令、一个参数,并且只是在一个循环中调整了参数的值,并在每次迭代时运行 ExecuteNonQuery。然后事务提交在循环外运行。
  • HDF5:虽然有一个 .Net 端口,并且还有一个库可以以某种方式处理 R 中的 HDF5 文件,但我强烈反对任何人这样做。这是一场纯粹的噩梦。.Net 端口写得非常糟糕,哎呀,整个 HDF5 概念都值得怀疑。它是一种非常古老且在我看来已经过时的存储矢量化/列数据的解决方案。这是 2012 年而不是 1995 年。如果不能从之前存储它们的文件中完全删除数据集和向量,那么我不认为这是一种烦恼,而是一个主要的设计缺陷。一般来说,API(不仅仅是.Net)的设计和编写都非常糟糕,恕我直言,有大量的类对象,没有人在没有花费数小时研究文件结构的情况下,了解如何使用。我认为这在一定程度上可以从非常稀少的文档和示例代码中得到证明。此外,h5r R 库是一场戏剧,绝对是一场噩梦。它的编写也很糟糕(通常由于刷新错误导致文件无法正确关闭并且它会损坏文件),该库甚至无法正确安装在 32 位操作系统上......并且它继续存在。我写的关于 HDF5 的文章最多,因为我把大部分时间都花在了这篇文章上……最后得到了最大的挫败感。拥有一个可从 R 和 .Net 访问的快速列式文件存储系统的想法很诱人,但它并没有实现它在 API 集成和可用性或缺乏可用性方面的承诺。它的编写也很糟糕(通常由于刷新错误导致文件无法正确关闭并且它会损坏文件),该库甚至无法正确安装在 32 位操作系统上......并且它继续存在。我写的关于 HDF5 的文章最多,因为我把大部分时间都花在了这篇文章上……最后得到了最大的挫败感。拥有一个可从 R 和 .Net 访问的快速列式文件存储系统的想法很诱人,但它并没有实现它在 API 集成和可用性或缺乏可用性方面的承诺。它的编写也很糟糕(通常由于刷新错误导致文件无法正确关闭并且它会损坏文件),该库甚至无法正确安装在 32 位操作系统上......并且它继续存在。我写的关于 HDF5 的文章最多,因为我把大部分时间都花在了这篇文章上……最后得到了最大的挫败感。拥有一个可从 R 和 .Net 访问的快速列式文件存储系统的想法很诱人,但它并没有实现它在 API 集成和可用性或缺乏可用性方面的承诺。

更新:我放弃了测试velocityDB只是因为似乎没有任何适配器可以从R中访问数据库。我目前正在考虑用图表库编写我自己的 GUI,它可以从写入的二进制文件访问生成的数据,或者通过无代理消息总线 (zeroMQ) 发送或通过 LockFree++ 发送到“参与者”(我的 gui)。然后我可以从 C# 中调用 R 并将结果返回到我的 GUI。这可能会给我最大的灵活性和自由,但显然也是最乏味的编码。在我的测试过程中,我遇到了越来越多的限制,每次 db 测试我都越来越喜欢这个想法。

结果:感谢您的参与。最后,我将赏金积分授予 Chipmonkey,因为他部分建议了我认为解决我的问题的重要点(尽管我最终选择了我自己的不同解决方案)。我最终在内存存储中的 Redis 和从 .Net 到 R.dll 的直接调用之间进行了混合。Redis 允许不同进程访问其存储在内存中的数据。这使得它成为一种方便的解决方案,可以将数据快速存储为 Redis 中的键/值,然后从 R 中访问相同的数据。此外,我通过其 .dll 和出色的 R.Net 库直接发送数据并调用 R 中的函数。在我的机器上将 100 万个值类型的集合传递给 R 大约需要 2.3 秒,这已经足够快了,因为我可以方便地传递数据,

于 2012-11-09T06:58:05.307 回答
0

请注意:我曾经在一个德尔福论坛上遇到过类似的问题。我可以帮助他使用我当时编写的一个简单的 ID-key-value 数据库后端(一种 NoSQL 引擎)。基本上,它使用 B-Tree 来存储三元组(32 位 ObjectID、32 位 PropertyKey、64 位值)。我可以设法实时保存大约 500k/秒的值(大约 5 年前)。当然,数据在所有三个值(ID、property-ID 和 value)上都建立了索引。您可以通过忽略值索引来优化它。

我仍然拥有的源代码在 Delphi 中,但我会考虑使用 C# 实现类似的东西。我不能告诉你它是否能满足你的性能需求,但如果一切都失败了,试试看。使用缓冲写入也应该大大提高性能。

于 2012-11-10T18:20:04.313 回答
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 几乎解决了生产者-消费者工作流程,如果您将使用它们的多个实例并使用 AddToAny/TakeFromAny,您可以获得任何类型的多线程性能

每个消费线程可以有不同的 db4o 会话(文件)以达到所需的性能(db4o 是单线程的)。

于 2012-11-10T22:21:30.903 回答
-1

既然你想使用 ZeroMQ 为什么不使用 Memcache 而不是 Redis 呢?
据我所知,ZeroMQ 不提供持久性。Memcache 也不提供持久性,并且比 Redis 快一点。
或者换一种方式,如果你使用 Redis 为什么不使用 beanstalk MQ?
如果您想使用 Redis(用于持久性),您可能希望从 ZeroMQ 切换到 beanstalk MQ(也是一个快速的内存队列,但也具有通过日志记录的持久性)。Beanstalk 也有 C# 库。

于 2012-11-14T17:27:20.413 回答