疑問:為何要使用服務端緩存氏豌?
1.對熱點數(shù)據進行緩存,可以加快響應速度
2.高并發(fā)热凹,大流量這種問題怎么解決泵喘?加機器抗泪电,用緩存抗,優(yōu)化算法纪铺。相速。。
3.降低對數(shù)據服務器的壓力
(老鷹抓小雞)鲜锚,一句話----不惜一切代價將流量攔截突诬,攔截,再攔截芜繁。
服務端緩存的分類:
數(shù)據庫緩存
SQL查詢流程:
命中條件
緩存存在一個hash表中旺隙,通過查詢SQL,查詢數(shù)據庫浆洗,客戶端協(xié)議等作為key,在判斷命中前催束,mysql不會解析SQL,而是使用SQL去查詢緩存伏社,SQL上的任何字符的不同抠刺,如空格,注釋摘昌,都會導致緩存不命中速妖。如果查詢有不確定的數(shù)據like now(),current_date(),那么查詢完成后結果者不會被緩存聪黎,包含不確定的數(shù)的是不會放置到緩存中罕容。
數(shù)據庫緩存各種相關參數(shù):https://www.cnblogs.com/yesuuu/p/6114600.html
mysql緩存原理以及碎片問題:https://blog.csdn.net/qzqanzc/article/details/80418125
mysql一級緩存,二級緩存:https://www.cnblogs.com/maoyizhimi/p/7778504.html
一個實際的例子:(如何開啟mysql緩存可以自行查資料稿饰,僅僅是實驗為說明原理锦秒,知道了這個后就當沒有mysql緩存這個事兒。)
例子:show variables like '%query_cache%';
可以通過設置query_cache_type->OFF來關閉緩存喉镰,但這就將查詢緩沖永久地關閉了旅择。在MySQL 5.0中提供了一種可以臨時關閉查詢緩沖的方法:
(1) SELECT SQL_NO_CACHE field1, field2 FROM TABLE1
以上的SQL語句由于使用了SQL_NO_CACHE,因此侣姆,不管這條SQL語句是否被執(zhí)行過生真,服務器都不會在緩沖區(qū)中查找,每次都會執(zhí)行它捺宗。
我們還可以將my.ini中的query_cache_type設成2柱蟀,這樣只有在使用了SQL_CACHE后,才使用查詢緩沖蚜厉。
(2) SELECT SQL_CALHE * FROM TABLE1
數(shù)據庫緩存失效機制:
自動失效
在表的結構或數(shù)據發(fā)生改變時长已,查詢緩存中的數(shù)據不再有效。有這些INSERT、UPDATE痰哨、 DELETE胶果、TRUNCATE、ALTERTABLE斤斧、DROPTABLE或DROPDATABASE會導致緩存數(shù)據失效早抠。所以查詢緩存適合有大量相同查詢的應用,不適合有大量數(shù)據更新的應用撬讽。
手動失效
1.FLUSH QUERY CACHE;
2.清理查詢緩存內存碎片
3.RESET? QUERY CACHE;
4.SQL會從查詢緩存中移出所有查詢
總結:
mysql的查詢緩存利用率很低蕊连,原因是每當有修改表內容操作時,緩存中所有與該表相關的內容全部要被清空游昼。
mysql緩存還容易造成碎片問題甘苍。參考:https://blog.csdn.net/jiakairong/article/details/78958215
低版本mysql緩存默認是開啟的,但從5.6開始默認禁用query_cache_type參數(shù)烘豌,禁用緩存载庭。
建議:
不要使用mysql緩存,高版本的mysql的query_cache_type也是模式關閉的廊佩,咱們目前的云平臺庫中的mysql緩存也是關閉的囚聚。使用mysql數(shù)據庫緩存,加重了數(shù)據庫服務器的負擔标锄,而且利用率不一定高顽铸。尤其是對于經常有變更(數(shù)據/結構)的情況下,頻繁寫入的緩存又不能高效利用料皇。專人專職谓松,大概率情況下不要讓廚子去寫代碼~
緩存服務器緩存
關于緩存穿透,緩存雪崩践剂,key重建的話題就不聊了鬼譬。可以參考簡書的這篇帖子逊脯,寫的不錯:
http://www.reibang.com/p/b126d466f01a
一优质、進程內緩存還是進程外緩存
服務端緩存的分類:進程內緩存和進程外緩存(redis/memcache)
進程內緩存的定義:
將一些數(shù)據緩存在站點,或者服務的進程內男窟,這就是進程內緩存盆赤。
進程內緩存的實現(xiàn)載體贾富,最簡單的歉眷,可以是一個帶鎖的Map。又或者颤枪,可以使用第三方庫汗捡,例如leveldb。
進程內緩存能存儲什么?
redis/memcache等進程外緩存服務能緩存什么扇住,進程內緩存就能緩存什么春缕。
進程內緩存可以存儲json數(shù)據,可以存儲html頁面艘蹋,可以存儲對象锄贼。
進程內緩存的好處:
1.與沒有緩存相比,進程內緩存的好處是女阀,數(shù)據讀取不再需要訪問數(shù)據庫宅荤。
2.與進程外緩存相比,進程內 緩存省去了網絡開銷浸策,所以一來節(jié)省了內網帶寬冯键,而來響應時延會更低。
進程內緩存的缺點:
進程外緩存雖然多了一次網絡交互庸汗,但仍然是統(tǒng)一存儲惫确。站點和服務中的多個節(jié)點訪問統(tǒng)一的緩存服務,數(shù)據統(tǒng)一存儲蚯舱,容易保證數(shù)據的一致性改化。而進程內緩存,保存在多個站點和服務的節(jié)點內晓淀,數(shù)據存了多份所袁,一致性比較難保證。
保證進程內緩存的數(shù)據一致性:
第一種方案:單節(jié)點通知其他節(jié)點凶掰,寫請求發(fā)生在server1,在修改完自己內存數(shù)據與數(shù)據庫中的數(shù)據后燥爷,主動通知其他server節(jié)點,也修改內存的數(shù)據懦窘。
缺點:同一功能的一個集群的多個節(jié)點前翎,相互耦合在一起,特別是節(jié)點較多時畅涂,網狀連接關系會極其復雜港华。
第二種方案:通過MQ通知其他節(jié)點。寫請求發(fā)生在server1午衰,在修改完自己內存數(shù)據與數(shù)據庫中的數(shù)據之后立宜,給MQ發(fā)布數(shù)據變化通知,其他server節(jié)點訂閱MQ消息臊岸,也修改內存數(shù)據橙数。
這種方案解決了節(jié)點間的耦合,但引入了MQ帅戒,使得系統(tǒng)更加復雜灯帮。
前兩種方案,節(jié)點數(shù)量較多,數(shù)據冗余份數(shù)越多钟哥,數(shù)據更新的原子性越難保證迎献,一致性也就越難保證。
第三種方案:干脆放棄“實時一致性”腻贰,每個節(jié)點啟動一個timer,定時從后端拉取最新的數(shù)據吁恍,更新內存緩存。
缺點:因為不實時播演,可能會讀到臟數(shù)據践盼。
關于進程內緩存的總結說明:
不要頻繁使用進程內緩存:
分層架構設計有一條準則:站點層,服務層要做到無數(shù)據宾巍,無狀態(tài)咕幻,這樣才能任意的加節(jié)點水平擴展,數(shù)據和狀態(tài)應盡量存儲到后端的數(shù)據存儲服務(數(shù)據庫或者緩存服務)顶霞。
何時可以使用進程內緩存:
1.只讀數(shù)據肄程,可以考慮在進程啟動時加載到內存。
2.一定程度上允許數(shù)據不一致的業(yè)務选浑。(頁面對數(shù)據一致性要求較低蓝厌,可以考慮使用進程內頁面緩存)
終了,一句話總結:還是使用redis和memcache吧
二古徒、redis還是memcache
memcache與redis的對比:
數(shù)據結構:vaue是哈希拓提,列表,集合隧膘,有序集合這類復雜的數(shù)據結構時代态,會選擇redis,因為mc無法滿足這些需求。
持久化:mc無法滿足持久化的需求疹吃,只能選擇redis蹦疑。
千萬不要把redis當做數(shù)據庫使用:
1> redis的定期快照不能保證數(shù)據不丟失
2> redis的AOF(Append Only file 持久化功能)會降低效率,并且不能支持太大的數(shù)據量萨驶。
緩存場景下歉摧,開啟固化功能,有什么利弊腔呜?
優(yōu)點:redis掛了重啟叁温,內存里能夠快速恢復熱數(shù)據,不會瞬時將壓力壓到數(shù)據庫上核畴,沒有一個cache預熱的過程膝但。
缺點:在 redis掛了的過程中,如果數(shù)據庫中的數(shù)據有修改膛檀,可能導致redis重啟后锰镀,數(shù)據庫與redis的數(shù)據不一致。
因此咖刃,只讀場景泳炉,或者允許一些不一致的業(yè)務場景,可以嘗試開啟redis的固化功能嚎杨。
天然高可用:redis天然支持分布式集群功能花鹅,可以實現(xiàn)主動復制,讀寫分離枫浙。而memcache刨肃,要想實現(xiàn)高可用,需要進行二次開發(fā)箩帚。
思考:(大部分業(yè)務場景真友,緩存真的需要高可用嗎)
1>緩存場景,很多時候紧帕,是允許cache miss的
2>緩存掛了盔然,很多時候后可以通過DB讀取數(shù)據
存儲內容比較大:memcache的value存儲,最大為1M是嗜,如果存儲的value很大愈案,只能使用redis
redis中key和value的最大大小限制:https://redis.io/topics/data-types-intro
redis中key的數(shù)量限制:https://redis.io/topics/faq
(redis官網的一句話,redis不會限制你對緩存量大小的限制鹅搪,僅僅取決于你機器配置的內存)
什么時候傾向于memcache?
純KV,緩存對象小于1M站绪,key的長度大于250個字符,沒有持久化要求丽柿,基本不需要分布式集群恢准,高并發(fā)(秒殺場景,抗量)甫题,單臺機器多核利用(redis是單線程的)的情況顷歌,使用memcache或許更適合。
底層實現(xiàn)機制差異:
內存分配:
memcache使用預分配內存池的方式管理內存幔睬,能夠省去內存分配時間眯漩。redis則是臨時申請空間,可能導致碎片麻顶。
虛擬內存使用:
memcache把所有的數(shù)據存儲在物理內存中赦抖。redis有自己的VM機制,理論上能夠存儲比物理內存更多的數(shù)據辅肾,當數(shù)據量超量時队萤,會引發(fā)swap,把冷數(shù)據刷道磁盤上矫钓。
網絡模型:
memcache使用非阻塞IO復用模型要尔,redis也是使用非阻塞IO復用模型舍杜。
redis還提供了一些非KV存儲之外的排序,聚合功能赵辕,在執(zhí)行這些功能時既绩,復雜的CPU計算,會阻塞整個IO調度还惠。
線程模型:
memcache使用多線程饲握,主線程監(jiān)聽,worker線程接受請求蚕键,執(zhí)行讀寫救欧,這個過程中,可能存在鎖沖突锣光。redis使用單線程笆怠,雖無鎖沖突,但難以利用多核的特性提升整體吞吐量誊爹。
三骑疆、緩存,淘汰還是修改
1.KV緩存都緩存了一些什么數(shù)據替废?
(1)樸素類型的數(shù)據箍铭,例如:int
(2)序列化后的對象,例如:User實體椎镣,本質是binary
(3)文本數(shù)據诈火,例如:json或者html
2.淘汰緩存和修改緩存有什么區(qū)別?
(1)淘汰某個key状答,操作簡單冷守,直接將key置為無效,但下一次該key的訪問會cache miss
(2)修改某個key的內容惊科,邏輯相對復雜拍摇,但下一次該key的訪問仍會cache hit
可以看到,差異僅僅在于一次cache miss馆截。
3.緩存中的value數(shù)據一般是怎么修改的充活?
(1)樸素類型的數(shù)據,視情況而定蜡娶。
(2)序列化后的對象:一般需要先get數(shù)據混卵,反序列化成對象,修改其中的成員窖张,再序列化為binary幕随,再set數(shù)據
(3)json或者html數(shù)據:一般也需要先get文本,parse成dom樹對象宿接,修改相關元素赘淮,序列化為文本辕录,再set數(shù)據
結論:對于對象類型,或者文本類型梢卸,修改緩存value的成本較高走诞,一般選擇直接淘汰緩存。
糾結于到底是修改緩存還是淘汰緩存的時候就是需要對比兩者成本的時候低剔,修改的成本小就直接修改,修改的成本較高就直接淘汰肮塞。
四襟齿、先操作數(shù)據庫還是先操作緩存?
這篇沈劍的帖子給出的建議是先操作緩存枕赵,然后再操作數(shù)據庫猜欺。但我感覺有問題,多線程操作的時候拷窜,可能數(shù)據不一致开皿。
比如:A,B兩個線程要做更新篮昧,C線程做查詢赋荆。A先刪除緩存(成功),B后刪除緩存(成功)懊昨,B先更新了數(shù)據庫窄潭,這時候C線程讀取到了數(shù)據庫的數(shù)據(B)的數(shù)據,并更新到了緩存中酵颁,然后A后更新了數(shù)據庫嫉你,結果是:數(shù)據庫中是A的數(shù)據,緩存中是B的數(shù)據躏惋。(緩存有效期幽污,延時雙刪策略(cache aside pattern))
為何在寫數(shù)據庫的時候,是刪除緩存而不是重新設置緩存簿姨?
答:如果A,B兩個線程同時做數(shù)據更新距误,A先更新了數(shù)據庫,B后更新數(shù)據庫扁位,則此時數(shù)據庫里存的是B的數(shù)據深寥。而更新緩存的時候,是B先更新了緩存贤牛,而A后更新了緩存惋鹅,則緩存里是A的數(shù)據。這樣緩存和數(shù)據庫的數(shù)據也不一致闰集。
一、先更新緩存武鲁,再更新數(shù)據庫
(這里對緩存的操作都是刪除,而不是設置)
更新緩存(失斻迨蟆)挚瘟,不再繼續(xù)----->合理
更新緩存(成功)饲梭,更新數(shù)據庫成功---->合理
更新緩存(成功),更新數(shù)據庫失敗----->合理憔涉,僅僅是會在后續(xù)過程多一次對數(shù)據庫的查詢
但會存在我上邊提到的疑問订框。
二、先更新數(shù)據庫穿扳,再更新緩存
更新數(shù)據庫(失敼酢),不再繼續(xù)---->合理
更新數(shù)據庫(成功)泽谨,更新緩存(成功)------->合理
更新數(shù)據庫(成功)特漩,再更新緩存(失敗)-------->不合理雄卷,這時讀取到的是舊數(shù)據
這種方式蛤售,可以通過集中方式來彌補:
1.給緩存設置有效期悴能,這樣錯誤的緩存遲早會變成正確的。而且過期時間越短冯凹,緩存的正確率越高炒嘲。但是匈庭,這樣也同時會增加數(shù)據服務器的負擔浑劳。
2.當?shù)诙街械母拢▌h除)緩存失敗的時候魔熏,進行重試。
3.定期全量更新(定期把緩存全部失效镶骗,然后全量加載)滓窍,這種方式值得商榷巩那,有可能會有大量的請求在這時打到數(shù)據庫即横。
還有另外一種情況:
A,B兩個線程讀取緩存跺嗽,C線程進行寫數(shù)據庫页藻。
A讀緩存,沒有數(shù)據璃吧,進入讀庫流程畜挨,從數(shù)據庫讀取數(shù)據噩凹,讀取成功,還沒有寫緩存逮刨。
C進來寫數(shù)據庫堵泽,寫成功。
B讀緩存箩退,沒有數(shù)據戴涝,進入讀庫流程,從數(shù)據庫讀取數(shù)據奸鸯。讀取成功(B讀取的是C寫入的最新數(shù)據)可帽,還沒有寫緩存映跟。
B寫緩存成功,然后A寫緩存成功球恤,結果---->數(shù)據庫是最新數(shù)據荸镊,緩存中是舊的數(shù)據躬存。
這個時候需要使用redis分布式鎖(https://www.imooc.com/article/34098)。也可以使用有效期策略宛逗。(看具體業(yè)務要求)
五钦椭、緩存與數(shù)據庫不一致,怎么辦侥锦?
(1+2)先一個寫請求恭垦,淘汰緩存,寫數(shù)據庫
(3+4+5)接著立刻一個讀請求唠帝,讀緩存玄柏,cache miss粪摘,讀從庫,寫緩存放入數(shù)據苔悦,以便后續(xù)的讀能夠cache hit(主從同步沒有完成椎咧,緩存中放入了舊數(shù)據)
(6)最后勤讽,主從同步完成
導致的結果是:舊數(shù)據放入緩存,即使主從同步完成蜈七,后續(xù)仍然會從緩存一直讀取到舊數(shù)據莫矗。
可以看到作谚,加入緩存后庵芭,導致的不一致影響時間會很長双吆,并且最終也不會達到一致。
(6)主從同步
(7)通過工具訂閱從庫的binlog匾竿,這里能夠最準確的知道岭妖,從庫數(shù)據同步完成的時間
畫外音:本圖畫的訂閱工具是DTS,可以是cannal假夺,也可以自己訂閱和分析binlog
(8)從庫執(zhí)行完寫操作斋攀,向緩存再次發(fā)起刪除淳蔼,淘汰這段時間內可能寫入緩存的舊數(shù)據
如此這般,至少能夠保證闺魏,引入緩存之后俯画,主從不一致艰垂,不會比沒有引入緩存更壞。
畫外音:即使引入緩存娩怎,也只有一個很小的時間間隔胰柑,可能讀到舊數(shù)據柬讨。
也可以使用延時雙刪策略
六、主從數(shù)據庫不一致却桶,怎么辦蔗牡?
常見的數(shù)據庫集群架構:
一主多從辩越,主從同步,讀寫分離
為何會主從不一致偷拔,因為同步會有時間差:
如何避免這種時間差導致的不一致:
方案一:忽略
任何脫離業(yè)務的架構設計都是耍流氓莲绰,絕大部分業(yè)務蛤签,例如:百度搜索,淘寶訂單称龙,QQ消息戳晌,58帖子都允許短時間不一致沦偎。
如果業(yè)務能夠接受,別把系統(tǒng)架構搞得太復雜搔驼。
方案二:強制讀主
(1)使用一個高可用主庫提供數(shù)據庫服務
(2)讀和寫都落到主庫上
(3)采用緩存來提升系統(tǒng)讀性能
方案三:選擇性讀
(1)寫主庫
(2)將哪個庫舌涨,哪個表囊嘉,哪個主鍵三個信息拼裝一個key設置到cache里啦租,這條記錄的超時時間,設置為“主從同步時延”
key的格式為“db:table:PK”阐肤,假設主從延時為1s贪薪,這個key的cache超時時間也為1s。
這是要讀哪個庫,哪個表错忱,哪個主鍵的數(shù)據呢,也將這三個信息拼裝一個key儿普,到cache里去查詢眉孩,如果勒葱,
(1)cache里有這個key凛虽,說明1s內剛發(fā)生過寫請求,數(shù)據庫主從同步可能還沒有完成殃姓,此時就應該去主庫查詢
(2)cache里沒有這個key瓦阐,說明最近沒有發(fā)生過寫請求睡蟋,此時就可以去從庫查詢
以此,保證讀到的一定不是不一致的臟數(shù)據该面。
總結
數(shù)據庫主庫和從庫不一致隔缀,常見有這么幾種優(yōu)化方案:
(1)業(yè)務可以接受傍菇,系統(tǒng)不優(yōu)化
(2)強制讀主丢习,高可用主庫,用緩存提高讀性能
(3)在cache里記錄哪些記錄發(fā)生過寫請求揽思,來路由讀主還是讀從
參考資料:
何時使用mysql緩存以及提高緩存命中率的建議:https://blog.csdn.net/qq_25622107/article/details/80223470
現(xiàn)代WEB應用程序的服務器端緩存策略??https://alankent.me/2018/08/25/server-side-caching-strategies-for-modern-web-applications/
58沈劍 緩存??https://mp.weixin.qq.com/s/V1hGa6D9aGrP6PiCWEmc0w
Cache Aside Pattern?https://mp.weixin.qq.com/s/-fk-cEIo3iDCUSwT_l8d2w