CouchDB推荐文档结构

7
我们正在考虑将一个使用监控应用程序从Postgres更改为CouchDB。一些数字如下:
大约有2000个连接,每5分钟轮询一次,每天大约有60万行新数据。在Postgres中,我们将这些数据按天存储:
t_usage {service_id, timestamp, data_in, data_out} t_usage_20100101继承自t_usage。 t_usage_20100102继承自t_usage。等等。
我们使用一个乐观的存储过程来写入数据,假定分区存在并在必要时创建它。我们可以非常快速地插入数据。
对于数据的读取,我们按重要性和当前性能顺序列出了以下用例:
* 单个服务,单日使用:性能良好 * 多个服务,月度使用情况:性能差 * 单个服务,月度使用情况:性能差 * 多个服务,多个月份:性能非常差 * 多个服务,单日使用:性能良好
这是有道理的,因为分区是针对天数进行优化的,这是我们最重要的用例。但是,我们正在寻找改进次要需求的方法。
我们经常需要通过小时参数化查询,例如,仅在上午8点到下午6点之间给出结果,因此摘要表的用处有限。(这些参数的更改频率足够高,以至于创建多个摘要数据表是不可行的)。
在此背景下,第一个问题是:CouchDB是否适合此数据?如果是,考虑到上述用例,您将如何最好地在CouchDB文档中建模数据?我已经列出了一些选项,我们正在进行基准测试(_id、_rev除外):
每天每个连接一个文档。
{
  service_id:555
  day:20100101
  usage: {1265248762: {in:584,out:11342}, 1265249062: {in:94,out:1242}}
}

每月大约有6万份新文件。其中大部分新数据都是对现有文件的更新,而不是新文件。

(在这里,使用的对象是基于轮询时间戳的,值为传入和传出的字节)。

每个连接每月只能有一个文档

{
  service_id:555
  month:201001
  usage: {1265248762: {in:584,out:11342}, 1265249062: {in:94,out:1242}}
}

每个月大约有2,000份新文档需要处理。还需要适当更新现有文档。

一行数据对应一个文档

{
  service_id:555
  timestamp:1265248762
  in:584
  out:11342
}
{
  service_id:555
  timestamp:1265249062
  in:94
  out:1242
}

每月新增约1500万个文档。所有数据都将插入新文档中。插入速度较快,但我对亿万级文档在一年或两年后的效率有疑问。文件IO似乎是一个难以克服的问题(虽然我承认我并不完全理解其中的机制)。
我试图用面向文档的方式来处理这个问题,但打破关系型数据库的惯例确实有些困难。事实上,你只能最小限度地将视图参数化,这让我有点担心。话虽如此,上述哪种方法最合适呢?还有其他我没有考虑到的格式可以更好地发挥作用吗?
提前感谢,
杰米。
1个回答

10

我不认为这是个可怕的想法。

让我们考虑你的连接/月份方案。

假设一个条目大约有40个字符(这很慷慨),每个月您会得到大约8200个条目,那么在月底时您的最终文档大小将达到大约350K。

这意味着,全力以赴,您每5分钟将读取和写入2000个350K的文档。

就I/O而言,在5分钟时间窗口内,考虑读取和写入,平均每秒不到6MB。这对于今天的低端硬件来说完全足够。

然而,还有一个问题。当您存储该文档时,Couch将评估其内容以构建其视图,因此Couch将解析350K的文档。我的担心是(至少上次检查是这样,但已经有一段时间了)我不认为Couch可以跨CPU核心很好地扩展,所以这很容易固定Couch将要使用的单个CPU核心。我希望Couch能够读取、解析和处理2MB/s,但老实说我不知道。尽管erlang有它的好处,但它并不是最好的按直线奔跑的计算机语言。

最后一个问题是跟上数据库。在月底时,这将每5分钟写入700MB的数据。使用Couch的架构(仅追加),您将每5分钟写入700MB的数据,即每小时8.1GB,24小时后为201GB。

经过数据库压缩后,它会压缩到700MB(一个月),但在该过程中,该文件将变得越来越大,而且相当快。

