之前三篇講了MoSonic整體設計上的思路參考。這篇講一下其中的一些細節優化方面遇到的問題。
Cache Money對於查詢類型的要求限制的非常死,整個系統變成只有兩種查詢:
- select id from table where simple condition
- select * from table where id = XXX
假設有user表,有password跟is_banned兩列。已知user_name,要如何獲得is_banned?
Cache Money的限制使得必須得有兩個查詢:
- select id from user where user_name=XXX
- select * from user where id=XXX
然後再從第二個查詢裏面的*去獲得is_banned。
有人會認爲這很傻,完全可以用一個: select is_banned from user where user_name=XXX的查詢來替代,實現減少sql查詢次數,以及sql返回的數量兩方面的優化。
這是“優化”是一個很有意思的問題。如果我們要認爲 select is_banned from user where user_name=XXX 是一個優化;首先必須清楚瞭解這條sql中數據庫究竟做了什麼。
首先,要支持where user_name=XXX的查詢,user_name這列是必須加索引的。
那麼,user_name的索引中包含了什麼?這實質上是user_name對user表主鍵(id)的索引!
數據庫要返回is_banned這個數據,實際上它是需要:
- 首先查詢user_name的索引,獲得id的對應的row指針
- 根據指針定位至真實的row,並在row找到is_banned的列的數據,才能返回。
上述的數據庫內部操作,跟cache money的兩個查詢實質上是一一對應的。
唯一不同的是,數據庫查詢是硬盤尋址,而cache money是memcache訪問。
memcache與傳統硬盤誰快?memcache與SSD硬盤誰快?一個memcache集羣與單個SSD物理硬盤誰快?高併發情景下又如何?
我們的初衷是要實現一個分佈式的系統以解決性能問題,但爲什麼突然又要把單個硬盤上的操作視爲一個優化?
有人還會認爲一開始user_name的索引其實建錯了,應該是建user_name + is_banned的聯合索引,這樣數據庫內部在執行select is_banned from user where user_name=XXX 這個查詢的時候,就可以直接在索引數據上獲得is_banned的值,而無需多一次尋址。
這種又是不是優化?
我們先把user表的分佈式放一邊,僅考慮單機的情況;我們假設它是優化。
我們原來的設計中,cache money在典型情況下是緩存了100%的查詢,對於這種新冒出來的查詢,要怎麼緩存?
仔細研究的話,這種緩存其實是可以做的;但查詢類型增加了,緩存類型增加了,相應的緩存更新策略複雜度也會增加。
這裏的實際問題是:在框架中增加這些複雜度/編碼量,緩存佔用,數據庫的索引空間;而換來的查詢優化是否值得?
這其實不可一概而論,是要視業務場景而定。
如果,以用戶名獲得密碼的查詢在產品業務中的調用異常頻繁,甚至就是業務本身100%的調用,那麼神馬編碼複雜度,緩存佔用,索引空間都是浮雲,這是必須付出的代價。
OK,我們決定做不惜一切代價來實現這個優化;這其實又引入了另一個問題:
MoSonic支持的查詢類型是否是優化此業務的唯一選擇?
可不可以在不改變MoSonic現有結構的情況下來實現這樣的優化?
這個碰巧是有現成的案例,人人網,爲了高效的檢查一個用戶帳號是否有效,專門搞了一整臺服務器做中間層緩存所有人人網用戶是否被ban的數據,並提供高效的網絡接口供系統其他模塊調用。
具體可查閱人人網中間層:實踐篇。
所以,答案是可以。
MoSonic本身是作爲一個通用的底層ORM框架,它的分佈式支持,緩存支持,都是透明的;但也不強制開發者必須使用;對Cache Money不符合的業務場景,那麼是可以把緩存禁用掉;開發者根據自己業務特性去尋求更貼切的緩存方案。
設計一個通用的數據庫訪問方案,實質上並不應該對特定業務做特定場景下的優化,而應該留出來空間,讓開發者在遇到特殊場景時有實現特定優化的可能。
如果說,MoSonic把cache money這層透明緩存做成強制的,開發者連禁用某個表的緩存控制都沒有,那纔是設計的問題。
上述,實際上僅討論了一個小細節在單機場景下的情況,同一個問題如果延伸至分佈式的場景,也會引發很多很有意思的話題,這篇就先不累述了。