您的位置:首页 > 数据库 > Redis

Redis:击穿、雪崩和持久化问题

2019-07-23 10:30 344 查看

Redis击穿问题

请求数据顺序,首先会从redis缓存中拿数据,若未拿到则查数据库,再写到redis。如果是请求一条根本不存在的数据时,则会去直接访问数据库,但是数据库也没有,所以它也没把数据写入redis缓存。所以每次这种请求都会直接访问数据库。如果请求的数量太大的话,这种绕过redis直接访问数据库情况则成为击穿。

  • 解决方法

1、在查询数据库的时候若未查询到数据,依然在redis中保存一条记录,并把value设置为null或者其他便于区别的值,可以再给该条数据设置一个过期时间,那么以后再有请求过来访问时就不会绕过redis直接去查库。
2、在从redis中获取数据前增加查询校验,对所有可能存在的查询参数缓存到hash集合中,若某个请求在该集合中存在,则可以访问rides,若不存在则直接丢弃该请求。

Redis雪崩问题

每个key(即数据)如果设置了失效时间的话,如果大量key同时过期的时候,或者说因为某种原因redis中的数据突然大批量丢失,这些key又大量地去请求这些key时,因为redis里面没有这些数据,就会大量的请求就会大量涌向数据库,就会导致数据库处理不过来,导致“雪崩”。

  • 解决方法

1、设置均匀的过期时间,避免某段时间出现大批量数据失效

2、在缓存失效后,通过加锁或者队列来控制读数据库写缓存的线程数量。比如对某个key只允许一个线程查询数据和写缓存,其他线程等待。

3、做二级缓存,一级缓存未查询到数据,则从二级缓存查询

链接: 原文地址.

Redis持久化问题

