Windows上的MongoDB有时*使用tailable游标作为消息队列

时间:2014-01-27 18:22:09

标签: performance mongodb cursor tail

我们正在C上完成通用服务总线实现,客户端用于C#,Delphi,PL / SQL和PHP。 该库运行良好,我们的总线具有非常好的性能,除非 MongoDB数据库在Windows上运行(在2008 R2,2003和7上测试)没有其他“特殊“程序正在运行。

Out test执行以下操作:

  1. 程序A在有上限的集合上发送消息
  2. 程序B关闭消息队列集合,并在使用awaitData param设置为true的游标显示消息时“唤醒”
  3. 当程序B醒来时,准备一个消息并向程序A发送响应,在程序A中插入特定集合的文档
  4. 程序A已经在等待第二个“响应”集合,并在程序B(生产者)发回响应时被唤醒
  5. 循环结束
  6. 我们的测试程序计算循环并报告使用Visual Studio 2010编译的控制台应用程序的性能。

    我们在一台机器上运行这一切,或者在MongoDB上使用不同的机器,并在同一台机器上运行使用者和生产者。 我们在Windows 2008R2,Windows 2003和Windows 7上运行它。 对于2008R2,我们使用了针对该操作系统的特殊mongo构建,而对于2003和7,我们使用了“遗留”64位构建。

    在一个干净的操作系统中,没有程序运行,我们的测试每秒执行大约32-50次往返,与一切全速运行时获得的“好”结果相比,这是一个糟糕的性能。

    现在,奇怪的是:

    当在运行mongo数据库的同一台机器上启动某个应用程序时,当消费者和生产者在一台机器上运行时,我们的测试速度可以提高到大约450 /秒(当在同一台机器上运行环回时)到大约300 /秒和mongodb在另一台通过网络的机器上。

    之前我们从未注意到这个问题的原因是因为我们的开发中几乎所有时间都是Visual Studio打开的,Visual Studio是一个充当“mongodb加速器”的程序(我知道这听起来很荒谬,请不要在此声明中抨击我。)

    首先,我们在没有VS打开的情况下运行我们的测试时,“随机”地注意到了这个问题。因此,我们倾向于将其归咎于运行vmware的底层SAN,或vm主机,或宇宙射线或我们程序上的NSA窥探。 直到我们在运行测试的同时最终确定VS打开之间的相关性,并缩小到以下范围:

    在Windows系统上运行的MongoDB(作为控制台或服务),虚拟或物理版本2008R2,2003或7将在一个上限集合上接收数据的模式运行缓慢,并唤醒一个尾随光标,然后发回一个响应消费者在另一个上限集合中以相同的方式除非你只需启动一个程序,如Visual Studio,Delphi XE4,谷歌Chrome浏览器,CrystalDiskMark磁盘I / O测试程序(其他程序也可能加速Mongo )。然后mongodb加速了之前提到的模式的全部数量级。

    我们无法确切地找到这些程序可能导致问题的共同点。

    此时我们对此问题感到震惊,我甚至查看了用于tailable游标的MongoDB代码,但没有找到任何可能导致问题的气味。代码几乎旋转了最多4秒等待数据出现,除了每个循环上的可疑“睡眠”调用之外,没有其他任何东西引人注目。

    某些程序是否有可能最终导致Sleep()Windows API调用的行为方式不同?这使得mongo在tailable游标上执行此操作的速度较慢??

    我们认为某些事情确实在“放慢速度”,因为CPU利用率配置文件也会下降,就像mongodb在运行缓慢时“等待”某些事情一样。

    我知道这种模式在基于unix / linux的系统上运行良好,我在Mac上尝试了相同的代码库,没有任何问题,所以这可怕闻起来像Windows问题。

    其他人遇到过类似的问题吗?

1 个答案:

答案 0 :(得分:1)

找到问题的根源。 MongoDB调用直接Sleep()Windows API函数,即使睡眠时间小于15毫秒。

由于默认的Windows最低分辨率,任何低于此值(至少在Windows 2008 R2,Windows 2003和Windows 7上)的任何内容都将至少休眠15毫秒。

MongoDb中的简单解决方案是更新time_support.cpp:

void sleepmillis(long long s) {
    fassert(16228, s <= 0xffffffff );        
    Sleep((DWORD) s);
}

为:

extern "C" unsigned int __stdcall timeBeginPeriod( unsigned int ms );
extern "C" unsigned int __stdcall timeEndPeriod( unsigned int ms );
// Notice bellow the arbitrary nature of 50ms set as the "minimum" timer resolution
// There seems to be no complete agreement on that *is* the default timer resolution in Windows.
// To be on the "safe side" let's use 50ms
#define BELLOW_WINDOWS_MIN_RESOLUTION(s)(s > 0 && s < 50)

void sleepmillis(long long s) {
    fassert(16228, s <= 0xffffffff );
    // When our waiting period falls bellow Windows min resolution, let's set resolution
    // to 1 ms. Note that this change may effect all kernel scheduler thread operations.
    // Apparently this changes the Windows kernel "quantum" length
    // see http://msdn.microsoft.com/en-us/library/windows/desktop/dd757624(v=vs.85).aspx
    // Applications such as Google Chrome seem to do this during the life of Chrome, that's why
    // running apps which do this "accelerate" certain mongo operations that depending on proper
    // Sleep() resolution on Windows
    if(BELLOW_WINDOWS_MIN_RESOLUTION(s)) timeBeginPeriod(1);
    Sleep((DWORD) s);
    if(BELLOW_WINDOWS_MIN_RESOLUTION(s)) timeEndPeriod(1);
}