zoukankan      html  css  js  c++  java
  • redis常见Bug及雪崩、穿透、击穿解析

    情景一:频繁报异常——TimeOut/无可用连接

    这类错误还是挺常见的,一般来说都属于下面几种情况之一:

    一、没有单例

    当并发达到一定量级时候,如果没有使用单例模式实例化,会引发此类错误致服务不可用。因为在高并发情况下没有使用单例进行实例化,会导致连接池被大量创建,在业务不停止的情况下很容易发生来不及释放的情况,最终导致阻塞。

    正确做法是实例一个全局对象,参考如下:

    private IDatabase DB;
    private static ConnectionMultiplexer conn = null;
    public static object locker = new object();
    public Redis(int I)
    {
        if (conn == null)
        {
            lock (locker)
            {
                if (conn == null)
                {
                    conn = ConnectionMultiplexer.Connect("1.1.1.1:6379");
                }
            }
        }
        DB = conn.GetDatabase(I);
    }

    二、耗时过长的指令(StackExchange客户端连接的只读属性TimeoutMilliseconds=1000),可以通过修改配置搞定:

    var config = new ConfigurationOptions
    {
        ConnectTimeout = 15000,//连接超时改成15秒
        ResponseTimeout = 15000//返回超时改成15秒
    };

    三、CLR中Thread Pool最小按需创建线程数过小导致的等待成本(500ms),可在program.cs设置最小线程数解决(数量要按实际情况设置,否则可能会影响性能):

    public class Program
    {
        public static void Main(string[] args)
        {
            System.Threading.ThreadPool.SetMinThreads(70, 70);
            CreateWebHostBuilder(args).Build().Run();
        }
    }

    建议设置的同时,把所有的redis操作更改成异步操作。上面三种应用如果还是出现相同问题的话,推测是硬件问题了。但是概率非常小,目前还没有遇到过。

    情景二:频繁报OOM

    当redis占用内存超过或持平了服务器的内存上限,就会出现这个错误,新手尤其容易犯这类错误。正确的做法是设置缓存上限,并且搭配内存回收策略使用,下附常用回收策略:

    #设置最大缓存,单位bytes,下面这条是设置最大缓存1G
    maxmemory 1073741824
    
    #回收策略
    
    #默认,到达上限时抛出异常
    maxmemory-policy noeviction 
    
    #淘汰使用频率最低的键,包括持久键
    maxmemory-policy allkeys-lru 
    
    #淘汰使用频率最低的键,不包括持久键
    maxmemory-policy volatile-lru
    
    #随机淘汰,包括持久键
    maxmemory-policy allkeys-random
    
    #随机淘汰,不包括持久键
    maxmemory-policy volatile-random
    
    #淘汰有过期时间的键,距离原本过期时间最近的优先
    maxmemory-policy volatile-ttl

    修改配置文件后重启服务即可。

    情景三:redis正常但数据库不时挂掉

    如果你确认自己的redis服务正常的话,那么相当大的概率是缓存雪崩。

    缓存雪崩简单来说就是大量缓存同时过期,如果有几个G或者更多的缓存同时过期,而发生时又很不幸的是业务高峰期的话,你的数据库大概率会挂掉,而且一旦挂掉,就没那么快恢复了。

    解决方法很简单:缓存键设置随机过期时间,避开批量过期就可以解决这个问题。

    但如果已经随机仍然频发,那么就要换个思路了:还有可能是缓存击穿。

    什么是缓存击穿呢?简单来说就是某个被大量访问的键忽然过期,从而导致数据库压力陡增。

    针对击穿的解决方法也不复杂:缓存键过期时,先拿一个互斥锁再去数据库拿数据,写入缓存以后释放锁;期间访问同一键的其他进程设置延时访问即可。

    其他进程可以少次数循环取键,比如循环5次,每次100毫秒。循环完了还拿不到就先返回失败。

    这样做不仅可以降低数据库的压力,同时也降低了redis的写压力;实际情况可以根据业务需要看着办,并不是一定要这样做。

    情景四:redis设置后数据库压力不减

    缓存服务如果没有任何异常,那么有可能是缓存穿透。

    什么是缓存穿透呢?简单来说就是有人老是去请求不存在的数据。

    那什么是不存在的数据?假设数据库中用户id是从10000开始的,现在有人频繁去请求用户id为500的数据,这个id为500的数据就是不存在的数据。因为它不可能存在于数据库。

    这时候会怎么样呢?系统先查询缓存,查不到所以去查了数据库,也没有查到,所以直接返回空。但是下一次请求还是要走一遍缓存+数据库。这个时候只要一段简单的脚本就可以对数据库造成压力。

    解决方法很简单:当数据库查不到数据时,先设置一个null键入缓存,之后就任他刷。一般情况下会给这个null值设置一个过期时间。

  • 相关阅读:
    初中几何
    角平分线导致的三角形内外角关系
    解方程
    初中|数学题目整理
    求一次函数解析式
    整式的四则运算
    分式方程
    做辅助线的方法
    线段相等的证明思路
    python2.7安装sqlite3模块
  • 原文地址:https://www.cnblogs.com/muchengqingxin/p/13177734.html
Copyright © 2011-2022 走看看