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

本来我一直不知道怎么来更好地优化网页的性能,然后最近做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了)

(0)

相关推荐

  • python中使用mysql数据库详细介绍

    一.安装mysql 如果是windows 用户,mysql 的安装非常简单,直接下载安装文件,双击安装文件一步一步进行操作即可. Linux 下的安装可能会更加简单,除了下载安装包进行安装外,一般的linux 仓库中都会有mysql ,我们只需要通过一个命令就可以下载安装: Ubuntu\deepin 复制代码 代码如下: >>sudo apt-get install mysql-server >>Sudo apt-get install  mysql-client centOS/

  • Python bsddb模块操作Berkeley DB数据库介绍

    bsddb模块是用来操作bdb的模块,bdb是著名的Berkeley DB,它的性能非常好,mysql的存储后端引擎都支持bdb的方式.这里简单介绍一些关于bsddb的使用方法. bdb不同于一般的关系数据库,它存储的数据只能是以key和value组成的一对数据,使用就像python的字典一样,它不能直接表示多个字段,当要存储多个字段的数据时,只能把数据作为一个整体存放到value中. 使用bsddb面临的第一问题是使用什么数据访问方法,bdb支持四种:btree, hash, queue, r

  • Python操作MongoDB数据库PyMongo库使用方法

    引用PyMongo 复制代码 代码如下: >>> import pymongo 创建连接Connection 复制代码 代码如下: >>> import pymongo >>> conn = pymongo.Connection('localhost',27017) 或 复制代码 代码如下: >>> from pymongo import Connection >>> conn = Connection('local

  • python连接MySQL数据库实例分析

    本文实例讲述了python连接MySQL数据库的方法.分享给大家供大家参考.具体实现方法如下: import MySQLdb conn = MySQLdb.connect(host="localhost", user="root", passwd="123456", db="test") cursor = conn.cursor() cursor.execute("select * from hard")

  • Python连接mssql数据库编码问题解决方法

    python一直对中文支持的不好,最近老遇到编码问题,而且几乎没有通用的方案来解决这个问题,但是对常见的方法都试过之后,发现还是可以解决的,下面总结了常用的支持中文的编码问题(这些方法中可能其中一个就能解决问题,也可能是多个组合). (1).首先,要保证文件的开头要加上编码设置来说明文件的编码 复制代码 代码如下: #encoding=utf-8 (2).然后,在连接数据的连接参数里加上字符集说明查询出的结果的编码,这个不加的后果可能是查询出的汉字字符都是问号 复制代码 代码如下: conn=p

  • Python操作CouchDB数据库简单示例

    安装python couchDb库: https://pypi.python.org/pypi/CouchDB/0.10 连接服务器 复制代码 代码如下: >>> import couchdb >>> couch = couchdb.Server('http://example.com:5984/') 创建数据库 复制代码 代码如下: >>> db = couch.create('test') # 新建数据库 >>> db = cou

  • 在Python中编写数据库模块的教程

    在一个Web App中,所有数据,包括用户信息.发布的日志.评论等,都存储在数据库中.在awesome-python-app中,我们选择MySQL作为数据库. Web App里面有很多地方都要访问数据库.访问数据库需要创建数据库连接.游标对象,然后执行SQL语句,最后处理异常,清理资源.这些访问数据库的代码如果分散到各个函数中,势必无法维护,也不利于代码复用. 此外,在一个Web App中,有多个用户会同时访问,系统以多进程或多线程模式来处理每个用户的请求.假设以多线程为例,每个线程在访问数据库

  • Python MySQLdb模块连接操作mysql数据库实例

    mysql是一个优秀的开源数据库,它现在的应用非常的广泛,因此很有必要简单的介绍一下用python操作mysql数据库的方法.python操作数据库需要安装一个第三方的模块,在http://mysql-python.sourceforge.net/有下载和文档. 由于python的数据库模块有专门的数据库模块的规范,所以,其实不管使用哪种数据库的方法都大同小异的,这里就给出一段示范的代码: #-*- encoding: gb2312 -*- import os, sys, string impo

  • Python实现数据库编程方法详解

    本文实例讲述了Python实现数据库编程方法.分享给大家供大家参考.具体分析如下: 用PYTHON语言进行数据库编程, 至少有六种方法可供采用. 我在实际项目中采用,不但功能强大,而且方便快捷.以下是我在工作和学习中经验总结. 方法一:使用DAO (Data Access Objects) 这个第一种方法可能会比较过时啦.不过还是非常有用的. 假设你已经安装好了PYTHONWIN,现在开始跟我上路吧-- 找到工具栏上ToolsàCOM MakePy utilities,你会看到弹出一个Selec

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

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

  • Python的Django框架实现数据库查询(不返回QuerySet的方法)

    一.创建模型类: # -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models class Course(models.Model): """课程表""" name = models.CharField(verbose_name='课程名称', max_length=255) description = models.Tex

  • python+tkinter+mysql做简单数据库查询界面

    目录 一.准备工作: 二.代码: 三.界面 四.总结 一.准备工作: 1.安装mysql3.7,创建一个test数据库,创建student表,创建列:(列名看代码),创建几条数据 (以上工作直接用navicat for mysql工具完成) 二.代码: import sys import tkinter as tk import mysql.connector as sql #--------------------查询函数--------------------------- def sql_

  • MySQL中用通用查询日志找出查询次数最多的语句的教程

    MySQL开启通用查询日志general log mysql打开general log之后,所有的查询语句都可以在general log文件中以可读的方式得到,但是这样general log文件会非常大,所以默认都是关闭的.有的时候为了查错等原因,还是需要暂时打开general log的(本次测试只修改在内存中的参数值,不设置参数文件). general_log支持动态修改: mysql> select version(); +-----------+ | version() | +------

  • 在Python中用has_key()方法查找键是否存在的教程

    如果给定的键在字典可用,has_key()方法返回true,否则返回false. 语法 以下是has_key()方法的语法: dict.has_key(key) 参数 key -- 这是要搜索在字典中的键. 返回值 此方法返回true,如果给定键在字典可用,否则返回false. 例子 下面的例子显示了has_key()方法的使用. #!/usr/bin/python dict = {'Name': 'Zara', 'Age': 7} print "Value : %s" % dict.

  • 在Python中用get()方法获取字典键值的教程

    get()方法返回给定键的值.如果键不可用,则返回默认值None. 语法 以下是get()方法的语法: dict.get(key, default=None) 参数 key -- 这是要搜索在字典中的键. default -- 这是要返回键不存在的的情况下默认值. 返回值 该方法返回一个给定键的值.如果键不可用,则返回默认值为None. 例子 下面的例子显示了get()方法的使用. #!/usr/bin/python dict = {'Name': 'Zara', 'Age': 27} prin

  • Spring AOP实现Redis缓存数据库查询源码

    应用场景 我们希望能够将数据库查询结果缓存到Redis中,这样在第二次做同样的查询时便可以直接从redis取结果,从而减少数据库读写次数. 需要解决的问题 操作缓存的代码写在哪?必须要做到与业务逻辑代码完全分离. 如何避免脏读? 从缓存中读出的数据必须与数据库中的数据一致. 如何为一个数据库查询结果生成一个唯一的标识?即通过该标识(Redis中为Key),能唯一确定一个查询结果,同一个查询结果,一定能映射到同一个key.只有这样才能保证缓存内容的正确性 如何序列化查询结果?查询结果可能是单个实体

  • 用实例详解Python中的Django框架中prefetch_related()函数对数据库查询的优化

    实例的背景说明 假定一个个人信息系统,需要记录系统中各个人的故乡.居住地.以及到过的城市.数据库设计如下: Models.py 内容如下: from django.db import models class Province(models.Model): name = models.CharField(max_length=10) def __unicode__(self): return self.name class City(models.Model): name = models.Ch

  • Python 操作mysql数据库查询之fetchone(), fetchmany(), fetchall()用法示例

    本文实例讲述了Python 操作mysql数据库查询之fetchone(), fetchmany(), fetchall()用法.分享给大家供大家参考,具体如下: demo.py(查询,取出一条数据,fetchone): from pymysql import * def main(): # 创建Connection连接 conn = connect(host='localhost',port=3306,user='root',password='mysql',database='jing_do

  • python 数据库查询返回list或tuple实例

    MySQLdb默认查询结果都是返回tuple,输出时候不是很方便,必须按照0,1这样读取,无意中在网上找到简单的修改方法,就是传递一个cursors.DictCursor就行. 默认程序: import MySQLdb db = MySQLdb.connect(host='localhost', user='root', passwd='123456', db='test') cur = db.cursor() cur.execute('select * from user') rs = cur

随机推荐