redis有两种持久化方式一种是快照(snapshotting),另一种就是只追加文件(append-only file),通过合理的配置两种持久化方式可以有效的保存内存中的数据,将数据丢失量降到最低。

  • 快照(snapshotting):

    快照可以将某一时刻的所有数据都写入硬盘中,便于服务器重启后的数据恢复
    redis默认开启快照持久化,默认配置为 save 900 1 每900秒有一次数据写入,自动触发bgsave(在后台异步的创建快照进行持久化)
  • save 300 10 每300秒有10次数据写入,自动触发bgsave
  • save 60 10000 每60秒有10000次数据写入,自动触发bgsave
  • 配置可以根据需求自己更改,以上是redis通过配置的形式自动给你持久化,当然我们也可以通过手动的方式持久化,使用redis中的save命令,在redis接收到save命令起不再相应客户端的任何命令,直到快照创建完毕,若想在此期间也可以操作redis,我们可以选择使用reids的bgsave命令,异步在后台创建快照持久化数据,原理是会调用fork创建一个子进程,由子进程将快照写入硬盘
  • 缺点:如果系统发生崩溃,则会丢失在最近一次生成快照之后的发生更改的数据,例如8:00创建了一个快照,9:00发生了系统崩溃,8:00 - 9:00之间发生的数据更改会全部丢失。
  • AOF持久化(append-only file):

      AOF持久化会将被执行的写命令写到AOF文件的末尾,以此记录数据的变化,如若服务器宕机,则从新加载一遍aof文件即可恢复。
    • redis默认关闭aof持久化,开启需要将配置文件中
      appendonly no ->appendonly yes, appendfsync always 每个redis命令都会同步写入硬盘,这样做会严重降低redis的性能,不建议使用
    • appendfsync everysec 每秒执行一次同步,显示的将多个写命令同步到硬盘
    • appendfsync no 让操作系统决定何时进行同步
  • 配置可以根据实际需求自己更改,选取最适合的配置,但个人建议使用appendfsync everysec,因为使用appendfsync everysec 和不设置aof时性能没有太大差别,而使用appendfsync always则在数据量大一点的情况下回严重影响redis的速度,转式硬盘每秒能处理200个写命令,固态硬盘每秒能处理几万个写处理,在大数据量的情况下回频繁的对硬盘进行写操作,对固态硬盘影响很严重,会引发写入放大,在某些情况下甚至会将固态硬盘的寿命从原来的济南降低为几个月,所以在选择aof同步频率时要根据实际需求及硬件条件来抉择
  • 缺点: aof最大的缺点就是体积大小了,假如内存中的数据量不断增大,由于aof是追加写入文件方式,会导致aof文件慢慢的增大,最极端的情况下会占用整个硬盘。但是redis中有相应的aof重写命令bgrewriteaof命令可以进行从谢aof文件去除文件中冗余的命令,在配置文件中可也以进行设置 auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb意味着当aof体积增大一倍(100%)或者当aof文件的体积大于64mb时会触发bgrewriteaof命令进行aof重写
  • 原文:https://blog.csdn.net/qq_36558538/article/details/81588075

    Copy On Write

    快照原理

    我们知道 Redis 是单线程程序,这个线程要同时负责多个客户端套接字的并发读写操作和内存数据结构的逻辑读写。

    在服务线上请求的同时,Redis 还需要进行内存快照,内存快照要求 Redis 必须进行文件 IO 操作,可文件 IO 操作是不能使用多路复用 API。
    这意味着单线程在服务线上请求的同时,还要进行文件 IO 操作,而文件 IO 操作会严重拖累服务器请求的性能。

    还有个重要的问题,为了不阻塞线上的业务,Redis 就需要一边持久化,一边响应客户端的请求。持久化的同时,内存数据结构还在改变,比如一个大型的 hash 字典正在持久化,结果一个请求过来把它给删掉了,可是还没持久化完呢,这该怎么办呢?

    Redis 使用操作系统的多进程 COW(Copy On Write)机制来实现快照持久化,这个机制很有意思,也很少人知道。多进程 COW 也是鉴定程序员知识广度的一个重要指标。

    fork(多进程)

    Redis 在持久化时会调用 glibc 的函数 fork 产生一个子进程,快照持久化完全交给子进程来处理,父进程继续处理客户端请求。子进程刚刚产生时,它和父进程共享内存里面的代码段和数据段。这时你可以把父子进程想象成一个连体婴儿,它们在共享身体。这是 Linux 操作系统的机制,为了节约内存资源,所以尽可能让它们共享起来。在进程分离的一瞬间,内存的增长几乎没有明显变化。

    用 Python 语言描述进程分离的逻辑如下。fork 函数会在父子进程同时返回,在父进程里返回子进程的 pid,在子进程里返回零。如果操作系统的内存资源不足,pid 就会是负数,表示 fork 失败。

    pid = os. 3ff7 fork()
    if pid > 0:
    handle_client_requests() # 父进程继续处理客户端请求
    if pid == 0:
    handle_snapshot_write() # 子进程处理快照写磁盘
    if pid < 0:
    # fork error


    子进程做数据持久化,不会修改现有的内存数据结构,它只是对数据结构进行遍历读取,然后序列化写到磁盘中。但是父进程不一样,它必须持续服务客户端请求,然后对内存数据结构进行不间断的修改。

    这个时候就会使用操作系统的 COW 机制来进行数据段页面的分离。如图所示,数据段是由很多操作系统的页面组合而成,当父进程对其中一个页面的数据进行修改时,会将被共享的页面复制一份分离出来,然后对这个复制的页面进行修改。这时子进程相应的页面是没有变化的,还是进程产生时那一瞬间的数据。

    随着父进程修改操作的持续进行,越来越多的共享页面被分离出来,内存就会持续增长。但是也不会超过原有数据内存的 2 倍大小。另外一个 Redis 实例里冷数据占的比例往往是比较高的,所以很少会出现所有的页面都会被分离,被分离的往往只有其中一部分页面。每个页面的大小只有 4KB,一个 Redis 实例里面一般都会有成千上万个页面。

    子进程因为数据没有变化,它能看到的内存里的数据在进程产生的一瞬间就凝固了,再也不会改变,这也是为什么 Redis 的持久化叫“快照”的原因。接下来子进程就可以非常安心地遍历数据,进行序列化写磁盘了。

    内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
    标签: 
    相关文章推荐