1、背景 redis
最近一朋友作社區重構,社區主要功能有發帖、回帖、查看帖子詳情,詳情頁按不一樣條件展現回帖(除了預先定義的順序外,可能每一個用戶看到的順序都不同,組合超過100個),大概的效果以下: sql
之前用的是開源的代碼,存儲用的是Mysql,系統也過於臃腫,稍微有點流量系統響應就很慢,因此準備重構。數據庫
重構後的方案以下後端
一、存儲仍是Mysql;緩存
二、爲了提升訪問速度,引入MongoDB做爲緩存(爲何不用Redis,由於MongoDB多線程,可擴充性好,而且支持較複雜的查詢) 多線程
Mysql數據表大概以下: 運維
上述表格是通過簡化版的內容。spa
存儲方面,Mysql存了全量的帖子和帖子回覆,MongoDB也存了全量的帖子和帖子回覆,之因此這麼設計是由於讓用戶帖子詳情頁不用訪問數據庫,提升訪問速度。線程
那爲何只保存在MongoDB裏呢,由於MongoDB不支持多表事務,社區的場景插入回覆,還有其它邏輯須要處理,因此須要藉助Mysql的InnoDB的事務機制保證數據的一致性。設計
重構後訪問帖子詳情頁順序以下:
一、根據帖子id從MongoDB獲取帖子詳情信息,包括標題、內容及發帖時間和發帖人,若是讀取不到,直接報錯;
二、根據帖子id及當前條件從MongoDB獲取帖子回覆信息,一樣讀取不到也報錯。
爲何不按分頁將每一個帖子按頁緩存回覆呢,由於前面說了整個詳情頁展現條件很是複雜,能夠倒序排,也可升序排,還能夠只看做者,有的回覆還有權限,若是所有緩存帖子回覆列表,則緩存的數據量很是的大。
2、問題分析
通過分析,這樣的設計帶來幾個問題:
一、系統設計比較複雜,由於要保證數據在Mysql、MongoDB中一致,須要作不少的代碼進行數據覈對、檢查;
二、系統可用性差,由於帖子詳情頁所有讀取的是MongoDB,因此若是MongoDB掛了,則整個系統也就掛了,特別是MongoDB對於運維團隊還不是特別熟悉的狀況下。
有什麼更好的方案呢,回到緩存的本質,關於緩存的使用有很多模式,通常來講對緩存不要強依賴,即緩存掛了,整個系統不要掛,讓系統打到後端存儲而且更新緩存,這樣還有最後一道防線,而在這個案例中,將MongoDB當存儲用了,而且同時使用兩個存儲。
若是當緩存用,怎麼解決帖子詳情頁多種組合條件的致使緩存數據太大的問題?其實對於社區這樣的場景,主要佔內存的是回覆的內容,只要保證帖子回覆內容只緩存一份就能夠了。
改進後帖子詳情頁邏輯以下:
一、根據帖子id從 MongoDB中獲取帖子詳情信息,若是獲取不到,則從Mysql中獲取,而且寫回到MongoDB中;
二、根據帖子id從MongoDB中獲取當頁須要展現的帖子回覆id,讀取不到再從Mysql回源,並寫回到MongoDB中;根據上面獲取的回覆id再從MongoDB中獲取回覆的詳情,一樣若是獲取不到則從Mysql回源,而且寫入到MongoDB中。
固然在添加、更新回覆後,也須要更新相應的回覆內容,這樣就保證了帖子回覆只緩存一份,不會形成緩存的數據量過大的問題。還有很是重要的一點,整個系統沒有對緩存強依賴,即便MongoDB掛了,系統還會從Mysql讀取數據。最後系統的代碼也變得很是簡潔。
固然這裏還有不少細節須要注意,像如何避免同一時間大量的回源Mysql的問題,這些業內已經有標準的方案,就不在此展開討論了。
3、案例總結
一、系統設計越簡單越好;
二、不要強依賴緩存;