Docs 菜单
Docs 主页
/ / /
pymongo

常见问题解答

在此页面上

  • PyMongo 线程安全吗?
  • PyMongo 分叉安全吗?
  • 我能否将 PyMongo 与多处理结合使用?
  • PyMongo 能否将查询结果作为 Pandas DataFrame 加载?
  • PyMongo 中的连接池是如何工作的?
  • 为什么 PyMongo 向我的所有文档添加 _id 字段?
  • 如何更改游标的超时值?
  • 如何存储 Decimal实例?
  • 为什么 PyMongo 会将9.99转换为9.9900000000000002
  • PyMongo 是否支持文档的属性式访问?
  • PyMongo 是否支持异步框架?
  • PyMongo 是否可以与 mod_wsgi 一起使用?
  • PyMongo 是否可以与 PythonAnywhere 一起使用?
  • 如何将文档编码为 JSON?
  • PyMongo 在 Python 3中的行为是否不同?
  • 我是否可以在 Python 2和 Python 3之间共享 Pickled ObjectId?

是的。 PyMongo 具有线程安全性,并为线程应用程序提供内置连接池。

不可以。如果使用 fork()方法创建新进程,请勿将MongoClient类的实例从父进程传递给子进程。 这很可能在子进程中的MongoClient实例之间造成死锁。 相反,请在子进程中创建一个新的MongoClient实例。

注意

如果可能发生这种死锁,PyMongo 会尝试发出警告。

是的。 但是,在 Unix 系统上,多处理模块使用fork()方法生成进程。 这会带来与 PyMongo 分叉安全吗?中描述的相同风险

要在 PyMongo 中使用多处理,请编写类似于以下示例的代码:

# 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()

重要

请勿将MongoClient类的实例从父进程复制到子进程。

您可以使用 PyMongoArrow 库处理数值或柱状数据。PyMongoArrow 允许您将MongoDB查询结果集加载为 Pandas DataFrames NumPy ndarrays 或Apache Arrow Tables 。

对于 MongoDB 拓扑结构中的每个服务器,每个 MongoClient 实例都有一个内置连接池。连接池按需打开套接字以支持应用程序中对 MongoDB 的并发请求。

每个连接池的最大大小由 maxPoolSize 选项设置,默认值为 100。如果某个服务器的在用连接数达到 maxPoolSize 值,则对该服务器的下一个请求将等待,直到出现可用连接。

除了支持应用程序请求所需的套接字之外,每个 MongoClient 实例还为 MongoDB 拓扑结构中的每个服务器再打开两个套接字,用于监视服务器的状态。例如,连接到三节点副本集的客户端会打开六个监控套接字。如果应用程序使用 maxPoolSize 的默认设置,并且仅查询主(默认)节点,则连接池中的总连接数最多可以为 106。如果应用程序使用读取偏好来查询从节点,则这些连接池会增长,总连接数可以为 306

要在一个进程中支持大量并发 MongoDB 请求,可以增加 maxPoolSize

连接池的速率受到了限制。maxConnecting 选项将决定连接池在任何时候可以并行创建的连接数量。例如,如果 maxConnecting 的值为 2,则仅当发生以下情况之一时,尝试同时签出连接的第三个请求才会成功:

  • 连接池完成创建连接,且池中连接数少于 maxPoolSize

  • 现有连接会重新检入池中。

  • 由于对创建连接的速率有限制,驱动程序重用现有连接的能力得到了提高。

您可以使用 minPoolSize 选项设置每个服务器的最小并发连接数,默认值为 0。驱动程序会用这个数量的套接字初始化连接池。如果套接字关闭,导致套接字总数(包括使用中的和闲置的)下降到最小值以下,则会打开更多套接字,直到达到最小值。

您可以通过设置 maxIdleTimeMS 选项来设置一个连接在连接池中保持空闲状态的最大毫秒数。一旦 maxIdleTimeMS 的连接处于空闲状态,连接池就会将其删除并替换。此选项默认为 0(无限制)。

以下 MongoClient 的默认配置适用于大多数应用程序:

client = MongoClient(host, port)

MongoClient 支持多个并发请求。为每个流程创建一个客户端,并重复用于流程中的所有操作。这种做法比为每个请求创建一个客户端更有效率。

