高性能开发

10

背景

我们一直在努力寻找“高性能”应用的解决方案。该应用程序基本上是一个高吞吐量的内存管理器,可以同步回写到磁盘。其“读取”和“写入”速度非常快,大约每秒处理3000个事务。我们尽可能地使用内存来处理数据,但最终数据会过时并需要刷新到磁盘,这就导致了一个巨大的“瓶颈”。该应用程序是多线程的,有约50个线程。没有IPC(进程间通信)。

尝试

我们最初用Java编写了这个应用程序,在某个负载下它运行得很好,但达到瓶颈后就无法跟上速度了。 然后我们尝试使用C#,但遇到了相同的瓶颈。 我们尝试使用未托管的代码(C#),尽管在初始测试中使用MMF(内存映射文件)非常快,但在生产中,读取却很慢(使用Views)。 我们尝试过CouchBase,但我们遇到了高网络利用率的问题。这可能是我们配置不良!

额外信息:在我们的Java尝试中(非MMF),我们的带有需要刷新到磁盘的信息队列的线程达到了无法跟上“写入”到磁盘的程度。 在我们的C#内存映射文件方法中,问题在于读取非常慢,而写入却完美工作。由于某种原因,Views很慢!

问题

所以问题是,当您打算传输大量数据时,是否可以有人提供可能有助于解决问题的方法或架构设计?我知道这似乎有点宽泛,但我认为高性能、高吞吐量的特定性质应该可以缩小答案范围。

有人可以证明在这个级别上使用Couchbase、MongoDB或Cassandra吗?其他的想法或解决方案也将不胜感激。


你们是如何在磁盘之间进行数据分片的?此外,我个人无法保证Mongo在如此高的负载下的表现,但他们似乎非常注重速度,并提供了许多设施来尝试在几个服务器/磁盘之间分配读/写负载。 - Keith Ripley
我们之前使用Couchbase来实现这个功能。最初的设计是单个盒子,其中有多个线程;其中一个线程有一个需要刷新到磁盘的数据队列。目前我们正在尝试使用内存映射文件(C#),并且“写入”没有问题,因为它由操作系统管理,但是存在读取问题。就是无法解决 :) - Dane Balia
你多久读一次?你不能使用一个FIFO队列吗,其中一个线程不断地读取,以确保X个对象始终可供处理线程使用吗? - jgauffin
我对高网络利用率感到困惑。我对Couchbase不太熟悉,但在Mongo中,客户端会连接到(可能是本地的)mongos进程,该mongos进程然后会连接到正确的片段并检索结果。对于在主索引上查询单个对象,您只需命中一个服务器,您可能需要一些请求和序列化以及偶尔的配置更新开销,但我不明白这怎么可能会突然使网络不能使用... - Keith Ripley
Keith,请看一下这个链接,https://raw.github.com/dspezia/redis-doc/client_command/topics/Data_size.png。对于我们来说,这种性能表现是不可接受的,而这还是REDIS。为了满足我们的需求,我们需要5000台机器。我想没有简单的解决方案或技巧;隧道正在变窄 :( - Dane Balia
显示剩余4条评论
3个回答

3

首先,我想明确一点,我几乎没有任何建立高性能、可扩展应用程序的经验。

Martin Fowler对LMAX架构进行了描述,该架构允许单个线程处理约600万个订单每秒。我不确定它是否能帮助您(因为您似乎需要移动大量数据),但也许您可以从中获取一些想法:http://martinfowler.com/articles/lmax.html

该架构基于事件源,通常用于提供(相对)容易的可扩展性。


看起来很有前途,需要一点时间来理解“概念”。但是我们会尝试从Disruptor模式中获得一些东西。 - Dane Balia
是的,这并不能真正解决我们的问题。Disruptor模式更关注于当有大量工作(工作步骤)需要完成时,以及工作之间的争用(例如,在队列中发现的争用)。我们的问题在于一个独立的队列无法高效地写入磁盘,而不会使队列变得难以管理。 - Dane Balia

2
大量数据和磁盘访问。我们所说的磁盘是什么类型?如果您使用多个文件,HDD往往会花费大量时间移动磁头。(如果使用SSD,则不应该成为问题。) 此外,您应该利用内存映射文件以页面大小的块管理的事实。数据结构应尽可能对齐页面边界。
但无论如何,您必须确保知道瓶颈在哪里。例如,优化数据结构实际上无法帮助您减少线程同步带来的时间损失。如果您使用HDD,则页面对齐可能并不能像将所有内容都塞进一个文件中那样有用。因此,请使用适当的工具找出仍然阻止您前进的问题。
使用通用数据库实现可能无法如您所愿地帮助您。毕竟,它们是通用的。如果性能真的是一个问题,专门针对您要求的实现可能会比这些更通用的实现表现更好。

这样一个针对Java的分析工具,例如JProfiler - Marc-Christian Schulze
@DaneBalia:你可能想要分享一下你在分析中发现的内容。 - jgauffin
当Wormbo说“确保你知道瓶颈在哪里”时,他说得很到位。根据我们的分析,瓶颈首先表现为“JVM不释放内存”。进一步的探究发现是队列没有释放对象,这直接指向了磁盘无法跟上I/O速度的问题。 - Dane Balia
感谢大家的帮助和评论。我们打算尝试一下 Redis,因为它似乎是最适合我们需求的,如果它不行的话,我们会编写自己的实现。以下链接可能对其他人有帮助:http://kkovacs.eu/cassandra-vs-mongodb-vs-couchdb-vs-redis/ - Dane Balia
如果确实是I/O部分的问题,那么你可能需要深入了解文件系统和内存映射文件的工作原理,才能够改进任何东西。由于SSD对您来说不是一种选项,因此更重要的是将所有相关数据紧密地组合在物理磁盘上。这应该有助于磁盘自身缓存和Windows文件缓存预测所需数据。此外,我所指的“页面对齐”数据是指内存映射文件按固定大小的块和固定边界读取。以4k的页面大小为例,从3k开始读取2k的数据,您将需要两个页面而不是一个。 - Wormbo
显示剩余2条评论

-1

如果你想要快速的话,在写入时尽可能避免使用持久性和队列,而在读取时使用内存存储/缓存。

语言与此无关。


不确定为什么会被踩..编程语言通常会有10-30%的差异(有些甚至达到50%)。但是IO到磁盘上比内存慢大约10K倍。看看Lmax,最小化IO,在单台机器上每秒执行6M个事务。同样适用于常见用法,使用持久队列,我保证您的吞吐量至少会降低10倍。而且你还需要手动维护死信队列。现在看看与持久成本相比的语言数据..这并不意味着你没有持久性,但最小化它可以帮助提高性能.. - user1496062

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接