歡迎光臨
每天分享高質量文章

C# Memory Cache 踩坑記錄

背景

前些天公司服務器資料庫訪問量偏高,運維人員收到告警推送,安排我團隊小伙伴排查原因.

我們發現原來系統定期會跑一個回歸測試,該測運行的任務較多,每處理一條任務都會到資料庫中取相關資料,高速地回歸測試也帶來了高頻率的資料庫讀取.

解決方案1

我們認為每個任務要取的資料大相徑庭,因此我們考慮對這個過程進行修改,加入MemoryCache把資料庫中讀取到的資料進行快取.

整個修改非常簡單,相信對常年混跡在博客園中的各位大佬來說小菜一碟,因此小弟不再敘述添加快取的步驟細節.

從快取的添加,代碼提交,Teamcity 編譯通過,到測試環境,QA環境的安裝無比流暢,一切顯得如手到擒來.

嗯,優秀是一種習慣, 沒有一點辦法.

人生如戲,當我們還沉浸在”我加的Cache不可能又BUG”的自信中時,QA傳來噩耗,回歸測試大量未通過 ….

故障排查

之前習慣了使用Redis快取,因此,常識告訴我們 —  在資料庫中資料沒有改動的前提下,加了快取後讀取的資料的效果和從資料庫中讀取的效果是一模一樣的.

除非  ,,,   除非  這個常識是錯誤的….

因此我們加了日誌,對寫入快取前後讀取出來的資料進行了對比,結果出人意料.

該死 MemoryCache 毀我老臉,丟我精度,拿命來!!!!!

從日誌中看到,第一行是從資料庫中讀取的結果,第二行是從cache中讀取的,前兩條資料完全一致,到了第三條,第四條,第五條,仔細觀察發現,在小數點後面,居然有些小數點後比較微小的變化,不管變化的大小但資料確實發生改變了,所以MemoryCache會影響資料精度??這樣會改變資料精度的MemoryCache又有何用??

機智的我,似乎早已看穿了一切,這肯定不是MenoryCache的鍋!!!

不一樣的MemoryCache

我從https://referencesource.microsoft.com 中扒出了MemoryCache的原始碼一探究竟.

定位到MemoryCache中的AddOrGetExisting方法,我們看到,其實我們把資料儲存到該快取的過程本質是把該物件存到一個名為_entries的         Hashtable 中,同樣,取資料也是通過Key到該Hashtable中取出來,整個過程並沒有對該物件進行序列化反序列等,也沒有對該物件進行clone操作.這就意味著我們之前存入的,和後面取出的(不管我們從MemoryCache中取資料取多少次),永遠只取出同一個物件.

這一點,和我之前使用的RedisCache是有很大區別的.我們在Redis中存入資料,是把物件序列化後存到Redis中,取資料是把Redis中的位元組資料反序列成物件,意味著前一次存入的,和後一次取出的,已經不是同一個物件了,因此Redis中的資料是安全的.

猜想

我做出了一個大膽的猜想,之前從MemoryCache中取出來的資料之所以變化了,可能是取出物件後,複雜的處理過程中對該物件進行了什麼修改操作,所以後期,再次從資料庫中讀取資料,讀出來的已經已經不是最初存入的資料,而是前一次修改之後的資料.帶著這個猜想,我對代碼進行了修改.

解決方案2

從MenoryCache中取到資料後對結果進行clone(),這樣即使程式對取出來的結果進行了修改也不會影響Cache中的資料了.

又是一次提心掉到的提交,編譯,安裝後, 回歸測試順利通過.

感覺人生到達了高潮   -_-

把踩得坑分享出來,希望後面的小伙伴引以為鑒,

原文地址:https://www.cnblogs.com/CoderAyu/p/10242230.html


赞(0)

分享創造快樂