该驱动程序不限制可以等待套接字变为可用状态的请求数量,应用程序有责任在负载峰值期间将其池的大小限制为绑定队列。请求等待的时间为 waitQueueTimeoutMS 选项中指定的时间,默认为 0(无限制)。

如果请求等待套接字的时间超过waitQueueTimeoutMS定义的时长,则会引发ConnectionFailure错误。 如果在负载峰值期间限制操作的持续时间比完成每个操作更重要,请使用此选项。

MongoClient.close() 被任何请求调用时,驱动程序会关闭所有空闲套接字,并关闭所有正在使用的套接字,因为这些套接字会返回到池中。调用 MongoClient.close() 只能关闭不活动的套接字,因此使用此方法无法中断或终止任何正在进行的操作。只有当进程结束时,驱动程序才会关闭这些套接字。

有关更多信息,请参阅MongoDB Server文档中的连接池概述

当您使用Collection.insert_one()方法、 Collection.insert_many()方法或Collection.bulk_write()方法将文档插入 MongoDB 中,并且该文档不包含_id字段时,PyMongo 会自动为您添加此字段。 它还将该字段的值设置为ObjectId的实例。

以下代码示例将没有_id字段的文档插入 MongoDB,然后打印该文档。 插入后,文档包含一个_id字段,其值是ObjectId的实例。

>>> my_doc = {'x': 1}
>>> collection.insert_one(my_doc)
InsertOneResult(ObjectId('560db337fba522189f171720'), acknowledged=True)
>>> my_doc
{'x': 1, '_id': ObjectId('560db337fba522189f171720')}

PyMongo 以这种方式添加_id字段有几个原因:

  • 所有 MongoDB 文档都必须有一个_id字段。

  • 如果 PyMongo 插入的文档没有_id字段,MongoDB 会自行添加一个字段,但不会将该值报告给 PyMongo 供应用程序使用。

  • 对于大多数高写入量应用程序来说,在添加_id字段之前复制文档的成本过高。

提示

如果您不希望 PyMongo 在文档中添加_id ,则仅插入应用程序已添加_id字段的文档。

MongoDB 不支持游标的自定义超时,但您可以关闭游标超时。 为此,请将no_cursor_timeout=True选项传递给find()方法。

MongoDB v 3.4 引入了Decimal128 BSON 类型,这是一种基于128位十进制的浮点值,能够以精确的精度模拟十进制舍入。 PyMongo 版本3.4及更高版本也支持此类型。 但是,早期的 MongoDB 版本仅支持 IEEE 754浮点数,相当于 Python float类型。 PyMongo 只能将Decimal实例存储到这些版本的 MongoDB 中,方法是将它们转换为float类型。 您必须显式执行此转换。

有关更多信息,请参阅 十进制128 的 PyMongo API 文档。

MongoDB 将9.99表示为 IEEE 浮点值,该值无法精确表示该值。 在某些版本的 Python 中也是如此。 在这方面,PyMongo 的行为与 JavaScript shell、所有其他 MongoDB 驱动程序以及 Python 语言本身相同。

否。PyMongo 未实现此功能,原因如下:

  1. 添加属性会污染文档的属性命名空间,并且在使用与字典方法同名的键时,可能会导致细微的错误或混淆性错误。

  2. PyMongo 使用 SON 对象而不是常规字典来维护键顺序,因为服务器需要它来执行某些操作。 添加此功能会使SON类变得复杂,并且如果 PyMongo 恢复使用字典,则可能会破坏向后兼容性。

  3. 文档的行为就像字典一样,这使得 PyMongo 新用户相对容易理解。 更改文档行为为这些用户增加了进入障碍。

有关更多信息,请参阅相关 Jira 案例。

是的。 有关更多信息,请参阅第三方工具指南。

是的。请参阅工具指南中的mod_wsgi

不会。PyMongo 会创建 Python 线程,而 PythonAnywhere 不支持。

有关更多信息,请参阅相关的 Jira 工单。

PyMongo 支持一些在 JSON 中不支持的特殊类型,例如ObjectIdDBRef 。 因此,Python 的json模块不适用于 PyMongo 中的所有文档。 相反,PyMongo 包含 json_util 模块,用于将 Python 的json 模块与 BSON 文档和 MongoDB 扩展 JSON 结合使用的工具。

