在使用django在web项目开发中避免不了会进行性能优化,最近特意总结了一些我用到过的性能优化方法。都是实际开发过程中非常有用的技巧,希望能够对大家有所帮助。
性能分析工具
cprofile
from cProfile import Profile
import pstats
prof = Profile()
prof.enable()
# 运行函数
run_func()
prof.create_stats()
p = pstats.Stats(prof)
p.print_callees()
运行程序,可以看到每行代码执行所花费的时间
line_profiler
在函数名上添加@profile装饰器,然后使用
kernprof -l -v loopdemo.py
django-debug-toolbar
Django Debug Toolbar是Django开发中必备利器,可以帮助开发者快速了解项目的整体信息以及每个页面包括sql信息,http相关信息.
pip install django-debug-toolbar
-
打开项目文件夹settings.py 文件, 把"debug_toolbar"加到INSTALLED_APP里去。
-
打开项目文件夹里的urls.py, 把debug_toolbar的urls加进去。
from django.conf import settings
from django.conf.urls import include, url
from django.urls import include, path
if settings.DEBUG:
import debug_toolbar
urlpatterns = [
path('__debug__/', include(debug_toolbar.urls)),
] + urlpatterns
- 在settings.py里添加中间件
MIDDLEWARE = [
# ...
'debug_toolbar.middleware.DebugToolbarMiddleware',
# ...
]
- 在settings.py设置本地IP, debug_toolbar只能在localhost本地测试环境下运行。
INTERNAL_IPS = [
# ...
'127.0.0.1',
# ...
]
代码优化
# 使用values和values_list
Author.objects.values("name")
Author.objects.values_lsit('name')
# 使用 defer() 和 only() 来避免加载不需要的数据列
Author.objects.only("name")
Author.objects.defer("id", "age", "author_detail")
# objs.count()来替代len(objs),book.author_id代替book.author.id
# 尽量使用单语句查询,多使用annotate和aggregate聚合函数查询。以及filter=Q(field=value)的子查询条件。
Author.objects.values('age').annotate(male_sum=Sum('num', filter=Q(sex='male')))
# 使用select_related和prefetch_related
# select_related对正向查询和一对多的外键查询结果进行缓存
Book.objects.select_related('foreign').all()
# prefetch_related对反向查询和多对多的查询结果进行缓存
Author.objects.prefetch_related('book_set').all()
# 批量插入
Author.objects.bulk_create([Entry(name='a'),Entry(name='b'),])
# 查询集的惰性缓存
objs.iterator() 和 objs.exists()方法来防止生成cache
多任务
线程池
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
def run(num):
time.sleep(num)
return num
def main():
# 开启线程池
with ThreadPoolExecutor(max_workers=5) as t:
obj_list = []
for numin range(1, 5):
obj = t.submit(spider, num)
obj_list.append(obj)
for future in as_completed(obj_list):
# 获取线程执行结果
data = future.result()
进程池
ProcessPoolExecutor 使用同线程池,只不过进程池的使用依赖于__main__主程序入口
协程
import asyncio
import requests
import time
start = time.time()
async def get(url):
return requests.get(url)
async def request():
url = 'https://www.baidu.com/'
# await后面要跟 coroutine 对象
response = await get(url)
def callback(task):
print('Status:', task.result())
# 得到一个croutine对象
coroutine = request()
# 创建一个task任务
task = asyncio.ensure_future(coroutine)
# 将task作为参数传递至回调函数中
task.add_done_callback(callback)
# 创建5个协程任务
tasks = [asyncio.ensure_future(request()) for _ in range(5)]
# 注册循环事件
loop = asyncio.get_event_loop()
# 将协程添加到循环事件中并执行
loop.run_until_complete(asyncio.wait(tasks))
# 不使用回调函数被我们依然可以使用task.result()来获取结果
for task in tasks:
print(task.result())
针对不同的业务场景,我们需要分析影响性能的代码瓶颈在哪里,从而有针对性的做出相应的解决。