本來我一直不知道怎麼來更好地優化網頁的性能,然後最近做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, DER FROM verydcdo
DESC LIMIT 20;
因為在updtime上面做了索引,所以其實真正查詢時間也就是搜尋索引的時間而已。然則為什麼這個操作會慢呢?因為我的資料是按照publish time插入的,按update time進行顯示的話就肯定需要在至少20個不同的地方做I/O,這麼一來就慢了。解決的方法就是讓它在一個地方做I/O。也就是,除非資料庫加入新資料 /改變原有數據,否則把這條語句的回傳結果快取起來。這麼一來又快了20倍:)
接下來的是20條小case:取得發佈人和點擊數資訊
SELECT owner FROM LOCK WHERE id=XXXX;
XXX
/etc/init.d/memcached restart
/etc/init.d/memcached restart
使用之
import memcache
mc = memcache.Client(['127.0.0.1:11211'], debug=0)
memcache其實了第一個就是set(key,value,timeout),這個很簡單就是把key映射到value,timeout指的是什麼時候這個映射失效
第二個就是get(key)函數,回傳key所指向的value
所以對一個正常的sql查詢可以這麼乾
sql = 'select count(*) from verycd'
c = sqlite3.connect('verycd.db').cursor()
. = c.fetchone()[0]
# 現在的處理方式
from hashlib import md5
key=md5(sql)
count: c.execute(sql)
count = c.fetchone()[0]
mc.set(key,count,60*5) #存5分鐘
程式碼很直觀我就不解釋了。
優化過語句1和語句2後,首頁的平均生成時間已經降低到0.02秒,和discuz一個量級了;再經過語句3的優化,最終結果是首頁生成時間降低到了0.006秒左右,經過memcached寥寥幾行程式碼的優化,效能提高了3300%。終於可以挺直腰板來看Discuz了)