背景
前些天公司服務器數據庫訪問量偏高,運維人員收到告警推送,安排我團隊小伙伴排查原因.
我們發現原來系統定期會跑一個回歸測試,該測運行的任務較多,每處理一條任務都會到數據庫中取相關數據,高速地回歸測試也帶來了高頻率的數據庫讀取.
解決方案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中的數據了.
又是一次提心掉到的提交,編譯,安裝后, 回歸測試順利通過.
感覺人生到達了高潮 -_-
把踩得坑分享出來,希望后面的小伙伴引以為鑒,