python-bsonjs 是另一个 BSON-to-MongoDB-Extended-JSON 转换器,构建在 libbson 之上 。python-bsonjs 不依赖于 PyMongo,在某些情况下可能比json_util提供更高的性能。

提示

当使用RawBSONDocument类型时, python-bsonjs 与 PyMongo 配合使用效果最佳。

PyMongo 将bytes类的实例编码为具有子类型0的 BSON 类型5 (二进制数据)。 在 Python 2中,这些实例被解码为具有子类型0 Binary 在 Python 3中,它们被解码回bytes

以下代码示例使用 PyMongo 将bytes实例插入 MongoDB,然后查找该实例。 在Python 2 中,字节string解码为 Binary。 在Python 3 中,字节string被解码回 bytes

>>> import pymongo
>>> c = pymongo.MongoClient()
>>> c.test.bintest.insert_one({'binary': b'this is a byte string'}).inserted_id
ObjectId('4f9086b1fba5222021000000')
>>> c.test.bintest.find_one()
{u'binary': Binary('this is a byte string', 0), u'_id': ObjectId('4f9086b1fba5222021000000')}
>>> import pymongo
>>> c = pymongo.MongoClient()
>>> c.test.bintest.insert_one({'binary': b'this is a byte string'}).inserted_id
ObjectId('4f9086b1fba5222021000000')
>>> c.test.bintest.find_one()
{'binary': b'this is a byte string', '_id': ObjectId('4f9086b1fba5222021000000')}

同样,当 PyMongo 解析子类型为0的 JSON 二进制值时,Python 2和3的行为也不同。 在 Python 2中,这些值被解码为具有子类型0的Binary实例。 在 Python 3中,它们被解码为bytes的实例。

以下代码示例使用json_util模块对子类型为0的 JSON 二进制值进行解码。 在Python 2 中,字节string解码为 Binary。 在Python 3 中,字节string被解码回 bytes

>>> from bson.json_util import loads
>>> loads('{"b": {"$binary": "dGhpcyBpcyBhIGJ5dGUgc3RyaW5n", "$type": "00"}}')
{u'b': Binary('this is a byte string', 0)}
>>> from bson.json_util import loads
>>> loads('{"b": {"$binary": "dGhpcyBpcyBhIGJ5dGUgc3RyaW5n", "$type": "00"}}')
{'b': b'this is a byte string'}

如果您使用 Python 2 pickle ObjectId的实例,则始终可以使用 Python 3将其取消 pickle。 为此,必须将encoding='latin-1'选项传递给pickle.loads()方法。 以下代码示例展示了如何在 Python 2.7中 pickle ObjectId ,然后在 Python 3.7中取消 pickle:

# Python 2.7
>>> import pickle
>>> from bson.objectid import ObjectId
>>> oid = ObjectId()
>>> oid
ObjectId('4f919ba2fba5225b84000000')
>>> pickle.dumps(oid)
'ccopy_reg\n_reconstructor\np0\n(cbson.objectid\...'
# Python 3.7
>>> import pickle
>>> pickle.loads(b'ccopy_reg\n_reconstructor\np0\n(cbson.objectid\...', encoding='latin-1')
ObjectId('4f919ba2fba5225b84000000')

如果您在 Python 2中对ObjectID进行了 pickle,并想在 Python 3中对其进行取消,则必须将值不超过2protocol参数传递给pickle.dumps()方法。 以下代码示例展示了如何在 Python 3.7中 pickle ObjectId ,然后在 Python 2中取消 pickle。 7 :

# Python 3.7
>>> import pickle
>>> from bson.objectid import ObjectId
>>> oid = ObjectId()
>>> oid
ObjectId('4f96f20c430ee6bd06000000')
>>> pickle.dumps(oid, protocol=2)
b'\x80\x02cbson.objectid\nObjectId\nq\x00)\x81q\x01c_codecs\nencode\...'
# Python 2.7
>>> import pickle
>>> pickle.loads('\x80\x02cbson.objectid\nObjectId\nq\x00)\x81q\x01c_codecs\nencode\...')
ObjectId('4f96f20c430ee6bd06000000')

后退

第三方工具