ASP.net Cache丢失的问题诊断

1 业务需求

    缓存来自数据库的数据,不用频繁到数据库中加载。

2 使用模型

    添加一个类 MyCache,然后在里面添加静态属性字段:

    public static DataTable FolderData
    {
        get
        {
            string key = "FolderData";
            object o = HttpRuntime.Cache[key];
            if (o == null)
            {
                //从DB中加载数据
                o = LoadFromDB();
                //添加到缓存中(分钟过期)
                HttpRuntime.Cache.Insert(key, o, null,
                    System.Web.Caching.Cache.NoAbsoluteExpiration,
                    TimeSpan.FromMinutes(30));
            }
            return (DataTable)o;
        }
    }

逻辑是取数据时如果没有,则去数据库中加载,然后添加到缓存中以便下次使用。

前台调用时直接用MyCache. FolderData即可。

3 运行的问题

    运行一段时间后,通过后台数据库的监控,发现有一台Web服务器还是有多次加载的SQL发送到数据库的情况,而且发生的还很频繁。为什么缓存没有生效呢?丢了?添加监控日志:

 

    public static DataTable FolderData
    {
        get
        {
            string key = "FolderData";
            object o = HttpRuntime.Cache[key];
            if (o == null)
            {
                //从DB中加载数据 
                o = LoadFromDB();
                saveLog("Add to Cache Key:" + key);
                //添加到缓存中(分钟过期)(指定回调函数) 
                HttpRuntime.Cache.Insert(key, o, null,
                System.Web.Caching.Cache.NoAbsoluteExpiration,
                TimeSpan.FromMinutes(30),
                CacheItemPriority.Normal, ASPCacheRemoveCallBack);
            }
            return (DataTable)o;
        }
    } 

   A 添加进入Cache时,记录日志。

    B 添加删除的回调函数,当缓存被删除时,记录日志。

    private static void ASPCacheRemoveCallBack
    (string key, object value, CacheItemRemovedReason reason)
    {
        saveLog("Key:" + key + "被删除,原因:" + reason + ",时间:" + DateTime.Now.ToString());
    }

     然后分析这些日志,发现缓存有多次被删除,删除的原因是UnderUsed。

// 摘要:

// 之所以从缓存中移除该项,是因为系统要通过移除该项来释放内存。

Underused = 3,

但是连上服务器看,实际内存还有很多。(系统一共16G,使用的才11.5G,而且w3wp.exe进程才占用500MB。

    为什么还有很多内存剩余,但是却说内存不够要把缓存的数据清理掉呢?因为是缓存,目的是少去数据库,缓存的东西如果多了,占用内存大了,OS不是还有虚拟内存吗?可以交换到硬盘呀,当然,如果这种情况发生,我们就把缓存的过期时间设小一点,少缓存点数据,维持系统平衡。但是不能还剩下4~5G的时候就不缓存了呀。

    添加可用的日志:

    private static void ASPCacheRemoveCallBack
    (string key, object value, CacheItemRemovedReason reason)
    {
        saveLog("Key:" + key + "被删除,原因:" + reason + ",时间:" + DateTime.Now.ToString()
        + "可用数:" + HttpRuntime.Cache.EffectivePrivateBytesLimit);
    } 

 

查看日志,发现EffectivePrivateBytesLimit的值是0.

查阅资料:

http://msdn.microsoft.com/zh-SG/library/system.web.caching.cache.effectiveprivatebyteslimit(v=vs.80)

EffectivePrivateBytesLimit 属性返回应用程序进程可使用的千字节数。达到此限制后,缓存算法开始积极清理缓存。

可使用应用程序配置文件中的 caching cache 元素(ASP.NET 设置架构) 元素的 privateBytesLimit 属性 (Attribute) 设置 EffectivePrivateBytesLimit 属性 (Property)。未设置 privateBytesLimit 属性时,缓存算法将确定缓存的最大大小,EffectivePrivateBytesLimit 属性将包含该算法所选择的大小。

 为0表示ASP.net 认为没有可分配的内存了吗?实际情况并不是这样,系统刚运行一个缓存也没有时,它也是输出0。

    进一步查看Web.Config,我们并没有设定privateBytesLimit属性的值。

IIS上设定了最大内存限制吗?

也没有设定内存的限制,而且也没有设定要定时回收进程,这点可以从Application的变量没有重新初始化丢失得到验证。

同样的程序部署运行在另外好几套环境,EffectivePrivateBytesLimit的值也是0,但是它们没有频繁发生UnderUsed的情况。所以EffectivePrivateBytesLimit的值为0并不代表没有内存可用了,需要清理,反而更像没有对此属性做设定的意思。

    那到底是什么原因引起的ASP.net 的清理内存呢?

    继续查阅资料:http://msdn.microsoft.com/zh-cn/library/ms228248%28v=vs.100%29.aspx

    发现可以有几个设定来控制缓存的设定:

根据实际运行情况, 我们猜想:ASP.net 面临内存压力时好像算错了,好吧,我们让它不要算了,只要清理过期的数据就好了。把disableMemoryCollection设定为true,告诉ASP.net面临内存压力时不要回收内存。

连续运行3天,ASP.net 正常清理过期的数据,但是没有再出现删除原因为UnderUsed的日志,内存占用也稳定,没有大的波动。

4 结语

本来还想继续深入寻找ASP.net Cache的缓存的清理内存机制到底是什么,到底为什么出问题,初步反射了下它的代码,没有准确找到,再说这个问题是 MS搞出来的,负责人应该是它,不应该我们帮它排错(哈哈,偷懒的借口,不过每个人的精力是有限的,要解决回答为什么的问题是无限的。。。)

本文最终的结论就是设定了一个配置值而已,这么小的事似乎没必要发Blog,但是发现大批提到UnderUsed和disableMemoryCollection的文章都只有1句话的解释,没有详细的分析排错的过程,故而献丑写一篇,如有不对,还请大家指出,谢谢!

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。