30分鐘3300%性能提高——python+memcached網頁優化小記

 轉自:http://obmem.info/?p=717php

原本我一直不知道怎麼來更好地優化網頁的性能,而後最近作python和php同類網頁渲染速度比較時,意外地發現一個很簡單很白癡可是我一直沒發現的好方法(不得不BS我本身):直接像某些php應用好比Discuz論壇那樣,在生成的網頁中打印出「本頁面生成時間多少多少秒」,而後在不停地訪問網頁測試時,很直觀地就能發現什麼操做會致使瓶頸,怎樣來解決瓶頸了。python

 因而我發現SimpleCD在生成首頁時,意外地居然須要0.2秒左右,真真不能忍:對比Discuz論壇首頁平均生成才0.02秒,而Discuz論壇的首頁頁面無疑比SimpleCD的主頁要複雜很多;這讓我情何以堪啊,由於這必然不是Python語言致使的差距,只能說是我徹底沒作優化而Discuz程序優化得很好的後果。mysql

優化分析

其實不用分析也能知道確定是數據庫在拖累,SimpleCD在生成首頁時須要在sqlite的三個數據庫中進行42屢次查詢,是歷史緣由致使的極其低效的一個設計;可是這40屢次查詢中,其實大部分是很是快的查詢,仔細分析一下就有兩個是性能大戶,其餘都不慢。sql

第一個大戶就是:獲取數據個數shell

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

總之這也不是問題,跟剛纔的解決方法同樣,通通緩存

因此縱觀我這個例子,優化網頁性能能夠一言以蔽之,緩存數據庫查詢,便可。我相信大部分網頁應用都是這樣:)

Memcached終於出場

終於輪到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結構,最常使用的就是兩個函數了:

  • 第一個就是set(key,value,timeout),這個很簡單就是把key映射到value,timeout指的是何時這個映射失效
  • 第二個就是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了:)

說memcached是妖孽,並非由於memcached應用了以後性能狂升——這本是意料之中的事情,不這樣反而才奇怪——而是由於我基本上沒花多少時間就實現了這麼妖孽的效果,至少我花的時間並不比寫這篇blog的時間多。那麼方便的使用方法和那麼顯著的性能提高情況真是讓人咋舌。

相關文章
相關標籤/搜索