Python中用memcached来减少数据库查询次数的教程


Posted in Python onApril 07, 2015

本来我一直不知道怎么来更好地优化网页的性能,然后最近做python和php同类网页渲染速度比较时,意外地发现一个很简单很白痴但是 我一直没发现的好方法(不得不BS我自己):直接像某些php应用比如Discuz论坛那样,在生成的网页中打印出“本页面生成时间多少多少秒”,然后在 不停地访问网页测试时,很直观地就能发现什么操作会导致瓶颈,怎样来解决瓶颈了。

于是我发现SimpleCD在 生成首页时,意外地竟然需要0.2秒左右,真真不能忍:对比Discuz论坛首页平均生成才0.02秒,而Discuz论坛的首页页面无疑比 SimpleCD的主页要复杂不少;这让我情何以堪啊,因为这必然不是Python语言导致的差距,只能说是我完全没做优化而Discuz程序优化得很好 的后果。

其实不用分析也能知道肯定是数据库在拖累,SimpleCD在生成首页时需要在sqlite的三个数据库中进行42多次查询,是历史原因导致的极其低效的一个设计;但是这40多次查询中,其实大部分是非常快的查询,仔细分析一下就有两个是性能大户,其他都不慢。

第一个大户就是:获取数据个数
 

SELECT count(*) FROM verycd

这个操作每次都要花不少时间,这是因为每次数据库都要锁住然后遍历一遍主键统计个数的缘故,数据量越大耗时就越大,耗时为O(N),N为数据库大小;实际 上解决这个问题非常容易,只要随便在哪存一个当前数据的个数,只有在增删数据的时候改动就行了,这样时间就是O(1)的了

第二个大户就是:获取最新更新的20个数据列表
 

SELECT verycdid,title,brief,updtime FROM verycd
 
  ORDER BY updtime DESC LIMIT 20;

因为在updtime上面做了索引,所以其实真正查询时间也就是搜索索引的时间而已。然则为什么这个操作会慢呢?因为我的数据是按照publish time插入的,按update time进行显示的话就肯定需要在至少20个不同的地方做I/O,这么一来就慢了。解决的方法就是让它在一个地方做I/O。也就是,除非数据库加入新数据 /改变原有数据,否则把这条语句的返回结果缓存起来。这么一来又快了20倍:)

接下来的是20条小case:取得发布人和点击数信息
 

SELECT owner FROM LOCK WHERE id=XXXX;
 
SELECT hits FROM stat WHERE id=XXXX;

这里为什么没用sql的join语句来省点事呢?因为架构原因这些数据放在不同的数据库里,stat是点击率一类的数据库,因为需要频繁的插入所以用 mysql存储;而lock和verycd是需要大量select操作的数据库,因为mysql悲剧的索引使用情况和分页效率而存放在了sqlite3数 据库,所以无法join -.-

总之这也不是问题,跟刚才的解决方法一样,统统缓存

所以纵观我这个例子,优化网页性能可以一言以蔽之,缓存数据库查询,即可。我相信大部分网页应用都是这样:)

终于轮到memcached了,既然打算缓存,用文件做缓存的话还是有磁盘I/O,不如直接缓存到内存里面,内存I/O可就快多了。于是memcached顾名思义就是这么个东东。

memcached是很强大的工具,因为它可以支持分布式的共享内存缓存,大站都用它,对小站点来说,只要出得起内存,这也是好东西;首页所需要的内存缓冲区大小估计不会超过10K,更何况我现在也是内存土豪了,还在乎这个?

配置运行:因为是单机没啥好配的,改改内存和端口就行了
 

vi /etc/memcached.conf
 
/etc/init.d/memcached restart

在python的网页应用中使用之
 

import memcache
 
mc = memcache.Client(['127.0.0.1:11211'], debug=0)

memcache其实就是一个map结构,最常使用的就是两个函数了:

  1.     第一个就是set(key,value,timeout),这个很简单就是把key映射到value,timeout指的是什么时候这个映射失效
  2.     第二个就是get(key)函数,返回key所指向的value

于是对一个正常的sql查询可以这么干

sql = 'select count(*) from verycd'
 
c = sqlite3.connect('verycd.db').cursor()
 
 
 
# 原来的处理方式
 
c.execute(sql)
 
count = c.fetchone()[0]
 
 
 
# 现在的处理方式
 
from hashlib import md5
 
key=md5(sql)
 
count = mc.get(key)
 
if not count:
 
  c.execute(sql)
 
  count = c.fetchone()[0]
 
  mc.set(key,count,60*5) #存5分钟

 

其中md5是为了让key分布更均匀,其他代码很直观我就不解释了。

