Kan-Ru Chen's Weblog

Emesene & SQL Optimization

最近都是用 emesene 上 MSN,不過他的啟動速度實在是非常慢,啟動的時候硬碟燈狂閃,判斷應該是在讀取聯絡人的資料快取。先用 python profiler 跑過一次,結果果然是 Logger.py 裡面從 sqlite 撈資料的函式在慢。

大小約 17MB 的 db,每個 query 竟然都要一秒以上,難怪 emesene 啟動要這麼久了。就來幫 emesene 最佳化一下 sql 吧。

以底下這個 query 為例:

    select e.stamp,
           ue.data 
      from event e,
           user_event ue,
           user u
     where e.id      = ue.id_event and
           u.id      = ue.id_user and
           e.name    = "status-changed" and
           u.account = "%s"
     order by e.stamp desc
     limit 1

結果是

    1221129396.22343|NLN
    CPU Time: user 1.173822 sys 0.023997

用 explain query plan 看看

    0|1|TABLE user_event AS ue
    1|0|TABLE event AS e USING PRIMARY KEY
    2|2|TABLE user AS u USING PRIMARY KEY

應該是先把 user_event 與 event, user 三個 table join 在一起,最後再檢查 e.name 與 u.account。

來看看三個 table 的大小

這樣 join 起來是多大的 table 呀

先試試改寫 from clause 裡的順序

    select e.stamp, ue.data 
      from user u, event e, user_event ue
     where e.id      = ue.id_event and
           u.id      = ue.id_user and
           e.name    = "status-changed" and
           u.account = "%s"
     order by e.stamp desc
     limit 1

結果是

    1221129396.22343|NLN
    CPU Time: user 0.485926 sys 0.008998

很好,時間少了一半,explain query plan 顯示

    0|2|TABLE user_event AS ue
    1|0|TABLE user AS u USING PRIMARY KEY
    2  |1|TABLE event AS e USING PRIMARY KEY

如果幫 user_event 建個 index 呢?

    create index ueiduser on user_event(id_user);

再 query 一次

    1221129396.22343|NLN
    CPU Time: user 0.017997 sys 0.004999

快了非常多吧!explain query plan 顯示:

    0|0|TABLE user AS u
    1|1|TABLE user_event AS ue WITH INDEX ueiduser
    2|2|TABLE event AS e USING PRIMARY KEY

這是因為 sqlite 會以 from clause 的第一個當作 outer loop,所以我把數量最小的 user 移到前面,再適當的建立 index,使得 query 的速度快了 100 倍...

參考

Patch 在此

Emesene svn trunk 已經收錄最新 patch :)