Производительность 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.