redis使用中存在的问题及如何避免(一)

redis能够知足不少的应用场景,并且由于将全部数据都放到内存中,因此它的读写性能很好,不少公司都在使用redis。redis给咱们带来便利的同时,使用过程当中会存在什么问题呢,本文将简单加以总结。linux

  • 阻塞问题
    redis使用了单线程来处理请求,为何单线程能够支持如此高的并发呢?主要有以下几点:redis

    1. 纯内存访问:将全部数据都放到内存中,内存响应时间为100纳秒,是redis达到每秒万级别访问的重要基础
    2. 非阻塞IO:redis使用epoll做为I/O多路复用技术,redis自身的事件处理模型将epoll中的链接、读写、关闭都转换为事件,不在网络I/O上浪费过多时间
    3. 单线程:避免了线程切换和竞态产生的消耗,简化了数据结构和算法的实现
      所以若是某个命令执行时间过长,会形成其余命令阻塞,对redis来讲是致命的算法

      产生阻塞的场景:
      A. API或数据结构使用不合理
         a. 避免使用某些易形成阻塞的命令如:keys sort hgetall smembers
            执行showlog get [n] 能够获取最近n条执行慢的记录,对于执行超过必定时间
            (默认10ms,线上建议设置为1ms)的命令都会记录到一个定长队列(默认128,可调整)中。              
         b. 防止一次操做获取过多数据:缩减大对象或者把大对象拆分为多个小对象
            发现大对象的命令:redis-cli -h{ip} -p{port} bigkeys
            内部原理:采用分段进行scan操做,把历史扫描过的大对象统计出来
         c. 防止大量key同时过时:若是有不少key在同一秒内过时,超过了全部key的25%,redis主线程就会阻塞直到过时key比例降低到25%之内,
            所以要避免同一时间过时大量key,过时时间可作散列处理。
            redis4.0引入的lazyfree机制能够避免del、flushdb、flushall、rename等命令引发的redis-server阻塞,提升服务稳定性。
           
      B. CPU饱和
         单线程的redis处理命令时只能使用一个CPU,CPU饱和是指redis把单核的CPU使用率跑到接近100%。
         首先要肯定redis的并发量是否达到极限,经过redis-cli-h{ip} -p{port}--stat 获取redis当前使用状况。
         若是达到每秒6w+左右的qps,说明单台已跑到极限,须要水平扩展。
         若是qps只有几百或者几千CPU就已经饱和,可能使用了高算法复杂度的命令或者是对内存的过分优化
         (如放宽了ziplist的使用条件,虽然使用的内存会变少,可是更耗CPU)。
            
      C. 持久化操做
         持久化引发主线程的阻塞操做主要有:fork阻塞、AOF刷盘阻塞、HugePage写操做阻塞
         a. fork阻塞
            发生在RDB和AOF重写时,redis主线程调用fork操做产生共享内存的子线程,由子线程完成持久化文件的重写工做,若fork操做耗时过长会引发阻塞。
            避免使用内存过大的实例。              
         b. AOF刷盘阻塞
            开启AOF持久化功能时,通常会采用1次/s的刷盘方式,后台线程每秒对AOF文件作fsync操做,当硬盘压力过大时fsync操做须要等待直到写入完成。
            若是主线程距离上一次的fsync成功超过2s,为了数据安全会阻塞直到后台线程执行完fsync完成。这种阻塞是因为磁盘压力引发。
            尽可能独立部署
         c. HugePage写操做阻塞
            子进程在执行重写期间利用linux的copyonwrite机制,会拖慢写操做的执行时间,致使大量写操做慢查询。
            优化linux配置
  • 缓存穿透
    缓存穿透是指查询一个根本不存在的数据,缓存层和存储层都不命中,且不将空结果写到缓存中。
    会致使后端存储负载变大,形成后端存储宕机等问题。能够在程序中分别统计总调用数、缓存命中数、存储命中数,如有大量存储层空命中,多是出现了缓存穿透。
    产生缘由:1.自身代码或数据出现问题 2.恶意攻击,爬虫形成空命中
    如何解决:后端

    1. 缓存空对象
      存储层不命中,扔将空对象保存到缓存层。
      适用场景:数据频繁变化、实时性高
      带来问题:
      a.缓存了空值,会占用内存空间;能够设置较短过时时间,自动剔除。
      b.数据不一致,若存储层添加了此数据,有短暂不一致;可主动清除掉缓存的空对象。
    2. 布隆过滤器 在访问缓存层和数据层以前将存在的key用布隆过滤器提早保存起来,作第一层拦截。 适用场景:大用户集,实时性要求较低的场景,若有几亿的数据集,每隔一段时间会新增用户进去,在更新以前新用户的访问会存在缓存穿透问题。 缺点:代码维护复杂
相关文章
相关标签/搜索