在检索方面,这些大文档不会让我感到害怕。加载一个350K的JSON文档,是的,它很大,但在现代硬件上并不算太大。公告板上的头像比那还要大。因此,您想针对连接一个月内的活动做的任何事情都会非常快速。跨连接而言,显然你抓取的越多,花费就会越高(所有2000个连接需要700MB)。700MB是一个真实的数字,具有实际影响。此外,您的进程将需要积极地扔掉您不关心的数据,以便它可以丢弃无用的数据(除非您想在报告过程中加载700MB的堆)。
鉴于这些数字,每日连接可能更好一些,因为您可以更好地控制粒度。然而,坦率地说,我会选择最粗糙的文档,因为我认为这给了您从数据库中获得最佳价值,仅因为今天所有的磁头寻道和盘片旋转是导致许多I/O性能问题的根源,许多磁盘流数据很好。较大的文档(假设数据位于正确位置,因为Couch经常被压缩,这不应该是一个问题)比搜索更流畅。在内存中寻道与磁盘相比是“免费”的。
无论如何,在我们的硬件上运行您自己的测试,但请谨慎考虑所有这些问题。
编辑:
通过更多实验...
有几个有趣的观察结果。
在导入大型文档期间,CPU和I/O速度同样重要。这是因为进行 marshalling 和将 JSON 转换为视图使用的内部模型所消耗的 CPU 量。通过使用大文档(350k),我的CPU几乎达到了最大值(350%)。相比之下,对于较小的文档,它们一直以200%的速度运行,即使总体上是相同的信息,只是被切成不同的块。
在I/O方面,在350K文档期间,我记录了11MB/sec,但是在较小的文档中,它仅为8MB/sec。

对于压缩而言,它似乎几乎是受 I/O 限制的。我很难得出关于我的 I/O 潜力的好数据。缓存文件的副本可以达到每秒40MB以上的速度。压缩的速度大约为每秒8MB。但这与原始负载一致(假设Couch正在通过消息移动数据)。 CPU 较低,因为它进行的处理较少(它不解释JSON负载,也不重建视图),此外只有单个 CPU 进行了工作。

最后,对于读取,我尝试将整个数据库全部转储出来。一个单独的 CPU 被占据了,而我的 I/O 很低。我特意确保 CouchDB 文件实际上没有被缓存,我的机器有很多内存,所以许多东西都被缓存了。通过 _all_docs 进行的原始转储只有大约1 MB/秒。那几乎都是寻道和旋转延迟,而不是其他任何东西。当我使用大型文档执行转储时,I/O 到达了每秒3 MB,这表明了我之前提到的针对大型文档的流式传输效果的好处。

应该注意的是,在 Couch 网站上有关于提高性能的技巧我没有使用。特别是,我正在使用随机 ID。最后,这不是为了衡量 Couch 的性能,而是看起来负载会出现在哪里。我认为大文档和小文档之间的差异很有趣。

最后,最终的性能并不像仅仅以硬件为基础实现你的应用程序那样重要。正如你所提到的,你在进行自己的测试,这才是真正重要的事情。


CouchDB会启动多个系统进程来处理视图服务器以处理视图,因此它可以很好地跨多个核心进行扩展。CouchDB的其余部分是用Erlang编写的,并且在使用多个核心方面表现出色。 - mikeal
没错。我进行了测试,并将2000个这些大型文档(20个进程同时插入100个)插入到v0.9 Couch实例中。在4核2.66G Mac Pro上,这些文档基本上在3分30秒内插入完成。Couch占用了350%的CPU。最终磁盘文件大小约为2G。即使经过压缩,它几乎没有改变。相比之下,插入2000个“单日”文档只需要约18秒。当然快得多了。3分30秒太接近他们的5分钟窗口了。18秒好得多。不过压缩需要近3分钟。 - Will Hartung
非常感谢这个,这是一个很好的开始。我们运行了一些基准测试,并发现与您所发现的情况大致相同。我们将面临的主要问题是数据的不断更新 - 看起来对于“整个月份”文档来说会变得难以承受。只要我们能够定期压缩,希望我们就没问题。很遗憾我们不能为每个数据点选择一个文档,但正如您所怀疑的那样,文件IO似乎是禁止的。不幸的是,要更新任何其他类型的文档,我们需要先读取才能写入,以获取_rev... - majelbstoat
威尔,非常出色和明智的回答。真的很感谢你花时间深入挖掘这个问题。 - Riyad Kalla

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