如何让MongoDB在确认写入后将实际数据返回给其他读取器


How to make MongoDB return actual data to other readers after acknowledged write?

我正在尝试模拟MongoDB中的某种"事务"。我正在锁定关键代码部分(使用memcache(,从MongoDB读取文档,对其进行一些更改,将其写回并解锁部分。但是,如果并发运行,此类操作很少会无法按预期工作。确认后(w = 1(写入另一个线程仍然可以读取以前版本的文档。在重负载(10+ 并发请求(下,这种情况大约会发生 1/1000 次,但破坏了整个想法。

我尝试使用 fsync 选项,但它使写入速度太慢而无法使用(这些操作应该经常运行(。 日志选项 (j = 1( 不能解决问题。有没有办法让其他线程在从某个线程确认写入后读取文档的一致版本?

这是示例代码(我将其更改为简单计数器,无论如何都可以重现问题(:

$mchost = "127.0.0.1";
$mcport = 11211;
$lockkey = "testlockkey";
$mongoconnstr = "mongodb://localhost";
$mongodb = "testdb";
$mongotbl = "test";
$mongoopts = array("w" => 1);
$varname = "counter";
function LIBLockSection($id, $timeout)
{
    for ($i = 0; $i < $timeout; $i++) {
        if (memcache_add($GLOBALS["mc"], $id, 1, FALSE, 60)) return 1;
        usleep(50000);
    }
    return 0;
}
function LIBUnlockSection($id)
{
    return memcache_delete($GLOBALS["mc"], $id);
}
function MNGGetTable($connstr, $dbname, $tname)
{
    $m = new Mongo($connstr);
    $db = $m->selectDB($dbname);
    return $db->selectCollection($tname);
}
$GLOBALS["mc"] = memcache_connect($mchost, $mcport) or die('memcached is down!');
$tbl = MNGGetTable($mongoconnstr, $mongodb, $mongotbl);
$lockres = LIBLockSection($lockkey, 50);
if (!$lockres) {
    exit;
}
$x = $tbl->findOne();
if (!isset($x[$varname])) $x[$varname] = 0;
$x[$varname]++;
$opts = $mongoopts;
$opts["upsert"] = TRUE;
$tbl->update(array(), array($varname => $x[$varname]), $opts);
LIBUnlockSection($lockkey);
trigger_error($x[$varname]);

此脚本正在将其输出写入标准 PHP 错误日志。为了运行它,我在服务器的根目录中使用 ab(用于测试.php(:

ab -c 10 -n 10000 http://127.0.0.1/test.php

预期输出是从 1 到 10000 的序列号。但是如果没有 fsync 选项,有时我们会得到双精度:

[Tue Oct 01 00:53:53 2013] [error] [client 127.0.0.1] PHP Notice:  344 in /var/www/test.php on line 49
[Tue Oct 01 00:53:53 2013] [error] [client 127.0.0.1] PHP Notice:  344 in /var/www/test.php on line 49

现在我只看到一种可能的解决方案:在文档中使用"版本"变量并在数据库上查找和修改操作,以确保仅在操作之间未更改版本时才更新文档。但是代码看起来很丑,所以我想知道是否有其他方法可以解决这个问题。

我看到您的代码存在一些问题,可以解释:

但是如果没有 fsync 选项,有时我们会得到双精度:

您是使用已弃用的Mongo类之一:

$m = new Mongo($connstr);

默认的写入问题是套接字确认。

通常,这将通过以下方式进行反驳:

$mongoopts = array("w" => 1);

但是wMongo不兼容,而是与MongoClient兼容。Mongo的选项是 safe .

这意味着你永远不会真正从MongoDB获得成功OP的确认,而只是从套接字获得确认。

尝试修复您的代码,看看问题是否再次出现。

但是,此链接可能会有所帮助:http://docs.mongodb.org/manual/tutorial/create-an-auto-incrementing-field/从MongoDB获取原子,无竞争条件的增量密钥。