近段时间给side project “二十次幂”做了一些优化调整,UI做了很多减法,只留下了“程序员”、“互联网”、“财经资讯”三个栏目,也是我关注比较多方向。UI的布局也做了调整,从原来的3栏变成了2栏,将更多空间留给了中间内容模块。
第二个变化是调整了内容的展示策略,之前的原则是首页只展示原创内容,这样就错过了很多科技动态资讯内容,调整后,策略变成了原创和非原创都会展示,但是原创文章会加上标签。
第三个是系统系能上的优化,刚上线的那会儿访问首页不感觉慢,因为还没什么数据,但是随着数据越来越多,首页的响应时间达到了2秒,对用户来说来说这是一个无法容忍的等待时间。
于是开启了性能优化之道。
性能优化这块,其实 Flask 是有完善工具支持的,像 werkzeug 就直接集成了 cprofile 模块,所以只要简单设置好配置参数就能将每个函数的调用耗时情况打印出来。
# settings.py
PROFILE=True
# app.py
app.wsgi_app = ProfilerMiddleware(app.wsgi_app, restrictions=[30])
这个SQL查询这么慢是因为连接了远程数据库(实际上如果是内网查询没那么夸张)
从截图看得出消耗的时间主要集中在数据库查询,虽然知道是数据库查询慢,但是不知道是哪条查询语句,这时可以通过配置SQLALCHEMY的参数,将慢查询的语句打印出来:
# setting.py
SQLALCHEMY_RECORD_QUERIES = True
DATABASE_QUERY_TIMEOUT = 0.5 # 请求超时阀值
慢查询的SQL在回调函数 slow_query
里面记录,在请求完成后执行。
from flask_sqlalchemy import get_debug_queries
@app.after_request
def slow_query(response):
for query in get_debug_queries():
if query.duration >= app.config['DATABASE_QUERY_TIMEOUT']:
app.logger.warning("SLOW QUERY: %s\nParameters: %s\nDuration: %fs\nContext: %s\n" % (
query.statement, query.parameters, query.duration, query.context))
return response
设置超时时间是0.5秒,凡是时间超过0.5秒,SQL日志就将打印出来,最后查出来是获取文章列表的查询非常慢,
[2019-04-20 01:38:27,817] WARNING in __init__: SLOW QUERY: SELECT news_article.id AS news_article_id,
FROM article INNER JOIN account ON account.id = article.account_id
WHERE account.activate = %s AND article.status = %s AND article.is_ad = %s AND article.is_first = %s ORDER BY article.published_at DESC
LIMIT %s, %s
Parameters: (1, 1, 0, 1, 0, 20)
Duration: 0.954003s
用 explain
获取SQL的执行计划,这个查询涉及到5个查询条件和一个关联查询,尝试给涉及到的查询条件加上组合索引,效果有所改善,但我不希望这个频繁使用的表每次都做复杂的查询。
于是重新设计了一个新的方案,将首页推荐数据专门用一张新表来存储,数据通过定时任务队列来生成,因为写入推荐表的时候已经根据条件进行了过滤,所以首页数据获取的时候不需要加任何查询条件。这样就将查询时间从原来的3秒减少到1s。当然这个时间大部分都花在网络传输上。如果是换成局域网或者同一台主机,查询时间在10ms以内。
优化完之后,首页的加载时间从原来的2秒缩减到到的平均200ms。
后续还会继续优化,也会去探索商业化的东西,对标的网站主要有readhub、新榜等平台,还是有想象空间的,因为目前还是我一个人负责全站的开发,进度缓慢。所以想找一位擅长前端开发的同学一起继续完善网站,欢迎有兴趣的同学加我私聊(如果你是Python之禅的老读者更好)
关于 MySQL 数据库优化强烈推荐一本书 《高性能MySQL》
推荐阅读:
欢迎关注并置顶公众号,方便第一时间收看最新内容