Производительность MongoDB db.eval() в оболочке javascript против pymongo

Мне нужно выполнить операции обновления в документах, в которых мне нужно повернуть некоторые значения в массиве. В настоящее время запросы обновления MongoDB не позволяют вам $pop, а затем $push в том же поле в обновлении. После поиска рекомендаций в Интернете я решил, что db.eval() будет наиболее подходящим для моего использования, поскольку он обеспечивает атомарность и выполняемую мной операцию очень короткую, поэтому она не будет слишком долго блокировать db.

Вот пример того, что я пытаюсь сделать:

db.eval(function (id, newVal) {
    doc = db.collection.findOne({_id: id});
    doc.values.shift();
    doc.values.push(newVal);
    db.collection.save(doc);
}, id, newVal);

И это прекрасно работает! Затем я включил профилирование mongoDB, чтобы узнать, сколько миллисекунд использовала команда eval(), и я всегда получаю результаты меньше 1 миллисекунды:

 > db.system.profile.find({op: "command"}, {"millis": 1}) 
 { "millis" : 0 }
 { "millis" : 0 }
 ...

Это хорошая новость для меня, за исключением того, что мое приложение находится на python, поэтому я использую клиент pymongo для выполнения команд eval(). (Данные выше из оболочки mongo). Но теперь, когда я запускаю идентичные команды eval(), используя pymongo:

conn = pymongo.Connection(mongo_server_hostname)
db = conn.my_db

db.eval("""function (id, newVal) {
    doc = db.collection.findOne({_id: id});
    doc.values.shift();
    doc.values.push(newVal);
    db.collection.save(doc);
}""", id, new_val)

Я получаю очень разные профилирующие результаты:

> db.system.profile.find({op: "command"}, {"millis": 1}) 
{ "millis" : 13 }
{ "millis" : 14 }
{ "millis" : 14 }
...

Есть ли что-то принципиально иное в том, как запускать те же команды eval() из оболочки mongo и pymongo, что приводит к тому, что сервер занимает 14 мс для выполнения идентичных команд из pymongo?

Ответы

Ответ 1

Одной из возможных причин (но не обязательно причин) несоответствия, которые вы видите, является то, что и оболочка mongo, и mongod сервер используют Google v8 Javascript engine по умолчанию (хотя он может быть настроен на использование Spidermonkey в качестве альтернативы) для интерпретации команд, которые вы даете.

Google v8 видит горячие точки в коде Javascript и, вероятно, часто используется JIT-код.

С другой стороны, ваниль PyMongo написан на чистом Python, что означает, что он всегда будет интерпретироваться, что является чем-то, что имеет значительные накладные расходы.

Если вы этого уже не делаете, одна из возможностей заключается в использовании расширения PyMongo, написанного на C, а не по умолчанию, или, если остальная часть вашего приложения совместима, используйте интерпретатор PyPy JIT для Python.

Если вы используете любое распространение, полученное из Debian (например, Ubuntu), пакет python-pymongo-ext предоставляет вам предварительно скомпилированную версию версии C PyMongo.