常见问题解答
在此页面上
- 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 线程安全吗?
是的。 PyMongo 具有线程安全性,并为线程应用程序提供内置连接池。
PyMongo 分叉安全吗?
不可以。如果使用 fork()
方法创建新进程,请勿将MongoClient
类的实例从父进程传递给子进程。 这很可能在子进程中的MongoClient
实例之间造成死锁。 相反,请在子进程中创建一个新的MongoClient
实例。
注意
如果可能发生这种死锁,PyMongo 会尝试发出警告。
我能否将 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
类的实例从父进程复制到子进程。
PyMongo 能否将查询结果作为 Pandas DataFrame 加载?
您可以使用 PyMongoArrow 库处理数值或柱状数据。PyMongoArrow 允许您将MongoDB查询结果集加载为 Pandas DataFrames 、 NumPy ndarrays 或Apache Arrow Tables 。
PyMongo 中的连接池是如何工作的?
对于 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文档中的连接池概述。
为什么 PyMongo 向我的所有文档添加 _id 字段?
当您使用Collection.insert_one()
方法、 Collection.insert_many()
方法或Collection.bulk_write()
方法将文档插入 MongoDB 中,并且该文档不包含_id
字段时,PyMongo 会自动为您添加此字段。 它还将该字段的值设置为ObjectId
的实例。
以下代码示例将没有_id
字段的文档插入 MongoDB,然后打印该文档。 插入后,文档包含一个_id
字段,其值是ObjectId
的实例。
'x': 1} my_doc = { 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()
方法。
如何存储Decimal
实例?
MongoDB v 3.4 引入了Decimal128
BSON 类型,这是一种基于128位十进制的浮点值,能够以精确的精度模拟十进制舍入。 PyMongo 版本3.4及更高版本也支持此类型。 但是,早期的 MongoDB 版本仅支持 IEEE 754浮点数,相当于 Python float
类型。 PyMongo 只能将Decimal
实例存储到这些版本的 MongoDB 中,方法是将它们转换为float
类型。 您必须显式执行此转换。
有关更多信息,请参阅 十进制128 的 PyMongo API 文档。
为什么PyMongo 转换为9.99
9.9900000000000002
?
MongoDB 将9.99
表示为 IEEE 浮点值,该值无法精确表示该值。 在某些版本的 Python 中也是如此。 在这方面,PyMongo 的行为与 JavaScript shell、所有其他 MongoDB 驱动程序以及 Python 语言本身相同。
PyMongo 是否支持文档的属性式访问?
否。PyMongo 未实现此功能,原因如下:
添加属性会污染文档的属性命名空间,并且在使用与字典方法同名的键时,可能会导致细微的错误或混淆性错误。
PyMongo 使用 SON 对象而不是常规字典来维护键顺序,因为服务器需要它来执行某些操作。 添加此功能会使
SON
类变得复杂,并且如果 PyMongo 恢复使用字典,则可能会破坏向后兼容性。文档的行为就像字典一样,这使得 PyMongo 新用户相对容易理解。 更改文档行为为这些用户增加了进入障碍。
有关更多信息,请参阅相关 Jira 案例。
PyMongo 是否支持异步框架?
是的。 有关更多信息,请参阅第三方工具指南。
PyMongo 是否可以与 mod_wsgi 一起使用?
是的。请参阅工具指南中的mod_wsgi 。
PyMongo 是否可以与 PythonAnywhere 一起使用?
不会。PyMongo 会创建 Python 线程,而 PythonAnywhere 不支持。
有关更多信息,请参阅相关的 Jira 工单。
如何将文档编码为 JSON?
PyMongo 支持一些在 JSON 中不支持的特殊类型,例如ObjectId
和DBRef
。 因此,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 在 Python 3中的行为是否不同?
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()'binary': b'this is a byte string'}).inserted_id c.test.bintest.insert_one({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()'binary': b'this is a byte string'}).inserted_id c.test.bintest.insert_one({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 '{"b": {"$binary": "dGhpcyBpcyBhIGJ5dGUgc3RyaW5n", "$type": "00"}}') loads({u'b': Binary('this is a byte string', 0)}
from bson.json_util import loads '{"b": {"$binary": "dGhpcyBpcyBhIGJ5dGUgc3RyaW5n", "$type": "00"}}') loads({'b': b'this is a byte string'}
我是否可以在 Python 2和 Python 3之间共享 Pickled ObjectId?
如果您使用 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() oidObjectId('4f919ba2fba5225b84000000') pickle.dumps(oid)'ccopy_reg\n_reconstructor\np0\n(cbson.objectid\...' # Python 3.7 import pickle b'ccopy_reg\n_reconstructor\np0\n(cbson.objectid\...', encoding='latin-1') pickle.loads(ObjectId('4f919ba2fba5225b84000000')
如果您在 Python 2中对ObjectID
进行了 pickle,并想在 Python 3中对其进行取消,则必须将值不超过2
的protocol
参数传递给pickle.dumps()
方法。 以下代码示例展示了如何在 Python 3.7中 pickle ObjectId
,然后在 Python 2中取消 pickle。 7 :
# Python 3.7 import pickle from bson.objectid import ObjectId oid = ObjectId() oidObjectId('4f96f20c430ee6bd06000000') 2) pickle.dumps(oid, protocol=b'\x80\x02cbson.objectid\nObjectId\nq\x00)\x81q\x01c_codecs\nencode\...' # Python 2.7 import pickle '\x80\x02cbson.objectid\nObjectId\nq\x00)\x81q\x01c_codecs\nencode\...') pickle.loads(ObjectId('4f96f20c430ee6bd06000000')