常见问题解答
本页内容
- 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 系统上,multiprocessing 模块通过使用 fork()
方法来创建进程。这具有与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中连接池是如何工作的?
每个MongoClient
实例为其MongoDB拓扑中的每个服务器都有一个内置的连接池。连接池按需打开套接字以支持应用程序中对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 服务器文档中的 连接池概述。
为什么 PyMongo 会为我所有的文档添加一个 _id 字段?
当您使用 Collection.insert_one()
方法、Collection.insert_many()
方法或 Collection.bulk_write()
方法将文档插入 MongoDB,并且该文档没有包含 _id
字段时,PyMongo 会自动为您添加此字段。它还将字段的值设置为 ObjectId
实例。
以下代码示例将一个没有 _id
字段的文档插入 MongoDB,然后打印该文档。在插入后,该文档包含一个值为 ObjectId
实例的 _id
字段。
'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 v3.4 引入了 Decimal128
BSON 类型,这是一个 128 位的基于十进制的浮点值,能够以精确精度模拟十进制舍入。PyMongo 版本 3.4 及更高版本也支持此类型。然而,早期版本的 MongoDB 只支持 IEEE 754 浮点数,相当于 Python 的 float
类型。PyMongo 只能将这些版本的 MongoDB 存储为 Decimal
实例,通过将它们转换为 float
类型。您必须显式执行此转换。
有关更多信息,请参阅PyMongo API文档中关于 decimal128。
为什么PyMongo将9.99
转换为9.9900000000000002
?
MongoDB将9.99
表示为IEEE浮点值,这不能精确地表示该值。在某些版本的Python中也是如此。在这方面,PyMongo的行为与JavaScript shell、所有其他MongoDB驱动程序以及Python语言本身相同。
PyMongo支持文档的属性样式访问吗?
不。PyMongo没有实现此功能,以下是一些原因
添加属性会污染文档的属性命名空间,可能导致在使用与字典方法同名键时出现细微的错误或令人困惑的错误。
PyMongo只使用SON对象而不是常规字典来维护键排序,因为服务器需要这在某些操作中使用。添加此功能将使
SON
类更加复杂,并且如果PyMongo将来恢复使用字典,可能会破坏向下兼容性。文档的行为就像字典一样,这使得它们对新PyMongo用户来说相对简单易懂。改变文档的行为为这些用户增加了入门障碍。
如需更多信息,请参阅相关的 Jira 工单。
PyMongo 支持 Asynchronous Frameworks 吗?
是的。有关更多信息,请参阅第三方工具指南。
PyMongo 与 mod_wsgi 兼容吗?
是的。请参阅工具指南中的 mod_wsgi。
PyMongo 与 PythonAnywhere 兼容吗?
不兼容。PyMongo 创建 Python 线程,而 PythonAnywhere 不支持。
有关更多信息,请参阅相关的 Jira 工作单。
如何将文档编码为 JSON?
PyMongo 支持一些特殊类型,例如 ObjectId
和 DBRef
,这些类型在 JSON 中不受支持。因此,Python 的 json
模块不能与 PyMongo 中的所有文档一起使用。相反,PyMongo 包含了 json_util 模块,这是一个用于使用 Python 的 json
模块与 BSON 文档和 MongoDB 扩展 JSON。
python-bsonjs 是另一个基于 libbson 的 BSON 到 MongoDB 扩展 JSON 转换器。python-bsonjs 不依赖 PyMongo,在某些情况下可能比 json_util
提供更好的性能提升。
提示
当使用 RawBSONDocument
类型时,python-bsonjs 与 PyMongo 的配合最好。
PyMongo 在 Python 3 中的行为是否有所不同?
PyMongo 将 bytes
类的实例编码为 BSON 类型 5(二进制数据)子类型 0。在 Python 2 中,这些实例被解码为 Binary
子类型 0。在 Python 3 中,它们被解码回 bytes
。
以下代码示例使用 PyMongo 将 bytes
实例插入 MongoDB,然后查找该实例。在 Python 2 中,字节字符串被解码为 Binary
。在 Python 3 中,字节字符串被解码回 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 中,这些值被解码为 Binary
的实例,子类型为 0。在 Python 3 中,它们被解码为 bytes
的实例。
以下代码示例使用 json_util
模块解码具有子类型 0 的 JSON 二进制值。在 Python 2 中,字节字符串被解码为 Binary
。在 Python 3 中,字节字符串被解码回 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 将 ObjectId
实例进行 pickle,你总是可以用 Python 3 解开它。要做到这一点,你必须将 encoding='latin-1'
选项传递给 pickle.loads()
方法。以下代码示例展示了如何在 Python 2.7 中将 ObjectId
进行 pickle,然后在 Python 3.7 中解开它。
# 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 中 pickle 了 ObjectID
,并且想在 Python 3 中解开它,你必须将 protocol
参数的值设置为 2
或更小,传递给 pickle.dumps()
方法。以下代码示例展示了如何在 Python 3.7 中将 ObjectId
进行 pickle,然后在 Python 2.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')