什么,用redis替換mysql?瘋了吧!
拒絕總得有理有據(jù):
- redis作為內(nèi)存數(shù)據(jù)庫在體量上是容納不了磁盤數(shù)據(jù)庫的,完全替換是不可能也沒必要
- 業(yè)務服務通常邏輯復雜,如何實現(xiàn)復雜的sql查詢。磁盤數(shù)據(jù)庫一句sql查詢搞定的事,我得準備多少key/value的數(shù)據(jù)關(guān)系才能實現(xiàn)查詢。
- 以上都不是問題的情況下,一旦發(fā)生數(shù)據(jù)變更,key/value的數(shù)據(jù)關(guān)系如何同步
以上三個問題足以讓我對redis替換mysql望而卻步了。如此多的業(yè)務關(guān)系時時變更,同樣作為內(nèi)存數(shù)據(jù)庫,我寧愿選擇mongodb,至少它在某種層面上支持類sql的條件查詢。即使只有兩三張表的數(shù)據(jù)的業(yè)務服務,完全用redis替換mysql的工作量也不能小覷。完全手寫是不可能的,所以任務要求是通過ezorm自動生成代碼的方式來實現(xiàn)redis替換mysql。難度再度提升,代碼必須通用化才能用工具生成。
其實,到我開始編寫代碼的那一刻,我都不知道支持redis的orm會做成啥樣。當然,還是從最基礎的增刪改查做起(這是大部分orm提供的主要功能)減少程序員重復的編碼工作。
當然我也不是完全從零開始,我已經(jīng)有了基礎ezorm工具,它已經(jīng)支持 mssql、mysql和mongo了。所以,增加redis支持是理所當然的了。當然如果只是對象的增刪改查,難度到也不大。
第一版本
不同于關(guān)系型數(shù)據(jù)庫,增刪改查的接口是:insert/update/delete/select,redis的增刪改查只需要提供兩個接口就好了,set/get。為了簡化代碼,我提供了一套object接口,只要實現(xiàn)了object接口的對象可以直接通過反射操作進行對象的redis存取操作,這樣一來,我只需要讓對象實現(xiàn)接口函數(shù)即可。這就是我第一版做的事情。

當然這個版本,我還提供了一個關(guān)鍵的對象定義屬性,就是 dbs 屬性。之前的orm版本中,一個對象定義只能存在一個db屬性定義,對于redis對象而言,完全可能和關(guān)系型數(shù)據(jù)對象具有相同的結(jié)構(gòu)。如果再去生成一套對象結(jié)構(gòu)定義顯然是不合理的。
第一個版本還實現(xiàn)查詢接口,支持按索引查詢。雖然粗糙了點,至少看起來和關(guān)系數(shù)據(jù)庫提供的接口接近了。

第二版本
很明顯,在第一版本中沒有發(fā)揮orm的優(yōu)勢就自動生成代碼將一切影響效率的操作遷移至編譯階段。而對象的反射顯然除了加快了開發(fā)進度,簡化代碼。在關(guān)鍵的在執(zhí)行效率上大打折扣。于是乎第二版本我移除了所有反射操作,通過orm模板的關(guān)鍵特性自動生成對象字段的讀寫操作。于是生成的代碼就變成了以下形式:

第三版本
雖然在前兩個版本,我就針對redis提供的數(shù)據(jù)結(jié)構(gòu)進行功能劃分:
通用對象存儲(object)
對象關(guān)系存儲(relation)
其中,對象的存儲在redis里可以使用以下結(jié)構(gòu)
<key/value> pair 用于json對象存儲
hash用于需要獨立設置字段的對象
而對于對象關(guān)系的存儲redis提供幾種類型都可以支持:
<key/value> pair
set 集合結(jié)構(gòu)
zset有序集合結(jié)構(gòu)
geo地理位置結(jié)構(gòu)
list列表結(jié)構(gòu)
其實在前兩個版本中關(guān)系對象(relation)的存儲該如何定義一直是件困擾我的事,既需要能過通過某種形式定義出來,又不能讓用戶隨意定義。
通用對象的定義用戶是可以完全自定義對象的列(Field)的,但關(guān)系對象(Relation)如何定義呢?先看一下,redis中幾種類型的具體操作命令:

前兩個版本的做法是同樣利用之前通用對象的定義方法讓用戶定義字段,但其實redis對set/zset/geo/list都有嚴格的特殊要求,除了key和value外,在zset中增加的score,在geo中增加了longtitude和latitude。前兩版本的做法就是去驗證用戶定義關(guān)系對象的字段數(shù)與字段名稱,顯然是很傻的做法,在實際應用中必定坑洼不斷。
所以在這個版本中對關(guān)系對象類型的定義進行了抽象,既然所有關(guān)系對象結(jié)構(gòu)均有key和value字段,而key字段顯然都是string類型,我只需要增加一個新的定義屬性:valuetype類型。通過這個屬性結(jié)合模型的storetype來定義關(guān)系對象即可:

有了valuetype后,我考慮到如果能夠直接通過關(guān)系對象找到對象豈不完美,因為value對于使用者而言還必須進行一次轉(zhuǎn)化。所以這時我又增加了modeltype類型,對于關(guān)系對象的定義中valuetype剛好是modeltype定義的主鍵類型的話,那么直接在生成代碼的時候就生成相應接口豈不妙哉。

第四版本
其實完成了第三個版本后,我就躍躍欲試想小試牛刀了。但是問題也就來了,主要是:
- 基礎對象的數(shù)據(jù)準備
- 關(guān)系對象的數(shù)據(jù)準備
同時,雖然我可以定義以上兩種對象,但是實際如何替換現(xiàn)有查詢語句我仍然一頭霧水?;A對象的查詢很簡單,只要有id就可以查詢和關(guān)系型orm操作接口類似。
基礎對象的數(shù)據(jù)準備也很簡單,對象只有dbs同時支持mysql和redis,就可以通過mysql對象提供的查詢接口將所有對象取出來,再枚舉調(diào)用redis對象的set接口就可以完成基礎對象的數(shù)據(jù)準備。
而對于關(guān)系對象的數(shù)據(jù)準備,肯定最好的方式也是從數(shù)據(jù)庫直接導入最直接。所以就想到了一個新的屬性就是:importSQL屬性。

這個屬性一度讓我興奮不已。因為它不但幫我實現(xiàn)了對象的數(shù)據(jù)準備問題,還幫我解決了對象的數(shù)據(jù)同步問題。增加這個屬性后,對象相應增加了以下接口:

現(xiàn)在,可以真正開始使用ezorm的redis功能了,看看實際使用的代碼吧。
總結(jié)

接下來,我們應該做什么呢?請關(guān)注新項目redis-orm. 我們將完成索引的模擬。提前告知下模擬索引的功能:
