`

PyMongo 常见问题

阅读更多

这是一篇翻译文章,原链接在这里。翻译可能不准确,欢迎指出文章中存在的问题。

PyMongo是线程安全的吗

PyMongo是线程安全的,并且为多线程应用提供了内置的连接池

PyMongo是进程安全的吗

PyMongo不是进程安全的,如果你在fork()中使用MongoClient实例,必须小心。具体来说,MongoClient实例不能从父进程复制到子进程,父进程和每个子进程必须创建属于自己的MongoClient实例。由于本身的不兼容性,在子进程中使用从父进程复制的MonogoClient实例很有可能发生死锁。PyMongo会在有可能引起死锁的情况下发出警告。

MongoClient产生多个线程来运行后台任务,如监视连接服务器。这些线程共享受Lock实例(进程不安全)保护的状态,所以,MongoClient受到与其他使用锁(互斥)的多线程程序一样的限制,其中一个限制是在使用fork()后锁失效。在fork过程中,所有锁都会被复制到子进程中,并且与父进程保持相同的状态:如果父进程中是锁定的,子进程复制的锁也是锁定的。由fork()创建的子进程只有一个线程,所以在这个子线程中任何从父进程中任何子线程中取出的锁都不会被释放,当这个子线程尝试获取其中一个锁时,会发生死锁。

有关在fork()使用多线程上下文中的Python锁的问题,请参阅bugs.python.org/issue6721

连接池在PyMongo中是如何工作的

每个MongoClient实例在每个MongoDb服务器都有一个内置的连接池,这些连接池会立即打开socket,用来支持多线程应用所需的并发操作MongoDB数量。这些socket没有线程相关性。

每个连接池的大小被限制在maxPoolSize,默认值为100.如果存在maxPoolSize个到服务器的连接并且这些连接全部在使用中,那么到该服务器的下一个请求会一直等待,直到其中一个连接可用。

客户端实例在MongoDB集群中的每个服务器上额外打开一个socket来监视服务器的状态。

例如:一个连接到3个节点主从复制服务器的客户端将打开3个监视socket。它还可以根据需要打开更多个socket(最多maxPoolSize)来支持每个服务器上多线程应用的并发操作。在maxPoolSize为100的情况下,如果应用只使用主连接,则只有主连接池的连接数增加(最多103)。如果应用使用ReadPreference查询辅助数据库,则它的连接池的连接数也会增加,总连接数可以达到303.

可以通过使用minPoolSize(默认0)参数来设置每个服务器的最小并发连接数。连接池将初始化minPoolZise个socket。如果由于网络原因导致socket关闭,导致socket的连接数(使用中和空闲中)下降到最小值以下,则会打开新的socket,直到socket的数量达到最小值。

可以使用maxIdleTime参数来设置一个连接在连接池中保持空闲的最大毫秒数,之后它将被删除或者替换,默认值为None(没有限制)。

MongoClient的默认配置适用于大多数应用:

client = MongoClient(host, port)

为每个进程创建一次客户端,并将其重用于所有操作。为每个请求创建一个新的客户端是一个常见的错误,因为这样非常低效。

要在一个进程中支持极高数量的并发MongoDB操作,需增加maxPoolSize:

client = MongoClient(host, port, maxPoolSize=200)

或者使其没有限制:

client = MongoClient(host, port, maxPoolSize=None)

默认情况下,允许任意数量的线程等待socket可用,并且可以等待任意长的时间。可以设置waitQueueMultiple参数来限制等待线程的数量。例如:限制等待数量不大于500:

client = MongoClient(host, port, maxPoolSize=50, waitQueueMultiple=10)

当已经由500个线程正在等待socket时,第501个需要socket的线程将抛出ExceededMaxWaiters。使用waitQueueMultiple可以现在加载峰值期间应用中排队的数量,但是会引起额外的异常。

一旦连接池达到最大值,另外的线程可以无限等待socket可用,除非你设置了waitQueueTimeoutMS:

client = MongoClient(host, port, waitQueueTimeoutMS=100)

在这个例子中,一个线程如果等待socket的时间超过100ms,它将抛出ConnectionFailure错误。waitQueueTimeoutMS适用于在加载峰值期间限制操作的持续时间比完成每个操作更重要的情景。

当任何线程调用close()时, 所有闲置的socket都会被关闭,所有正在使用的socket将在它返回连接池时被关闭。

PyMongo支持Python3 吗?

PyMongo支持CPython3.4+和PyPy3。详情请参阅Python3 FAQ

PyMongo是否支持Gevent,asyncio,Tornado或Twisted等异步框架?

PyMongo完全支持Gevent

要将MongoDB与asyncioTornado一起使用,请参阅Motor项目。

对于Twisted,请参阅TxMongo

为什么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字段的文档。

副本中的键顺序-为什么查询在shell是有序的,在PyMongo中无序?

BSON文档中的键值对可以是任何顺序(除了_id始终是第一个)。在读写数据是,mongo shell按键保持顺序。下面的例子中请注意在插入是'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中的顺序显示键值对。下面的例子中,'a'显示在'b'前面:

>>> 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 游标id无效在服务端是什么意思?

如果MongoDB中的游标已经打开了很长时间而没有对它们执行任何操作,他们会在服务器上超时。这可能会导致在迭代游标时引发CursorNotFound异常。

如何更改游标的超时时间?

MongoDB不支持游标自定义超时时间,但可以完全关闭。在find()时传入no_cursor_timeout=True

如何存储decimal.Decimal实例?

PyMongo >= 3.4 支持引入Decimal128 BSON类型。详情请参阅Decimal12

MongoDB <= 3.2 仅支持IEEE 754 浮点数-与Python浮点类型相同。PyMongo可以在这些版本的MongoDB中存储Decimal实例的唯一方法是将他们转换成这个标准,所以不管如何你只可以存储浮点数。我们强迫用户明确的做这个转换来告知它们转换正在发生。

我保存了9.99,但是查询是确变成了9.9900000000000002,这是怎么回事?

数据库将9.99表示为IEEE浮点数(这是MongoDB和Python以及大多数其他现代语言通用的)。问题是9.99不能用双精度浮点数来表示,在Python的某些版本中也是如此:

>>> 9.99
9.9900000000000002

使用PyMongo保存9.99时得到的结果与使用JavaScript shell或任何其他语言保存的结果完全相同(以及将9.99输入到 Python程序的一样)。

你们能添加对文档属性方式的取值吗?

通过.获取文档的值,而不仅仅是现在只能用Python字典的方式获取

这个请求已经出现了很多次,但我们决定不实现任何这样的方式。相关的jria case有关于这个决定的一些信息,这里是一个简短的总结:

  1. 这将污染文档的属性名称空间,因此当使用与字典方法相同的名称的键时可能导致细微的bugs/混淆错误。
  2. 我们使用SON对象而不是常规字典的唯一原因是维护键排序,因为服务器需要这些准确的操作。因此我们在是否需要复杂的SON的必要性上犹豫不决(某种程度上我们希望恢复单独使用字典,而不会破坏每个人的向后兼容性)
  3. 因为文档的表现像字典,所以新用户很容易(和Pythonic)的处理文档。如果我们开始改变这点将会为新受增加一个障碍-另外的学习成本。

PyMongo中处理时区的正确方法是什么?

有关如何正确处理datetime对象的示例,请参阅Datetime and Timezones

如何保存一个datetime.date实例?

PyMongo不支持保存datetime.date实例,因为BSON没有类型来保存(没有时间的日期(yyyy-MM-dd))。PyMongo并没有强制将datetime.date转换为datetime.datetime的约定,所以你需要在代码中执行转换。

在web程序中使用ObjectId查询文档时,没有得到任何结果?

在Web应用程序中,通常在URL中会对文档的ObjectId进行编码,如:

"/posts/50b3bda58a02fb9a84d8991e"

web框架将ObjectId作为url字符串的一部分传递给后台,因此在她们呢传递给find_one()之前,必须转换为ObjectId。忘记这个转换是一个常见的错误。下面的例子是在Flask中正确的执行操作(其他Web框架类似):

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

如何在Django中使用PyMongo?

Django是一个流行的Python Web框架。 Django包含一个ORM,django.db。 目前,Django没有官方的MongoDB库。

django-mongodb-engine是一个非官方的MongoDB库,支持Django聚合,(原子)更新,嵌入对象,Map / Reduce和GridFS。它允许您使用Django的大部分内置功能,包括ORM,admin,authentication,site 和会话框架以及缓存。

但是,在Django中不使用Django库也很容易使用MongoDB和PyMongo。除了某些需要django.db(管理,认证和会话)的Django的功能不能使用MongoDB外,Django提供的大部分功能仍然可以使用。

有一个让Django和MongoDB容易使用的项目是mango,mango是为Django会话和认证开发的一系列MongoDB库(完全绕过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),所以不能支持所有documents。

python-bsonjs是建立在libbson之上的将BSON快速转换为 MongoDB Extended JSON的转换器。python-bsonjs不依赖于PyMongo,可以提供比json_util更好的性能。python-bsonjs在使用RawBSONDocument时与PyMongo最适合。

在解码另一种语言存储的日期时为什么会出现OverflowError?

PyMongo将BSON日期时间值解码为Python的datetime.datetime实例。datetime.datetime的实例被限制在datetime.MINYEAR(通常为1)和datetime.MAXYEAR(通常为9999)之间。某些MongoDB驱动程序(例如PHP驱动程序)可以存储远远超出datetime.datetime支持的年份值的BSON日期时间。

有几种方法可以解决此问题。 一种选择是过滤掉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实例在子进程中死锁的可能性很高。


作者:白夜
链接:https://juejin.im/post/5a3b1e9c51882515945abedf
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
分享到:
评论

相关推荐

    pymongo实现多结果进行多列排序的方法

    在日常的数据库操作中,对查询结果进行排序是一项非常常见的需求。特别是当需要根据多个字段对数据进行排序时,掌握正确的技巧尤为重要。本文将详细介绍如何使用`pymongo`实现多结果进行多列排序的方法,并深入探讨...

    Python库 | jsonclasses_pymongo-1.2.1-py3-none-any.whl

    4. **CRUD操作简化**:`jsonclasses_pymongo`简化了常见的增删改查操作,使得与MongoDB的交互更加直观。 5. **性能优化**:由于`jsonclasses`和`pymongo`都是经过优化的库,因此在处理大量数据时,性能表现优秀。 ...

    PyPI 官网下载 | pymongo-2.2.1-py2.7-win-amd64.egg

    在Python生态系统中,`.egg`文件是一种常见的软件包分发格式,它允许开发者轻松安装和管理依赖。`pymongo-2.2.1`这个版本的发布,意味着开发者可以利用此驱动程序与MongoDB 2.2.x版本进行交互。 一、pymongo介绍 1...

    PyPI 官网下载 | pymongo-3.0.2-py2.7-macosx-10.10-intel.egg

    2. **数据库操作**:通过`Database`对象,开发者可以执行常见的数据库操作,如创建、删除数据库以及查询其集合。 3. **集合操作**:集合是MongoDB中的数据结构,类似于传统关系数据库中的表。`Collection`类提供了...

    pymongolinux安装包

    `pymongo`的安装过程是Python开发中的常见任务。对于Linux系统,可以使用`pip`这个Python的包管理器来安装。首先确保系统已经安装了Python和`pip`,然后通过以下命令进行安装: ```bash pip install pymongo ``` ...

    PyPI 官网下载 | pymongo-3.12.0-cp36-cp36m-manylinux2014_i686.whl

    pymongo提供了丰富的API,使得Python开发者可以轻松地执行常见的数据库操作,如创建、查询、更新和删除文档,以及处理集合、索引等。 版本号`3.12.0`表示这是pymongo的一个特定版本。软件开发中,版本号通常由主...

    Python的MongoDB模块PyMongo操作方法集锦

    PyMongo提供了一系列方法,使得我们可以方便地执行常见的数据库操作,如数据的增、删、查、改以及索引管理。以下是一些关于PyMongo的基本操作: 1. **导入模块**: 首先,你需要导入`pymongo`模块,这将允许你在...

    python使用pymongo与MongoDB基本交互操作示例

    Python中的pymongo库是与MongoDB数据库进行交互的主要工具,它提供了丰富的API来执行常见的数据库操作,如创建、读取、更新和删除(CRUD)等。在本篇文章中,我们将深入探讨如何使用pymongo进行MongoDB的基本操作。 ...

    pymongo中聚合查询的使用方法

    4. **$match阶段**:这是聚合管道中的一个常见阶段,用于过滤输入文档,只允许匹配特定条件的文档通过。在`pipeline`中,`$match`操作可以使用查询表达式来筛选数据,类似于`find()`函数。 5. **$group阶段**:此...

    Python3中使用PyMongo的方法详解

    上述代码展示了两种常见的连接方式:一种是通过指定主机名和端口号;另一种则是通过提供完整的连接字符串。 #### 指定数据库 成功建立连接后,需要指定要操作的具体数据库。MongoDB允许用户通过不同的数据库组织...

    pymongo-debian:不再开发

    这在软件开发中是常见的做法,尤其是当官方仓库的软件版本落后时。 然而,标签 "Shell" 可能表明这个项目包含了使用 Shell 脚本来管理或构建这个包的元素。这可能涉及到使用诸如 `bash` 或 `sh` 这样的脚本语言来...

    Python与MongoDB是一种常见的搭配,增删改查案例

    Python与MongoDB是一种常见的搭配,因为Python拥有丰富的数据处理库和强大的社区支持,而MongoDB是一个流行的分布式文档型数据库,适合存储非结构化数据。Python的pymongo库使得开发者可以轻松地在Python应用中连接...

    pymongo实现控制mongodb中数字字段做加法的方法

    在MongoDB中,数据操作经常会涉及到对数字字段的增减操作,这在统计、计数等场景中尤其常见。Python的`pymongo`库提供了方便的API来处理这类需求。本篇将深入探讨如何使用`pymongo`实现对MongoDB中的数字字段执行...

    pymongo:在这里,我学习了如何使用python的连接器使用MongoDB数据库

    更新和删除文档也是常见的操作。假设我们要增加`Alice`的年龄: ```python update_query = {"name": "Alice"} new_age = {"$inc": {"age": 1}} # 增加1岁 collection.update_one(update_query, new_age) ``` `update...

    Python最常见的170道面试题全解析答案(二).docx

    **问题60:计算文件中大写字母的数量** 这个面试题考察的是文件读取和字符串处理的基本操作。在Python中,我们可以使用内置的`open()`函数来打开文件,然后通过`read()`方法读取文件内容。`isupper()`是字符串方法,...

    python读取json文件并将数据插入到mongodb的方法

    在Python编程中,将JSON文件的数据插入到MongoDB数据库是一项常见的任务。MongoDB是一个文档型数据库,非常适合存储JSON格式的数据。以下是一个详细的过程,演示如何完成这个任务。 首先,确保你已经安装了必要的库...

    python查询mongodb函数

    Python查询MongoDB函数是Python开发中常见的任务,用于与MongoDB数据库进行交互。MongoDB是一个流行的NoSQL数据库,以其灵活性、高性能和易于使用而著称。在Python中,我们可以使用`pymongo`库来连接、操作MongoDB...

    完整版 Python高级开发课程 高级教程 07 Python操作MongoDB数据库.rar

    9. **错误处理与优化**: 学习如何处理常见的MongoDB和PyMongo错误,以及如何优化查询性能,减少不必要的网络通信。 10. **实战项目**: 通过实际案例,将所学知识应用于实践中,例如构建一个数据分析系统,利用...

    NoSQL与Python学习.pdf

    文档中提到了MongoDB的一些常见错误处理方法,比如日志记录,这对于诊断问题和调试程序非常有帮助。 总结来说,NoSQL与Python的学习涉及对NoSQL数据库类型的理解、MongoDB的配置与管理、Pymongo库的使用以及对...

    Python常见MongoDB数据库操作实例总结

    本文实例讲述了Python常见MongoDB数据库操作。分享给大家供大家参考,具体如下: MongoDB 是一个基于分布式文件存储的数据库。由C++语言编写。旨在为WEB应用提供可扩展的高性能数据存储解决方案。 MongoDB 是一个...

Global site tag (gtag.js) - Google Analytics