Это переведенная статья, оригинальная ссылка находится по адресуздесь. перевестиможет быть не точным, добро пожаловать, чтобы указать на проблемы в статье.
Является ли PyMongo потокобезопасным?
PyMongo является потокобезопасным и предоставляет встроенный пул соединений для многопоточных приложений.
Безопасен ли процесс PyMongo?
PyMongo не является безопасным процессом, вы должны быть осторожны, если используете экземпляр MongoClient в fork(). В частности, экземпляры MongoClient нельзя копировать из родительского процесса в дочерний процесс, а родительский процесс и каждый дочерний процесс должны создавать свой собственный экземпляр MongoClient. Из-за собственной несовместимости использование экземпляра MonogoClient, скопированного из родительского процесса в дочерний процесс, может привести к взаимоблокировке. PyMongo будет выдавать предупреждения в случае возможной взаимоблокировки.
MongoClient порождает несколько потоков для выполнения фоновых задач, таких как мониторинг серверов соединений. Эти потоки совместно используют состояние, защищенное экземпляром Lock (процесс небезопасен), поэтому MongoClient подвержен тем же ограничениям, что и другие многопоточные программы, использующие блокировки (взаимное исключение), одним из которых является то, что блокировка становится недействительной после fork(). Во время процесса fork все блокировки копируются в дочерний процесс и остаются в том же состоянии, что и родительский процесс: если они заблокированы в родительском процессе, блокировки, скопированные дочерним процессом, также блокируются. Дочерний процесс, созданный fork(), имеет только один поток, поэтому любые блокировки, взятые из любых дочерних потоков в родительском процессе в этом дочернем потоке, не будут освобождены, и когда этот дочерний поток попытается получить одну из блокировок, он умрет. Замок.
О проблемах с fork() при использовании блокировок Python в многопоточном контексте см.bugs.python.org/issue6721.
Как работает пул соединений в PyMongo
Каждый экземпляр MongoClient имеет встроенный пул соединений на каждом сервере MongoDb, и эти пулы соединений будут немедленно открывать сокеты для поддержки количества одновременных операций MongoDB, необходимых для многопоточных приложений. Эти сокеты не имеют сходства с потоком.
Размер каждого пула соединений ограниченmaxPoolSize
, значение по умолчанию — 100. Если присутствуетmaxPoolSize
подключений к серверу и все эти подключения используются, следующий запрос к этому серверу будет ожидать, пока одно из подключений не станет доступным.
Экземпляр клиента открывает дополнительный сокет на каждом сервере в кластере MongoDB для отслеживания состояния сервера.
Например: клиент, подключающийся к серверам репликации master-slave с 3 узлами, откроет 3 сокета мониторинга. Он также может открывать столько сокетов, сколько необходимо (доmaxPoolSize
) для поддержки одновременной работы многопоточных приложений на каждом сервере. существуетmaxPoolSize
В случае 100, если приложение использует только основное соединение, увеличится только количество соединений в основном пуле соединений (до 103). Если приложение использует ReadPreference для запроса базы данных-получателя, количество подключений в его пуле подключений также увеличится, а общее количество подключений может достичь 303.
можно сделать с помощьюminPoolSize
(по умолчанию 0) параметр для установки минимального количества одновременных подключений на сервер. Пул соединений будет инициализированminPoolZise
разъем. Если сокет закрывается по сетевым причинам, в результате чего количество подключений к сокету (используемых и простаивающих) падает ниже минимального значения, новый сокет будет открываться до тех пор, пока количество сокетов не достигнет минимального значения.
можно использоватьmaxIdleTime
Параметр для установки максимального количества миллисекунд, в течение которого соединение будет простаивать в пуле соединений, после чего оно будет удалено или заменено, значение по умолчанию — None (без ограничений).
Конфигурация MongoClient по умолчанию подходит для большинства приложений:
client = MongoClient(host, port)
Создайте клиента один раз для каждого процесса и повторно используйте его для всех операций. Распространенной ошибкой является создание нового клиента для каждого запроса, потому что это очень неэффективно.
Чтобы поддерживать чрезвычайно большое количество одновременных операций MongoDB в процессе, увеличьтеmaxPoolSize
:
client = MongoClient(host, port, maxPoolSize=200)
Или сделайте его неограниченным:
client = MongoClient(host, port, maxPoolSize=None)
По умолчанию произвольному количеству потоков разрешено ожидать доступности сокета, и они могут ждать произвольное количество времени. можно установитьwaitQueueMultiple
параметр для ограничения количества ожидающих потоков. Например: ограничить количество ожиданий не более 500:
client = MongoClient(host, port, maxPoolSize=50, waitQueueMultiple=10)
Когда уже есть 500 потоков, ожидающих сокета, 501-й поток, которому нужен сокет, выдаст ExceededMaxWaiters. использоватьwaitQueueMultiple
Количество очередей в приложении во время пиковой нагрузки теперь может быть загружено, но вызовет дополнительные исключения.
Как только пул соединений достигает максимального значения, дополнительные потоки могут бесконечно ждать, пока сокеты станут доступными, если вы не установитеwaitQueueTimeoutMS
:
client = MongoClient(host, port, waitQueueTimeoutMS=100)
В этом примере, если поток ожидает сокета более 100 мс, он выдаст ошибку ConnectionFailure.waitQueueTimeoutMS
Полезно для сценариев, в которых ограничение продолжительности операций во время скачков нагрузки важнее, чем выполнение каждой операции.
Когда любой поток вызывает close(), все незанятые сокеты будут закрыты, а все используемые сокеты будут закрыты, когда он вернется в пул соединений.
Поддерживает ли PyMongo Python3?
PyMongo поддерживает CPython3.4+ и PyPy3. Для получения подробной информации см.Python3 FAQ.
Поддерживает ли PyMongo асинхронные фреймворки, такие как Gevent, asyncio, Tornado или Twisted?
PyMongo полностью поддерживаетGevent.
Чтобы использовать MongoDB сasyncioилиTornadoиспользуются вместе, см.Motorпроект.
Почему PyMongo добавляет поле _id во все мои документы?
когда используешьinsert_one()
,insert_many()
илиbulk_write()
При вставке документа в MongoDB, если документ не_id
поле, PyMongo автоматически добавит_id
поле, значение которогоObjectIdэкземпляр . Например:
>>> my_doc = {'x': 1}
>>> collection.insert_one(my_doc)
<pymongo.results.InsertOneResult object at 0x7f3fc25bd640>
>>> my_doc
{'x': 1, '_id': ObjectId('560db337fba522189f171720')}
при звонкеinsert_many()
При вставке списка литературы в один документ это часто бывает вызваноBulkWriteError
Ошибка. Это вызвано несколькими привычками Python:
>>> doc = {}
>>> collection.insert_many(doc for _ in range(10))
Traceback (most recent call last):
...
pymongo.errors.BulkWriteError: batch op errors occurred
>>> doc
{'_id': ObjectId('560f171cfba52279f0b0da0c')}
>>> docs = [{}]
>>> collection.insert_many(docs * 10)
Traceback (most recent call last):
...
pymongo.errors.BulkWriteError: batch op errors occurred
>>> docs
[{'_id': ObjectId('560f1933fba52279f0b0da0e')}]
PyMongo добавляет таким образом_id
поля по нескольким причинам:
- Все документы MongoDB должны быть представлены
_id
поле. - Если PyMongo вставляет дополнение без
_id
Документ поля, MongoDB добавит его сама, и не вернет_id
поле в PyMongo. - в добавлении
_id
Дублирование документа, вставляемого перед полем, является непомерно дорогим для большинства приложений с большим объемом операций записи.
Если вы не хотите, чтобы PyMongo добавлял в документацию_id
поле, вы можете вставить только существующее_id
Документация для поля.
Порядок ключей в репликах — почему запросы упорядочены в оболочке, но не упорядочены в PyMongo?
Пары ключ-значение в документе BSON могут быть в любом порядке (кроме_id
всегда первый). При чтении и записи данных ключи оболочки mongo поддерживают порядок. Обратите внимание, что в следующем примере «b» предшествует «a» во вставках, и то же самое верно для запросов:
> // mongo shell.
> db.collection.insert( { "_id" : 1, "subdocument" : { "b" : 1, "a" : 1 } } )
WriteResult({ "nInserted" : 1 })
> db.collection.find()
{ "_id" : 1, "subdocument" : { "b" : 1, "a" : 1 } }
PyMongo по умолчанию представляет документы BSON как словари Python, и порядок ключей в словаре отсутствует. То есть объявление словаря Python с «a» впереди или «b» впереди — это одно и то же.
>>> print({'a': 1.0, 'b': 1.0})
{'a': 1.0, 'b': 1.0}
>>> print({'b': 1.0, 'a': 1.0})
{'a': 1.0, 'b': 1.0}
Поэтому словари Python не гарантируют отображение пар ключ-значение в том порядке, в котором они находятся в BSON. В следующем примере «а» отображается перед «б»:
>>> print(collection.find_one())
{u'_id': 1.0, u'subdocument': {u'a': 1.0, u'b': 1.0}}
Используйте класс SON для сохранения порядка при чтении BSON, который представляет собой словарь, запоминающий порядок ключей. Во-первых, получите дескриптор коллекции, настроив использование SON вместо словаря:
>>> from bson import CodecOptions, SON
>>> opts = CodecOptions(document_class=SON)
>>> opts
CodecOptions(document_class=<class 'bson.son.SON'>,
tz_aware=False,
uuid_representation=PYTHON_LEGACY,
unicode_decode_error_handler='strict',
tzinfo=None)
>>> collection_son = collection.with_options(codec_options=opts)
И документы, и реплики в результатах запроса теперь представлены как объекты SON:
>>> print(collection_son.find_one())
SON([(u'_id', 1.0), (u'subdocument', SON([(u'b', 1.0), (u'a', 1.0)]))])
Порядок ключей в реплике такой же, как и в реальном хранилище: «b» предшествует «a».
Поскольку порядок ключей в словаре не определен, вы не можете предсказать, как он будет сериализован в BSON. Но MongoDB считает реплики идентичными, только если их ключи имеют одинаковый порядок. Таким образом, запрос копии со словарем может не дать результатов:
>>> collection.find_one({'subdocument': {'a': 1.0, 'b': 1.0}}) is None
True
Замена порядка ключей в запросе не имеет никакого значения:
>>> collection.find_one({'subdocument': {'b': 1.0, 'a': 1.0}}) is None
True
Как мы видели выше, Python считает эти два словаря одним и тем же.
Есть два обходных пути. Первый способ — сопоставить копии по полям:
>>> collection.find_one({'subdocument.a': 1.0,
... 'subdocument.b': 1.0})
{u'_id': 1.0, u'subdocument': {u'a': 1.0, u'b': 1.0}}
Приведенный выше запрос соответствует любой копии, где «a» равно 1,0, а «b» равно 1,0, независимо от того, в каком порядке вы указываете их в Python или в каком порядке они хранятся в BSON. Кроме того, этот запрос теперь может сопоставлять ключи, отличные от «a» и «b» в реплике, в то время как предыдущий запрос требовал точного совпадения.
Второй способ — использовать SON для указания порядка ключей:
>>> query = {'subdocument': SON([('b', 1.0), ('a', 1.0)])}
>>> collection.find_one(query)
{u'_id': 1.0, u'subdocument': {u'a': 1.0, u'b': 1.0}}
При запросе порядок ключей, используемый при создании SON, сохраняется при сериализации в BSON. Таким образом, вы можете создать реплику, точно совпадающую с репликой в коллекции.
Для получения дополнительной информации см. MongoDB Manual entry on subdocument matching
CursorNotFound Что означает неверный идентификатор курсора на стороне сервера?
Если курсоры в MongoDB были открыты в течение длительного времени без выполнения над ними каких-либо операций, они истечет время ожидания на сервере. Это может привести к возникновению исключений CursorNotFound при переборе курсора.
Как я могу изменить время ожидания курсора?
MongoDB не поддерживает настраиваемые тайм-ауты для курсоров, но его можно полностью отключить. существуетfind()
входящийno_cursor_timeout=True
.
как хранитьdecimal.Decimal
пример?
PyMongo >= 3.4 поддерживает введение типа Decimal128 BSON. Для получения подробной информации см.Decimal12.
MongoDB
Я сохранил 9.99, но запрос стал 9.99000000000000002, что происходит?
База данных представляет 9,99 как число с плавающей запятой IEEE (что является общим для MongoDB, Python и большинства других современных языков). Проблема в том, что 9.99 не может быть представлено числом двойной точности с плавающей запятой, а также в некоторых версиях Python:
>>> 9.99
9.9900000000000002
Сохранение 9.99 с помощью PyMongo дает тот же результат, что и сохранение с помощью оболочки JavaScript или любого другого языка (и такой же, как если 9.99 вводится в программу Python).
Можете ли вы добавить значение в метод свойства документа?
пройти через
.
Получите ценность документа, а не только словаря Python прямо сейчас
Этот запрос приходил много раз, но мы решили не реализовывать такой способ. Связанныйjria caseС некоторой информацией об этом решении, вот краткое резюме:
- Это загрязнит пространство имен атрибутов документа и, таким образом, может привести к тонким ошибкам/ошибкам запутывания при использовании ключей с теми же именами, что и методы словаря.
- Единственная причина, по которой мы используем объекты SON вместо обычных словарей, заключается в том, чтобы поддерживать порядок ключей, поскольку именно эти операции нужны серверу. Поэтому мы сомневаемся в необходимости сложного SON (каким-то образом мы хотим вернуться к использованию только словарей, не нарушая обратной совместимости для всех)
- Поскольку документы ведут себя как словари, новым пользователям легко (и Pythonic) работать с документами. Если мы начнем это менять, это добавит барьер для новых предметов — дополнительные затраты на обучение.
Как правильно обрабатывать часовые пояса в PyMongo?
Пример того, как правильно обрабатывать объекты даты и времени, см.Datetime and Timezones.
Как сохранить экземпляр datetime.date?
PyMongo не поддерживает сохранение экземпляров datetime.date, потому что BSON не имеет типа для сохранения (даты без времени (гггг-мм-дд)). PyMongo не обеспечивает соблюдениеdatetime.date
преобразовать вdatetime.datetime
соглашение, поэтому вам нужно выполнить преобразование в вашем коде.
При запросе документов с использованием ObjectId в веб-программе я не получаю никаких результатов?
В веб-приложении ObjectId документа обычно кодируется в URL-адресе, например:
"/posts/50b3bda58a02fb9a84d8991e"
Веб-фреймворк передает ObjectId серверной части как часть строки URL-адреса, поэтому, когда они передают его вfind_one()
Раньше его нужно было конвертировать в ObjectId. Забыть это преобразование является распространенной ошибкой. Следующий пример — правильный способ сделать это во Flask (аналогично другим веб-фреймворкам):
rom pymongo import MongoClient
from bson.objectid import ObjectId
from flask import Flask, render_template
client = MongoClient()
app = Flask(__name__)
@app.route("/posts/<_id>")
def show_post(_id):
# NOTE!: converting _id from string to ObjectId before passing to find_one
post = client.db.posts.find_one({'_id': ObjectId(_id)})
return render_template('post.html', post=post)
if __name__ == "__main__":
app.run()
Подробнее см.Querying By ObjectId
Как использовать PyMongo с Django?
Django — популярный веб-фреймворк Python. Django включает в себя ORM, django.db. В настоящее время нет официальной библиотеки MongoDB для Django.
django-mongodb-engine— это неофициальная библиотека MongoDB, которая поддерживает агрегацию Django, (атомарные) обновления, встроенные объекты, Map/Reduce и GridFS. Он позволяет использовать большинство встроенных функций Django, включая ORM, администрирование, аутентификацию, структуру сайта и сеанса, а также кэширование.
Однако также легко использовать MongoDB и PyMongo в Django без использования библиотеки Django. Большую часть функций, предоставляемых Django, все еще можно использовать, за исключением некоторых функций Django, для которых требуется django.db (администрирование, аутентификация и сеансы), которые не могут использовать MongoDB.
Один из проектов, который упрощает использование Django и MongoDB, — этоmango, mango — это серия библиотек MongoDB, разработанных для сеансов и аутентификации Django (полностью минуя django.db).
Работает ли PyMongo с mod_wsgi?
Может. Для получения подробной информации см.PyMongo and mod_wsgi
Как я могу использовать модуль Python json для кодирования моего документа в JSON?
json_utilЭто встроенная библиотека инструментов PyMongo, которую можно гибко использовать с модулем Python json, документами BSON и MongoDB Extended JSON. так какjson
Модули не поддерживают некоторые специальные типы PyMongo (например, ObjectId и DBRef), поэтому поддерживаются не все документы.
python-bsonjs— это быстрый конвертер BSON в MongoDB Extended JSON, созданный поверх libbson. python-bsonjs не зависит от PyMongo и может обеспечить лучшую производительность, чем json_util. python-bsonjs лучше всего работает с PyMongo при использовании RawBSONDocument.
Почему я получаю ошибку OverflowError при декодировании даты, сохраненной на другом языке?
PyMongo декодирует значения даты и времени BSON в экземпляры Python datetime.datetime. Количество экземпляров datetime.datetime ограничено между datetime.MINYEAR (обычно 1) и datetime.MAXYEAR (обычно 9999). Некоторые драйверы MongoDB, такие как драйвер PHP, могут хранить дату и время BSON далеко за пределами значений года, поддерживаемых datetime.datetime.
Есть несколько способов решить эту проблему. Один из вариантов — отфильтровать документы со значениями вне диапазона, поддерживаемого datetime.datetime:
>>> from datetime import datetime
>>> coll = client.test.dates
>>> cur = coll.find({'dt': {'$gte': datetime.min, '$lte': datetime.max}})
Другой способ - отфильтровать поле даты и времени, когда оно вам не нужно:
>>> cur = coll.find({}, projection={'dt': False})
Использование PyMongo в нескольких процессах
В системах Unix модуль многопроцессорности используетfork()
Процесс сборки. существуетfork()
Следует соблюдать осторожность при использовании экземпляров MongoClient в: Экземпляры MongoClient нельзя копировать из родительского процесса в дочерний процесс, родительский процесс и каждый дочерний процесс должны создавать свой собственный экземпляр MongoClient. Например:
# Each process creates its own instance of MongoClient.
def func():
db = pymongo.MongoClient().mydb
# Do something with db.
proc = multiprocessing.Process(target=func)
proc.start()
Никогда не делайте этого:
client = pymongo.MongoClient()
# Each child process attempts to copy a global MongoClient
# created in the parent process. Never do this.
def func():
db = client.mydb
# Do something with db.
proc = multiprocessing.Process(target=func)
proc.start()
так какfork()
, присущей несовместимости между потоками и блокировками, экземпляр MongoClient, скопированный из родительского процесса, имеет высокую вероятность взаимоблокировки в дочернем процессе.