优化过语句1和语句2后,首页的平均生成时间已经降低到0.02秒,和discuz一个量级了;再经过语句3的优化,最终结果是首页生成时间降低到了 0.006秒左右,经过memcached寥寥几行代码的优化,性能提高了3300%。终于可以挺直腰板来看Discuz了)

Python 相关文章推荐
通过python下载FTP上的文件夹的实现代码
Feb 10 Python
python3模拟百度登录并实现百度贴吧签到示例分享(百度贴吧自动签到)
Feb 24 Python
Python基于scrapy采集数据时使用代理服务器的方法
Apr 16 Python
python套接字流重定向实例汇总
Mar 03 Python
Python实现pdf文档转txt的方法示例
Jan 19 Python
深入理解Python 关于supper 的 用法和原理
Feb 28 Python
python删除文件夹下相同文件和无法打开的图片
Jul 16 Python
Python中的self用法详解
Aug 06 Python
TensorFlow Saver:保存和读取模型参数.ckpt实例
Feb 10 Python
浅谈tensorflow使用张量时的一些注意点tf.concat,tf.reshape,tf.stack
Jun 23 Python
Python scrapy爬取小说代码案例详解
Jul 09 Python
Python实现自动整理文件的脚本
Dec 17 Python
Python3中常用的处理时间和实现定时任务的方法的介绍
Apr 07 #Python
Python中使用pprint函数进行格式化输出的教程
Apr 07 #Python
利用QT写一个极简单的图形化Python闹钟程序
Apr 07 #Python
分析Python编程时利用wxPython来支持多线程的方法
Apr 07 #Python
Python中尝试多线程编程的一个简明例子
Apr 07 #Python
Python的Flask框架中Flask-Admin库的简单入门指引
Apr 07 #Python
用Python实现一个简单的线程池
Apr 07 #Python
You might like
关于php fread()使用技巧
2010/01/22 PHP
PHP处理excel cvs表格的方法实例介绍
2013/05/13 PHP
php_imagick实现图片剪切、旋转、锐化、减色或增加特效的方法
2014/12/15 PHP
简单的php+mysql聊天室实现方法(附源码)
2016/01/05 PHP
Laravel框架Eloquent ORM简介、模型建立及查询数据操作详解
2019/12/04 PHP
Yii实现微信公众号场景二维码的方法实例
2020/08/30 PHP
Javascript 中的 && 和 || 使用小结
2010/04/25 Javascript
Javascript 面向对象 继承
2010/05/13 Javascript
javascript面向对象编程(一) 实例代码
2010/06/25 Javascript
js限制文本框只能输入数字方法小结
2014/06/16 Javascript
jQuery固定浮动侧边栏实现思路及代码
2014/09/28 Javascript
Javascript Object 对象学习笔记
2014/12/17 Javascript
javascript中键盘事件用法实例分析
2015/01/30 Javascript
基于jquery animate操作css样式属性小结
2015/11/27 Javascript
jquery插件ajaxupload实现文件上传操作
2015/12/09 Javascript
完美实现八种js焦点轮播图(下篇)
2020/04/20 Javascript
微信小程序 空白页重定向解决办法
2017/06/27 Javascript
微信小程序组件之srcoll-view的详解
2017/10/19 Javascript
解决在vue项目中,发版之后,背景图片报错,路径不对的问题
2018/03/06 Javascript
node跨域转发 express+http-proxy-middleware的使用
2018/05/31 Javascript
JavaScript中的"=、==、==="区别讲解
2019/01/22 Javascript
利用原生JS实现data方法示例代码
2019/05/28 Javascript
javascript实现简单打字游戏
2019/10/29 Javascript
[48:39]Ti4主赛事胜者组第一天 EG vs NEWBEE 2
2014/07/19 DOTA
[05:08]第一届“网鱼杯”DOTA2比赛精彩集锦
2014/09/05 DOTA
Python socket网络编程TCP/IP服务器与客户端通信
2017/01/05 Python
python3.6使用urllib完成下载的实例
2018/12/19 Python
python读写csv文件并增加行列的实例代码
2019/08/01 Python
通俗讲解python 装饰器
2020/09/07 Python
python3中for循环踩过的坑记录
2020/12/14 Python
selenium设置浏览器为headless无头模式(Chrome和Firefox)
2021/01/08 Python
师范生实习自我鉴定
2013/11/01 职场文书
先进事迹报告会感言
2014/01/24 职场文书
文明餐桌行动实施方案
2014/02/19 职场文书
见习报告的格式
2014/10/31 职场文书
2014年艾滋病防治工作总结
2014/12/10 职场文书