博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Redis常用问题
阅读量:6120 次
发布时间:2019-06-21

本文共 4142 字,大约阅读时间需要 13 分钟。

总结经常遇到的问题:

  1、redis如何保证数据不丢失

    a、主从实时同步(除非主服务器挂了),数据实时同步

    b、写log文件(短暂丢失)

  2、雪崩+穿透(用乐观锁)

    http://www.cnblogs.com/zhangweizhong/p/6258797.html

 

高并发(要用到缓存)

 

缓存雪崩

  缓存雪崩是由于原有缓存失效(过期),新缓存未到期间。所有请求都去查询数据库,而对数据库CPU和内存造成巨大压力,严重的会造成数据库宕机。从而形成一系列连锁反应,造成整个系统崩溃。

  1. 碰到这种情况,一般并发量不是特别多的时候,使用最多的解决方案是加锁排队。(只有一个请求去同步数据,其他的请求去排队不读数据库

public object GetProductListNew()        {            const int cacheTime = 30;            const string cacheKey = "product_list";            const string lockKey = cacheKey;                        var cacheValue = CacheHelper.Get(cacheKey);            if (cacheValue != null)            {                return cacheValue;            }            else            {                lock (lockKey)                {                    cacheValue = CacheHelper.Get(cacheKey);                    if (cacheValue != null)                    {                        return cacheValue;                    }                    else                    {                        cacheValue = GetProductListFromDB(); //这里一般是 sql查询数据。                                      CacheHelper.Add(cacheKey, cacheValue, cacheTime);                    }                                    }                return cacheValue;            }        }

 

  2. 加锁排队只是为了减轻数据库的压力,并没有提高系统吞吐量。假设在高并发下,缓存重建期间key是锁着的,这是过来1000个请求999个都在阻塞的。同样会导致用户等待超时,这是个治标不治本的方法。

  还有一个解决办法解决方案是:给每一个缓存数据增加相应的缓存标记,记录缓存的是否失效,如果缓存标记失效,则更新数据缓存。

     public object GetProductListNew()        {            const int cacheTime = 30;            const string cacheKey = "product_list";            //缓存标记。            const string cacheSign = cacheKey + "_sign";                        var sign = CacheHelper.Get(cacheSign);            //获取缓存值            var cacheValue = CacheHelper.Get(cacheKey);            if (sign != null)            {                return cacheValue; //未过期,直接返回。            }            else            {                CacheHelper.Add(cacheSign, "1", cacheTime);                ThreadPool.QueueUserWorkItem((arg) =>                {                    cacheValue = GetProductListFromDB(); //这里一般是 sql查询数据。                    CacheHelper.Add(cacheKey, cacheValue, cacheTime*2); //日期设缓存时间的2倍,用于脏读。                                });                                return cacheValue;            }        }

  缓存标记:记录缓存数据是否过期,如果过期会触发通知另外的线程在后台去更新实际key的缓存。

  缓存数据:它的过期时间比缓存标记的时间延长1倍,例:标记缓存时间30分钟,数据缓存设置为60分钟。 这样,当缓存标记key过期后,实际缓存还能把旧数据返回给调用端,直到另外的线程在后台更新完成后,才会返回新缓存。

  这样做后,就可以一定程度上提高系统吞吐量。

 

缓存穿透

  缓存穿透是指用户查询数据,在数据库没有,自然在缓存中也不会有。这样就导致用户查询的时候,在缓存中找不到,每次都要去数据库再查询一遍,然后返回空。这样请求就绕过缓存直接查数据库,这也是经常提的缓存命中率问题。

  解决的办法就是:如果查询数据库也为空,直接设置一个默认值存放到缓存,这样第二次到缓冲中获取就有值了,而不会继续访问数据库,这种办法最简单粗暴。

public object GetProductListNew()        {            const int cacheTime = 30;            const string cacheKey = "product_list";            var cacheValue = CacheHelper.Get(cacheKey);            if (cacheValue != null)                return cacheValue;                            cacheValue = CacheHelper.Get(cacheKey);            if (cacheValue != null)            {                return cacheValue;            }            else            {                cacheValue = GetProductListFromDB(); //数据库查询不到,为空。                                if (cacheValue == null)                {                    cacheValue = string.Empty; //如果发现为空,设置个默认值,也缓存起来。                                }                CacheHelper.Add(cacheKey, cacheValue, cacheTime);                                return cacheValue;            }        }

  把空结果,也给缓存起来,这样下次同样的请求就可以直接返回空了,即可以避免当查询的值为空时引起的缓存穿透。同时也可以单独设置个缓存区域存储空值,对要查询的key进行预先校验,然后再放行给后面的正常缓存处理逻辑。

 

缓存预热

  缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样避免,用户请求的时候,再去加载相关的数据。

  解决思路:

    1,直接写个缓存刷新页面,上线时手工操作下。

    2,数据量不大,可以在WEB系统启动的时候加载。

    3,定时刷新缓存,

 

缓存更新

  缓存淘汰的策略有两种:

    (1) 定时去清理过期的缓存。

    (2)当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。 

  两者各有优劣,第一种的缺点是维护大量缓存的key是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂,具体用哪种方案,大家可以根据自己的应用场景来权衡。1. 预估失效时间 2. 版本号(必须单调递增,时间戳是最好的选择)3. 提供手动清理缓存的接口。

  我前面有篇文章,是介绍缓存系统的缓存更新的。感兴趣的朋友可以看看:

 

总结

  这些都是实际项目中,可能碰到的一些问题。实际上还有很多很多各种各样的问题。缓存层框架的封装往往要复杂的多。应用场景不同,方法和解决方案也不同。具体要根据实际情况来取舍。  

转载于:https://www.cnblogs.com/smallpigger/p/9836483.html

你可能感兴趣的文章
Webpack 2 中一些常见的优化措施
查看>>
移动端响应式
查看>>
python实现牛顿法求解求解最小值(包括拟牛顿法)【最优化课程笔记】
查看>>
js中var、let、const的区别
查看>>
腾讯云加入LoRa联盟成为发起成员,加速推动物联网到智联网的进化
查看>>
从Python2到Python3:超百万行代码迁移实践
查看>>
Windows Server已可安装Docker,Azure开始支持Mesosphere
查看>>
简洁优雅地实现夜间模式
查看>>
react学习总结
查看>>
微软正式发布PowerShell Core 6.0
查看>>
Amazon发布新的会话管理器
查看>>
InfoQ趋势报告:DevOps 和云计算
查看>>
舍弃Python,为什么知乎选用Go重构推荐系统?
查看>>
在soapui上踩过的坑
查看>>
MySQL的字符集和字符编码笔记
查看>>
ntpd同步时间
查看>>
must implement java.io.Serializable hessian
查看>>
Microsoft Licenses Flash Lite for Windows Mobile Users
查看>>
HDOJ 2020 绝对值排序
查看>>
HDOJ/HDU 2560 Buildings(嗯~水题)
查看>>