django查询优化
使用line_profiler:它告诉我们执行一个函数中的每一行需要多少时间 pip install line_profiler
一:ORM优化:
1.1:使用explain方法
统计一个查询所消耗的执行时间,更好地优化查询结果
Blog.objects.filter(title='My Blog').explain(verbose=True)
1.2:select_related():通过执行一个更复杂的SQL查询来工作,该查询还返回相关对象的字段
使用前:
def get_books_by_author():
books = Book.objects.all()
result = defaultdict(list)
for book in books:
author = book.author
title_and_author = '{} by {}'.format(
book.title,
author.name
)
result[book.library_id].append(title_and_author)
return result
使用后:
def get_books_by_author_select_related():
books = Book.objects.all().select_related('author')
result = defaultdict(list)
for book in books:
author = book.author
title_and_author = '{} by {}'.format(
book.title,
author.name
)
result[book.library_id].append(title_and_author)
return result
In [12]: timeit(get_books_by_author, number=10)
Out[12]: 41.363460485998075
In [13]: timeit(get_books_by_author_select_related, number=10)
Out[13]: 1.2787263889913447
1.3:prefetch_related():
prefetch_related类似于select_related防止不必要的SQL查询。不像select_related一次性获取主要和相关对象,prefetch_related对每种关系进行单独的查询,然后将结果“结合”在一起。这种方法的缺点是它需要多次往返数据库
Author.objects.filter(name__startswith ='R').prefetch('books')
工作原理:首先触发一个请求,该请求运行主查询Author.objects.filter(name__startswith=letter),然后Book.objects.filter(author_id__in=PKS_OF_AUTHORS_FROM_FIRST_REQUEST)执行,最后将两个响应合并到一个查询集中,该查询集中将Author每个作者的书缓存在内存中。因此,您最终得到的结果与相似,select_related但您通过不同的方式到达那里。
1.3:values()和values_list()
将SQL响应序列化为python数据所花费的时间与返回的行数和列数成正比,下面的函数中,即使我们只需要作者名字,书的图书馆id和书籍名称 ,我们也将书和作者模型上所有字段进行序列化了。我们还无缘无故地初始化Django模型实例,尽管我们没有对其进行任何特殊处理
def get_books_by_author_select_related():
books = Book.objects.all().select_related('author')
result = defaultdict(list)
for book in books:
author = book.author
title_and_author = '{} by {}'.format(
book.title,
author.name
)
result[book.library_id].append(title_and_author)
return result
因此,我们产生了相当大的开销,可以通过调用或在queryset上仅询问我们需要的字段来消除这些开销:.values().values_list()
def get_books_by_author_select_related_values():
books = (
Book.objects
.all()
.select_related('author')
.values('title', 'library_id', 'author__name')
)
result = defaultdict(list)
for book in books.iterator():
title_and_author = '{} by {}'.format(
book['title'],
book['author__name']
)
result[book['library_id']].append(title_and_author)
return result
def get_books_by_author_select_related_values_list():
books = (
Book.objects
.all()
.select_related('author')
.values_list('title', 'library_id', 'author__name')
)
result = defaultdict(list)
for book in books.iterator():
title_and_author = '{} by {}'.format(
book[0],
book[2]
)
result[book[1]].append(title_and_author)
return result
values()返回模型实例的字典表示形式的列表:[{'title': 'Snow Crash', 'library_id': 9, 'author__name': 'Neil'}, ...]
values_list()返回表示模型实例的元组的列表[('Snow Crash', 9, 'Neil'), ...]
In [13]: timeit(get_books_by_author_select_related, number=10)
Out[13]: 1.2787263889913447
In [14]: timeit(get_books_by_author_select_related_values, number=1)
Out[14]: 0.19064296898432076
In [15]: timeit(get_books_by_author_select_related_values_list, number=1)
Out[15]: 0.17425400999491103
1.5:bulk_create():批量创建对象,减少SQL查询次数
使用前:
for i in resultlist:
p = Account(name=i)
p.save()
使用后:
querysetlist=[]
for i in resultlist:
querysetlist.append(Account(name=i))
Account.objects.bulk_create(querysetlist)
二:用悲观锁来实现减库存操作:
from django.db import transaction
from django.db.models import F
with transaction.atomic():
product = Product.objects.select_for_update().get(id=101)
if product.storage > 1:
Product.objects.filter(id=101).update(storage=F("storage") - 1)
2.1:get_or_create以及update_or_create, 从函数名称上就可以知道这两个方法到底做了什么, 前者表示有则获取, 无则创建; 后者表示有则更新, 无则创建
object, created = User.objects.get_or_create(username='运维咖啡吧')
返回一个由object和created组成的元组,其中object就是一个查询到的或者是被创建的对象,created是一个表示是否创建了新对象的布尔值
实现方式类似于下边这样:
try:
object= User.objects.get(username='运维咖啡吧')
created =False
exception User.DoesNoExist:
object= User(username='运维咖啡吧')
object.save()
created =True
returenobject, created
执行原生SQL:
Django中能用ORM的就用它ORM吧,不建议执行原生SQL,可能会有一些安全问题,如果实在是SQL太复杂ORM实现不了,那就看看下边执行原生SQL的方法,跟直接使用pymysql基本一致了
from django.db import connection
with connection.cursor() as cursor:
cursor.execute('select * from accounts_User')
row = cursor.fetchall()
return row