文档首页 → 开发应用程序 → Python 驱动程序 → 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 ObjectIds 吗?
PyMongo 是否线程安全?
是的。PyMongo 是线程安全的,并为线程应用程序提供了内置的连接池。
PyMongo 是否进程安全?
不是的。如果您使用 fork()
方法创建一个新进程,不要将父进程中的 MongoClient
类实例传递给子进程。这会使得子进程中的 MongoClient
实例之间发生死锁的可能性很高。相反,在子进程中创建一个新的 MongoClient
实例。注意
是的。然而,在 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 DataFrame、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 服务器文档中的 administration/connection-pool-overview/。
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 v3.4 引入了 Decimal128
BSON 类型,这是一个 128 位基于十进制的浮点值,能够以精确精度模拟十进制舍入。PyMongo 版本 3.4 及以后的版本也支持此类型。然而,早期版本的 MongoDB 只支持 IEEE 754 浮点数,相当于 Python 的 float
类型。PyMongo 只能通过将它们转换为 float
类型将这些版本的 MongoDB 中的 Decimal
实例存储。您必须显式执行此转换。
有关更多信息,请参阅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 支持异步框架吗?
是的。有关更多信息,请参阅第三方工具指南。
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 是另一个将 BSON 转换为 MongoDB 扩展 JSON 的工具,它建立在 libbson 之上。python-bsonjs 不依赖于 PyMongo,在某些情况下可能比 json_util
提供更好的性能。
提示
当使用 RawBSONDocument
类型时,python-bsonjs 与 PyMongo 配合使用效果最佳。
PyMongo 在 Python 3 中表现是否不同?
PyMongo 将 bytes
类型的实例编码为 BSON 类型 5,子类型 0。在 Python 2 中,这些实例被解码为子类型 0 的 Binary
。在 Python 3 中,它们被解码回 bytes
。
以下代码示例使用 PyMongo 将一个 bytes
实例插入到 MongoDB 中,然后找到该实例。在 Python 2 中,字节字符串被解码为 Binary
。在 Python 3 中,字节字符串被解码回 bytes
。
同样,Python 2 和 3 在 PyMongo 解析具有子类型 0 的 JSON 二进制值时表现不同。在 Python 2 中,这些值被解码为子类型 0 的 Binary
实例。在 Python 3 中,它们被解码为 bytes
实例。
以下代码示例使用 json_util
模块解码具有子类型 0 的 JSON 二进制值。在 Python 2 中,字节字符串被解码为 Binary
。在 Python 3 中,字节字符串被解码回 bytes
。
我可以在 Python 2 和 Python 3 之间共享序列化的 ObjectId 吗?
如果您使用Python 2来序列化ObjectId
实例,您始终可以使用Python 3反序列化它。为此,您必须将encoding='latin-1'
选项传递给pickle.loads()
方法。以下代码示例演示了如何在Python 2.7中序列化ObjectId
,然后在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中序列化了ObjectID
,并希望在Python 3中反序列化它,您必须将protocol
参数的值设置为2
或更低,传递给pickle.dumps()
方法。以下代码示例演示了如何在Python 3.7中序列化ObjectId
,然后在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')