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与asyncio或Tornado一起使用,请参阅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有关于这个决定的一些信息,这里是一个简短的总结:
这将污染文档的属性名称空间,因此当使用与字典方法相同的名称的键时可能导致细微的bugs/混淆错误。
我们使用SON对象而不是常规字典的唯一原因是维护键排序,因为服务器需要这些准确的操作。因此我们在是否需要复杂的SON的必要性上犹豫不决(某种程度上我们希望恢复单独使用字典,而不会破坏每个人的向后兼容性)
因为文档的表现像字典,所以新用户很容易(和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实例在子进程中死锁的可能性很高。
PyMongo 常见问题的更多相关文章
- C++常见笔试面试要点以及常见问题
1. C++常见笔试面试要点: C++语言相关: (1) 虚函数(多态)的内部实现 (2) 智能指针用过哪些?shared_ptr和unique_ptr用的时候需要注意什么?shared_ptr的实现 ...
- SQL Server常见问题介绍及快速解决建议
前言 本文旨在帮助SQL Server数据库的使用人员了解常见的问题,及快速解决这些问题.这些问题是数据库的常规管理问题,对于很多对数据库没有深入了解的朋友提供一个大概的常见问题框架. 下面一些问题是 ...
- 【腾讯优测干货分享】如何降低App的待机内存(二)——规范测试流程及常见问题
本文来自于腾讯优测公众号(wxutest),未经作者同意,请勿转载,原文地址:https://mp.weixin.qq.com/s/806TiugiSJvFI7fH6eVA5w 作者:腾讯TMQ专项测 ...
- mysql数据库开发常见问题及优化
mysql 数据库是被广泛应用的关系型数据库,其体积小.支持多处理器.开源并免费的特性使其在 Internet 中小型网站中的使用率尤其高.在使用 mysql 的过程中不规范的 SQL 编写.非最优的 ...
- 《PDF.NE数据框架常见问题及解决方案-初》
<PDF.NE数据框架常见问题及解决方案-初> 1.新增数据库后,获取标识列的值: 解决方案: PDF.NET数据框架,已经为我们考略了很多,因为用PDF.NET进行数据的添加操作时 ...
- MIS性能优化常见问题与方案(辅助项目组性能优化的总结贴)
最近帮忙公司的几个项目组进行了不同方面的性能优化,发现几个项目都出现了一些共性的问题.这里写一篇文章,总结一下这几类问题,以及其对应的解决方案.方便其它项目组参考. 常见问题一:打开页面非常慢,有 ...
- JMeter常见问题集合
前言 本文内容仅仅是针对Jmeter的部分功能名词的介绍和解释,以及初学者不易理解的问题的整理.部分内容来自别人做的整理,为了更好地整理自己的思路,所以可耻的整理一下发到博客上. 标题[1-6]和[参 ...
- NHibernate常见问题及解决方法
NHibernate常见问题及解决方法 曾经学过NHibernate的,但是自从工作到现在快一年了却从未用到过,近来要巩固一下却发现忘记了许多,一个"in expected: <end ...
- .NET Core中ADO.NET SqlClient的使用与常见问题
一.简介 在很多要求性能的项目中,我们都要使用传统的ADO.NET的方式来完成我们日常的工作:目前有一些网友问有关于.NET Core操作SQL Server的问题在本文中解答一下. 本文旨在指出,在 ...
随机推荐
- JavaMail发送邮件、带附件邮件(完整版)
工程目录如下: 1.准备javaMail需要的两个Jar包:mail.jar.activation.jar,然后add to build path 2.QQ邮箱开启SMTP服务,开启后,它会给你一串授 ...
- PHP多进程处理并行处理任务实例
本文目的 本文通过例子讲解linux环境下,使用php进行并发任务处理,以及如何通过pipe用于进程间的数据同步.写得比较简单,作为备忘录. PHP多进程 通过pcntl_XXX系列函数使用多进程功能 ...
- 不管你是否已经准备面试, 这45道Python面试题都对你非常有帮助!(mark!)
1)什么是Python?使用Python有什么好处? Python是一种编程语言,包含对象,模块,线程,异常和自动内存管理.蟒蛇的好处在于它简单易用,可移植,可扩展,内置数据结构,并且它是一个开源的. ...
- 设置checkBox不拦截焦点
android:clickable="false"android:focusableInTouchMode="false"android:focusable=& ...
- 运行时常量池中的符号引用/String.intern() /ldc指令
运行时常量池,之前放在方法区(永久代)中,1.8之后被转移到元空间,放到了native memory中. 具体的数据结构是:(看对象的内存布局,句柄访问还是对象头中保存指向类的元数据的指针,这里以对象 ...
- 70 多表查询的分组F 聚合 Q 查询
聚合查询和分组查询 聚合 aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典.键的名称是聚合值的标识符,值是计算出来的聚合值.键的名称是按照字段和聚合函 ...
- 多线程串口编程工具CserialPort类(附VC基于MFC单文档协议通讯源程序及详细编程步骤)
老有人觉得MSComm通讯控件很土,更有人大声疾呼:忘了它吧.确实当我们对串口编程有了一定的了解后,应该用API函数写一个属于自己的串口程序,由于编程者对程序了解,对程序修改自如.但我一直没有停止过用 ...
- for循环遍历改用map函数
# for url in urls:# url = response.urljoin(url)# print(url)urls = map(lambda url:response.urljoin(ur ...
- ubuntu 安装 mysql 的正确姿势
1.下载官方提供的mysql-apt-config.deb包进行APT源设置,下载地址:https://dev.mysql.com/downloads/repo/apt/ 2. // 将 mysql- ...
- SQL注入之Sqli-labs系列第十二关
开始挑战第十二关(Error Based- Double quotes- String) 12点半了,不困,继续,继续,继续 先看看页面,通常的使用单引号等进行操作,看看啥么情况先 咦,出现错误信息了 ...