第一个redis应用方法导致的提示redis LRANGE命令超时问题的解决

我的第一个redis应用的方法代码如下:

public List GetRedisQuestionsLibraryList()  
       {  
           var qlist = RedisHelper.ListRange(RedisKey.RedisQuestionsLibraryList);  
           if (qlist == null || qlist.Count() <= 0)  
           {  
               List list = new QuestionsLibraryService().GetNolock(m => true).ToList();  
               RedisHelper.ListRightPush(RedisKey.RedisQuestionsLibraryList, list);  
               return list;  
           }  
           else  
           {  
               return RedisHelper.ListRange(RedisKey.RedisQuestionsLibraryList);  
           }  
  
       }  

此方法在使用中通过对服务器的监察,发现cpu从原来的1%左右飙升到40-50%。而且在错误日志出现了大量有关redis超时提示。错误提示代码如下:

Timeout performing LRANGE Redis:QuestionsLibraryList, inst: 7, queue: 8, qu: 0, qs: 8, qc: 0, wr: 0, wq: 0, in: 2944, ar: 0, clientName: iZ25f3l6awvZ, serverEndpoint: 127.0.0.1:8383, keyHashSlot: 3349, IOCP: (Busy=1,Free=799,Min=8,Max=800), WORKER: (Busy=9,Free=791,Min=8,Max=800) (Please take a look at this article for some common client-side issues that can cause timeouts: http://stackexchange.github.io/StackExchange.Redis/Timeouts)

可以确认这个都是页面调用RedisHelper.ListRange这个方法引起的。从网上扒的资料来看,redis是单进程,单线程的。也就是说,短时间内网站向redis服务发出了大量的ListRange指令。由于指令是在队列中一条一条处理的。导致后面的指令得不到处理。

我又到服务器上打开cmd窗口,然后进入redis命令后输入:

SLOWLOG GET

然后发觉最近的超时命令都是LRANGE,而且每条都超过了10几秒。但我很疑惑,因为我的RedisHelper类是单例连接,使用的都是静态方法和静态属性,静态变量。难道是我使用的GetRedisQuestionsLibraryList() 不是静态方法的缘故?这不科学啊,虽然我这个方法是实例方法。可是这个方法中自redis服务中获取值的方法确实实实在在的静态方法啊。这令我很凌乱,在网上扒资料,并没有找到这块有用的东西。但问题还是要解决。因为没有理论佐证,只好进行尝试。于是我把此方法改为静态属性的形式代码:

protected static List RedisQuestionsLibraryList
        {
            get
            {
                var qlist = RedisHelper.ListRange(LMSoft.WebCommon.RedisKey.RedisQuestionsLibraryList);
                if (qlist == null || qlist.Count() <= 0)
                {
                    List list = new QuestionsLibraryService().GetNolock(m => true).ToList();
                    RedisHelper.ListRightPush(LMSoft.WebCommon.RedisKey.RedisQuestionsLibraryList, list);
                    return list;
                }
                else
                {
                    return qlist;
                }
            }
        }

然后修改相关调用代码,编译,上传到服务器上进行测试。结果出现了大量的redis的Timeout performing LRANGE Redis超时提示。和原来的错误一样。果断修改为静态方法形式进行测试,代码如下:

 protected static List GetRedisQuestionsLibraryList()
        {
            var qlist = RedisHelper.ListRange(LMSoft.WebCommon.RedisKey.RedisQuestionsLibraryList);
            if (qlist == null || qlist.Count() <= 0)
            {
                List list = new QuestionsLibraryService().GetNolock(m => true).ToList();
                RedisHelper.ListRightPush(LMSoft.WebCommon.RedisKey.RedisQuestionsLibraryList, list);
                return list;
            }
            else
            {
                return qlist;
            }
        }


修改相关调用代码,编译,上传服务器测试,仍然是哪个错误提示。我表示很无语。马上再换,这次换为静态变量的形式,代码如下:

protected static List RedisQuestionsLibraryList = getRedisQuestionsLibraryList();
        private static List getRedisQuestionsLibraryList()
        {
            var qlist = RedisHelper.ListRange(LMSoft.WebCommon.RedisKey.RedisQuestionsLibraryList);
            if (qlist == null || qlist.Count() <= 0)
            {
                List list = new QuestionsLibraryService().GetNolock(m => true).ToList();
                RedisHelper.ListRightPush(LMSoft.WebCommon.RedisKey.RedisQuestionsLibraryList, list);
                return list;
            }
            else
            {
                return qlist;
            }
        }
       


上传上去后,观察网站cpu的使用。又观察错误日志代码。经过一个多小时的跟踪,这次没发现cpu使用大幅上扬的情况,也没发现超时错误。这算是解决了。可是我很无语,你说实例方法调用,会出现大量排队现象,怎么静态方法及静态属性这两种获取方法也出现大量redis排队现象。概念理解的不透么,也只能这样说了。

扒了扒资料也就找到一个似乎能解释通的。提到了静态变量的初始化及赋值。

1、任何带有初始值设定项的静态字段,则在执行该类的静态构造函数时,先要按照文本顺序执行那些初始值设定项

2、如果没有编写静态构造函数,而这时类中包含带有初始值设定的静态字段,那么编译器会自动生成默认的静态构造函数

3、类的静态构造函数在给定应用程序域中至多执行一次:只有创建类的实例或者引用类的任何静态成员才激发静态构造函数

这意味着我上面所写的静态变量的赋值方法,其实在C#的编译器中是自动创建的静态构造函数中完成的赋值操作,并且只执行了一次。


静态方法及静态属性按我以前的理解,它是所有该类的实例方法都可以使用的,并且在这里的话这个静态方法或者静态属性只调用一次Redis中的LRANGE命令。然而从测试的情况来看,它和实例方法一样,每次使用都要调用一次Redis中的LRANGE命令。我又对静态方法及静态属性的理论解释深入挖掘,发现了一个有意思的解释,就是它是一系列行为的组合。是一个action,也就是动作。那这也意味着每次调用的时候,它里面的一系列操作都要执行一遍。这样就解释通了。由于每次调用静态属性和静态方法,里面的操作都要执行,那自然对Redis的LRANGE命令进行了多次调用操作。然后导致了redis报超时错误。而静态变量由于赋值就进行了一次,也就是Redis的LRANGE命令只调用了一次,所以不会遇到由于大量调用造成超时的问题


你可能感兴趣的:(asp.net,MVC,C#,redis,数据库)