Redis——常见问题及解决方案

情景一:频繁报异常——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值设置一个过期时间。

posted @ 2020-04-24 16:56  老马-Max  阅读(660)  评论(0编辑  收藏  举报