Sam*_*dio 16 sql django performance django-queryset
我听说过使用以下内容的建议:
if qs.exists():
...
if qs.count():
...
try:
qs[0]
except IndexError:
...
Run Code Online (Sandbox Code Playgroud)
复制下面的评论:"我正在寻找一个声明,如"在MySQL中,PostgreSQL count()对于短查询更快,exists()对于长查询更快,并且当你很可能使用QuerySet [0]时需要第一个元素,你想检查它是否存在.但是,当count()更快时,它只会稍微快一些,所以建议在两者之间选择时始终使用exists()."
fel*_*lix 16
query.exists()
是最有效的方式.
特别是在postgres count()
上可能非常昂贵,有时比普通的选择查询更昂贵.
exists()
运行没有select_related,字段选择或排序的查询,只获取单个记录.这比使用表连接和排序计算整个查询要快得多.
qs[0]
仍将包括select_related,字段选择和排序; 所以它会更贵.
Django源代码在这里(django/db/models/sql/query.py RawQuery.has_results):
def has_results(self, using):
q = self.clone()
if not q.distinct:
q.clear_select_clause()
q.clear_ordering(True)
q.set_limits(high=1)
compiler = q.get_compiler(using=using)
return compiler.has_results()
Run Code Online (Sandbox Code Playgroud)
前几天得到我的另一个问题是在if语句中调用QuerySet.执行并返回整个查询!
如果变量query_set可能是None
(函数的未设置参数),那么使用:
if query_set is None:
#
Run Code Online (Sandbox Code Playgroud)
不:
if query_set:
# you just hit the database
Run Code Online (Sandbox Code Playgroud)
Sam*_*dio 14
看起来qs.count()和qs.exists()实际上是等价的.因此我没有发现使用exists()而不是count()的原因.后者并不慢,它可以用来检查存在和长度.mem()和count()都可能在MySQL中对同一个查询进行求值.
仅qs[0]
在您确实需要该对象时使用.如果你只是测试存在,它会明显变慢.
在Amazon SimpleDB上,400,000行:
qs
:325.00 usec/passqs.exists()
:144.46 usec/passqs.count()
144.33 usec/passqs[0]
:324.98 usec/pass在MySQL上,57行:
qs
:1.07 usec/passqs.exists()
:1.21 usec/passqs.count()
:1.16 usec/passqs[0]
:1.27 usec/pass我为每个传递使用了随机查询来降低数据库级缓存的风险.测试代码:
import timeit
base = """
import random
from plum.bacon.models import Session
ip_addr = str(random.randint(0,256))+'.'+str(random.randint(0,256))+'.'+str(random.randint(0,256))+'.'+str(random.randint(0,256))
try:
session = Session.objects.filter(ip=ip_addr)%s
if session:
pass
except:
pass
"""
query_variatons = [
base % "",
base % ".exists()",
base % ".count()",
base % "[0]"
]
for s in query_variatons:
t = timeit.Timer(stmt=s)
print "%.2f usec/pass" % (1000000 * t.timeit(number=100)/100000)
Run Code Online (Sandbox Code Playgroud)
@Sam Odio 的解决方案是一个不错的起点,但该方法存在一些缺陷,即:
因此,我没有过滤可能匹配的内容,而是决定排除肯定不匹配的内容,希望仍然避免使用 DB 缓存,但也确保相同的行数。
我只针对本地 MySQL 数据库进行了测试,数据集为:
>>> Session.objects.all().count()
40219
Run Code Online (Sandbox Code Playgroud)
计时码:
import timeit
base = """
import random
import string
from django.contrib.sessions.models import Session
never_match = ''.join(random.choice(string.ascii_uppercase) for _ in range(10))
sessions = Session.objects.exclude(session_key=never_match){}
if sessions:
pass
"""
s = base.format('count')
query_variations = [
"",
".exists()",
".count()",
"[0]",
]
for variation in query_variations:
t = timeit.Timer(stmt=base.format(variation))
print "{} => {:02f} usec/pass".format(variation.ljust(10), 1000000 * t.timeit(number=100)/100000)
Run Code Online (Sandbox Code Playgroud)
输出:
=> 1390.177710 usec/pass
.exists() => 2.479579 usec/pass
.count() => 22.426991 usec/pass
[0] => 2.437079 usec/pass
Run Code Online (Sandbox Code Playgroud)
所以你可以看到它count()
比exists()
这个数据集慢了大约 9 倍。
[0]
也很快,但需要异常处理。
归档时间: |
|
查看次数: |
12009 次 |
最近记录: |