前些天公司服务器数据库访问量偏高,运维人员收到告警推送,安排我团队小伙伴排查缘由.数据库
咱们发现原来系统按期会跑一个回归测试,该测运行的任务较多,每处理一条任务都会到数据库中取相关数据,高速地回归测试也带来了高频率的数据库读取.缓存
咱们认为每一个任务要取的数据截然不同,所以咱们考虑对这个过程进行修改,加入MemoryCache把数据库中读取到的数据进行缓存.安全
整个修改很是简单,相信对常年混迹在博客园中的各位大佬来讲小菜一碟,所以小弟再也不叙述添加缓存的步骤细节.服务器
从缓存的添加,代码提交,Teamcity 编译经过,到测试环境,QA环境的安装无比流畅,一切显得如手到擒来.运维
嗯,优秀是一种习惯, 没有一点办法.测试
人生如戏,当咱们还沉浸在"我加的Cache不可能又BUG"的自信中时,QA传来噩耗,回归测试大量未经过 ....日志
以前习惯了使用Redis缓存,所以,常识告诉咱们 --- 在数据库中数据没有改动的前提下,加了缓存后读取的数据的效果和从数据库中读取的效果是如出一辙的.对象
除非 ,,, 除非 这个常识是错误的....blog
所以咱们加了日志,对写入缓存先后读取出来的数据进行了对比,结果出人意料.ci
该死 MemoryCache 毁我老脸,丢我精度,拿命来!!!!!
从日志中看到,第一行是从数据库中读取的结果,第二行是从cache中读取的,前两条数据彻底一致,到了第三条,第四条,第五条,仔细观察发现,在小数点后面,竟然有些小数点后比较微小的变化,无论变化的大小但数据确实发生改变了,因此MemoryCache会影响数据精度??这样会改变数据精度的MemoryCache又有何用??
机智的我,彷佛早已看穿了一切,这确定不是MenoryCache的锅!!!
我从https://referencesource.microsoft.com 中扒出了MemoryCache的源码一探究竟.
定位到MemoryCache中的AddOrGetExisting方法,咱们看到,其实咱们把数据存储到该缓存的过程本质是把该对象存到一个名为_entries的 Hashtable 中,一样,取数据也是经过Key到该Hashtable中取出来,整个过程并无对该对象进行序列化反序列等,也没有对该对象进行clone操做.这就意味着咱们以前存入的,和后面取出的(无论咱们从MemoryCache中取数据取多少次),永远只取出同一个对象.
这一点,和我以前使用的RedisCache是有很大区别的.咱们在Redis中存入数据,是把对象序列化后存到Redis中,取数据是把Redis中的字节数据反序列成对象,意味着前一次存入的,和后一次取出的,已经不是同一个对象了,所以Redis中的数据是安全的.
我作出了一个大胆的猜测,以前从MemoryCache中取出来的数据之因此变化了,多是取出对象后,复杂的处理过程当中对该对象进行了什么修改操做,因此后期,再次从数据库中读取数据,读出来的已经已经不是最初存入的数据,而是前一次修改以后的数据.带着这个猜测,我对代码进行了修改.
从MenoryCache中取到数据后对结果进行clone(),这样即便程序对取出来的结果进行了修改也不会影响Cache中的数据了.
又是一次提心掉到的提交,编译,安装后, 回归测试顺利经过.
感受人生到达了高潮 -_-
把踩得坑分享出来,但愿后面的小伙伴引觉得鉴,