先說說自己對Memcache和Mongodb的一些看法,主要是拋磚引玉了,希望看到大家的意見和補充。
Memcache
Memcache的優勢我覺得總結下來主要體現在:
1) 分布式。可以由10台擁有4G記憶體的機器,構成一個40G的記憶體池,如果覺得還不夠大可以增加機器,這樣一個大的記憶體池,完全可以把大部分熱點業務資料儲存進去,由記憶體來阻擋大部分對資料庫讀的請求,對資料庫釋放可觀的壓力。
2) 單點。如果Web伺服器或App伺服器做負載平衡的話,在各自記憶體中儲存的緩衝可能各不相同,如果資料需要同步的話,比較麻煩(各自自己到期,還是分發資料同步?),即使資料並不需要同步,使用者也可能因為資料的不一致而產生使用者體驗上的不友好。
3) 效能強。不用懷疑和資料庫相比確實是,根源上還是記憶體的讀寫和磁碟讀寫效率上幾個數量級的差距。有的時候我們在抱怨資料庫讀寫太差的情況下可以看看磁碟的IO,如果確實是瓶頸的話裝啥強勁的資料庫估計也檔不了,強不強無非是這個資料庫多少充分的利用了記憶體。
但是也不太建議在任何情況下使用Memcache替代任何緩衝:
1) 如果Value特別大,不太適合。因為在預設編譯下Memcache只支援1M的Value(Key的限制到不是最大的問題)。其實從實踐的角度來說也不建議把非常大的資料儲存在Memcache中,因為有序列化還原序列化的過程,別小看它消耗的CPU。說到這個就要提一下,我一直覺得Memcache適合面向輸出的內容緩衝,而不是面向處理的資料緩衝,也就是不太適合把大塊資料放進去拿出來處理之後再放進去,而是適合拿出來就直接給輸出了,或是拿出來不需要處理直接用。
2) 如果不允許到期,不太適合。Memcache在預設情況下最大30天到期,而且在記憶體達到使用限制後它也會回收最少使用的資料。因此,如果我們要把它當作static變數的話就要考慮到這個問題,必須有重新初始化資料的過程。其實應該這麼想,既然是緩衝就是拿到了存起來,如果沒有必定有一個重新擷取重新緩衝的過程,而不是想著它永遠存在。
在使用Memcache的過程中當然也會有一些問題或者說最佳實務:
1) 清除部分資料的問題。Memcache只是一個Key/Value的池,一個公用汽車誰都可以上。我覺得對於類似的公用資源,如果用的人都按照自己的規則來的話很容易出現問題。因此,最好在Key值的規範上上使用類似命名空間的概念, 每一個使用者都能很明確的知道某一塊功能的Key的範圍,或者說首碼。帶來的好處是我們如果需要清空的話可以根據這個規範找到我們自己的一批Key然後再去清空,而不是清空所有的。當然有人是採用版本升級的概念,老的Key就讓它過去吧,到時候自然會清空,這也是一種辦法。不過Key有規範總是有好處的,在統計上也方便一點。
2) Value的組織問題。也就是說我們存的資料的粒度,比如要儲存一個列表,是一個儲存在一個索引值還是統一儲存為一個索引值,這取決於業務。如果粒度很小的話最好是在擷取的時候能批量擷取,在儲存的時候也能批量儲存。對於跨網路的調用次數越少越好,可以想一下,如果一個頁面需要輸出100行資料,每一個資料都需要擷取一次,一個頁面進行上百次串連這個效能會不會成問題。
那麼Memcache主要用在哪些功能上呢?
其實我覺得平時能想到在記憶體中做緩衝的地方我們都可以考慮下是不是可以去適用分布式緩衝,但是主要的用途還是用來在前端或中部擋一下讀的需求來釋放Web伺服器App伺服器以及DB的壓力。
Mongodb
Mongodb是一款比較優良的非關係型資料庫的文檔型的資料庫。它的優勢主要體現在:
1) 開源。意味著即使我們不去改也可以充分挖掘它,MS SQL除了看那些文檔,誰又知道它內部如何?。
2) 免費。意味著我們可以在大量垃圾伺服器上裝大量的執行個體,即使它效能不怎麼高,也架不住非常多的點啊。
3) 效能高。其它沒比較過,和MS SQL相比,同樣的應用(主要是寫操作)一個撐500使用者就掛了,一個可以撐到2000。在資料量上到百萬之後,即使沒索引,MS SQL的插入效能下降的也一塌糊塗。其實任何事物都有相對性的,在變得複雜變得完善了之後會犧牲一部分的效能,MS SQL體現的是非常強的安全性資料完整性,這點是Mongodb辦不到的。
4) 配置簡單並且靈活。在生產環境中對資料庫配置容錯移轉叢集和讀寫分離的資料庫複寫是很常見的需求,MS SQL的配置繁瑣的步驟還是很恐怖的,而Mongodb可以在五分鐘之內配置自己所需要的容錯移轉組,讀寫分離更是只需要一分鐘。靈活性體現在,我們可以配置一個M一個S,兩個M一個S(兩個M寫入的資料會合并到S上供讀取),一個M兩個S(一個M寫入的資料在兩個S上有鏡像),甚至是多個M多個S(理論上可以建立10個M,10個S,我們只需要通過輪詢方式隨便往哪個M上寫,需要讀的時候也可以輪訓任意一個S,當然我們要知道不可能保證在同一時間所有的S都有一致的資料)。那麼也可以配置兩個M的對作為一套容錯移轉叢集,然後這樣的群集配置兩套,再對應兩個S,也就是4個M對應2個S,保證M點具有容錯移轉。
5) 使用靈活。在之前的文章中我提到甚至可以通過SQL到JS運算式的轉換讓Mongodb支援SQL語句的查詢,不管怎麼說Mongodb在查詢上還是很方便的。
之前也說過了,並不是所有資料庫應用都使用採用Mongodb來替代的,它的主要缺點是:
1) 開源軟體的特點:更新快,應用工具不完善。由於更新快,我們的用戶端需要隨著它的更新來升級才能享受到一些新功能,更新快也意味著很可能在某一階段會缺乏某個重要功能。另外我們知道MS SQL在DEV/DBA/ADM多個維度都提供了非常好的GUI工具對資料庫進行維護。而Mongodb雖然提供了一些程式,但是並不是非常友好。我們的DBA可能會很鬱悶,去最佳化Mongodb的查詢。
2) 操作事務。Mongodb不支援內建的事務(沒有內建事務不意味著完全不能有事務的功能),對於某些應用也就不適合。不過對於大部分的互連網應用來說並不存在這個問題。
在使用Mongodb的過程中主要遇到下面的問題:
1) 真正的橫向擴充?在使用Memcache的過程中我們已經體會到這種爽了,基本可以無限的增加機器來橫向擴充,因為什麼,因為我們是通過用戶端來決定索引值儲存在那個執行個體上,在擷取的時候也很明確它在哪個執行個體上,即使是一次性擷取多個索引值,也是同樣。而對於資料庫來說,我們通過各種各樣的方式進行了Sharding,不說其它的,在查詢的時候我們根據一定的條件擷取批量的資料,怎麼樣去處理?比如我們按照使用者ID去分區,而查詢根本不在乎使用者ID,在乎的是使用者的年齡和教育程度,最後按照姓名排序,到哪裡去取這些資料?不管是基於用戶端還是基於服務端的Sharding都是非常難做的,並且即使有了自動化的Sharding效能不一定能有保障。最簡單的是盡量按照功能來分,再下去就是曆史資料的概念,真正要做到即時資料分散在各個節點,還是很困難。
2) 多線程,多進程。在寫入速度達不到預期的情況下我們多開幾個線程同時寫,或者多開幾個Mongodb進程(同一機器),也就是多個資料庫執行個體,然後向不同的執行個體去寫。這樣是否能提高效能?很遺憾,非常有限,甚至可以說根本不能提高。為什麼使用memcache的時候多開線程可以提高寫入速度?那是因為記憶體資料交換的瓶頸我們沒達到,而對於磁碟來說,IO的瓶頸每秒那麼幾十兆的是很容易達到的,一旦達到這個瓶頸了,無論是開多少個進程都無法提高效能了。還好Mongodb使用記憶體映射,看到記憶體使用量的多了,其實我對它的信心又多了一點(記憶體佔用多了我覺得CPU更容易讓它不閑著),怕就怕某個DB不使用什麼記憶體,看著IO瓶頸到了,記憶體和CPU還是吃不飽。
Memcache和Mongodb的配合
其實有了Memcache和Mongodb我們甚至可以讓80%以上的應用擺脫傳統關係型資料庫。我能想到它們其實可以互相配合彌補對方的不足:
Memcache適合根據Key儲存Value,那麼有的時候我們並不知道需要讀取哪些Key怎麼辦呢?我在想是不是可以把Mongodb或說資料庫當作一個未經處理資料,這份未經處理資料中分為需要查詢的欄位(索引欄位)和普通的資料欄位兩部分,把大量的非查詢欄位儲存在Memcache中,小粒度儲存,在查詢的時候我們查詢資料庫知道要擷取哪些資料,一般查詢頁面也就顯示20-100條吧,然後一次性從Memcache中擷取這些資料。也就是說,Mongodb的讀的壓力主要是索引欄位,而資料欄位只是在緩衝失效的時候才有用,使用Memcache擋住大部分實質資料的查詢。反過來說,如果我們要清空Memcache中的資料也知道要清空哪些Key。