A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

     说道redis,我们可能都会知道了他是一个类似缓存的一个内存数据库,我们从未感知到它的存在是因为他很快,为什么非常快,是因为他的发展到应用很快,还有他的反应速度也是非常快的。

        上一篇文章我们讲到了缓存以及非关系型数据库的由来,这篇我们来讲讲redis这种非关系型数据库用在什么地方,以及他的特色——持久化是怎么实现的。


一、redis的适用环境


        首先作为一个nosql的key—value组成的数据库,它们能存储的数据结构必须是简单的,因为有关系的数据即使存储进去之后查询也是很困难的,并且对于海量的数据存储还是关系型数据库比较合适。

        举一个把一般数据库数据存储到key-value中的例子:


student

学号

姓名

年龄

班级

001

小明

18

2

key

value

student:001:姓名

小明

student:001:年龄

18

student:001:班级

2


遵从规则为        key  表名:主键值:列名
                            value  列值


如果加上表关系的话还要复杂好几倍的。

        那么什么样的数据适合存储在非关系型数据库中的呢?

        1、关系不是很密切的的数据,比如用户信息,班级信息,评论数量等等。

        2、量比较大的数据,如访问记录等

        3、访问比较频繁的数据,如用户信息,访问数量,最新微博等


二、持久化


        那么这么多,这么重要的数据都存储在内存中,如果突然断电,岂不是很糟糕,于是就有了数据的持久化机制,这个其实就是把内存中的数据存储到硬盘中,方便数据的持续存在,也可以减少断电造成的损失。

        那么我们怎么持久化数据呢?多长时间进行一次持久化呢?

redis 支持两种持久化方式,一种是 Snapshotting(快照)也是默认方式,另一种是 Append-only file(缩写 aof)的方式。下面分别介绍:

     一)、Snapshotting

         快照是默认的持久化方式。这种方式是就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为dump.rdb。可以通过配置设置自动做快照持久化的方式。我们可以配置 redis在 n 秒内如果超过 m 个 key 被修改就自动做快照,下面是默认的快照保存配置:


  • save 900 1 #900 秒内如果超过 1 个 key 被修改,则发起快照保存
  • save 300 10 #300 秒内容如超过 10 个 key 被修改,则发起快照保存
  • save 60 10000


下面介绍详细的快照保存过程:

        1.redis 调用 fork,现在有了子进程和父进程。

        2. 父进程继续处理 client 请求,子进程负责将内存内容写入到临时文件。由于 os 的实时复制机制( copy on write)父子进程会共享相同的物理页面,当父进程处理写请求时 os 会为父进程要修改的页面创建副本,而不是写共享的页面。所以子进程地址空间内的数据是 fork时刻整个数据库的一个快照。

        3.当子进程将快照写入临时文件完毕后,用临时文件替换原来的快照文件,然后子进程退出。client 也可以使用 save 或者 bgsave 命令通知 redis 做一次快照持久化。 save 操作是在主线程中保存快照的,由于 redis 是用一个主线程来处理所有 client 的请求,这种方式会阻塞所有client 请求。所以不推荐使用。另一点需要注意的是,每次快照持久化都是将内存数据完整写入到磁盘一次,并不是增量的只同步变更数据。如果数据量大的话,而且写操作比较多,必然会引起大量的磁盘 io 操作,可能会严重影响性能。


       二)、AOF方式

        由于快照方式是在一定间隔时间做一次的,所以如果 redis 意外 down 掉的话,就会丢失最后一次快照后的所有修改。如果应用要求不能丢失任何修改的话,可以采用 aof 持久化方式。下面介绍 Append-only file:aof 比快照方式有更好的持久化性,是由于在使用 aof 持久化方式时,redis 会将每一个收到的写命令都通过 write 函数追加到文件中(默认是 appendonly.aof)。当 redis 重启时会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。当然由于 os 会在内核中缓存 write 做的修改,所以可能不是立即写到磁盘上。这样 aof 方式的持久化也还是有可能会丢失部分修改。不过我们可以通过配置文件告诉 redis 我们想要通过 fsync 函数强制 os 写入到磁盘的时机。有三种方式如下(默认是:每秒 fsync 一次)


  • appendonly yes //启用 aof 持久化方式
  • # appendfsync always //收到写命令就立即写入磁盘,最慢,但是保证完全的持久化
  • appendfsync everysec //每秒钟写入磁盘一次,在性能和持久化方面做了很好的折中
  • # appendfsync no //完全依赖 os,性能最好,持久化没保证
        aof 的方式也同时带来了另一个问题。持久化文件会变的越来越大。例如我们调用 incr test命令 100 次,文件中必须保存全部的 100 条命令,其实有 99 条都是多余的。因为要恢复数据库的状态其实文件中保存一条 set test 100 就够了。为了压缩 aof 的持久化文件。 redis 提供了 bgrewriteaof 命令。收到此命令 redis 将使用与快照类似的方式将内存中的数据以命令的方式保存到临时文件中,最后替换原来的文件。



5 个回复

倒序浏览
本帖最后由 lanqi0820 于 2018-5-29 15:28 编辑

作者写的很好,我说下我看过之后的观点,若我说的不对,请提出来一起交流。
关于快照和进程我想补充一点,进程之间是不共享数据的,即各自都有自己的一份数据,互不影响,也就是说当父进程创建出来子进程后,子进程就深度拷贝了父进程的所有数据,即子进程开辟了一个同父进程一样大小的内存空间来存储自己的数据。如果数据量大的话,创建子进程的瞬间就会占用双倍的内存,对于性能也是有影响的。
这是我的理解,欢迎来拍砖。
--------
关于深度拷贝和浅拷贝。现在有一个苹果树,深度拷贝就是完全拷贝出来一个苹果树,自己吃苹果去自己复制出来的树上摘,浅拷贝就是我用个小本子记下苹果树的坐标,吃的时候翻开小本子找坐标,自己去对应的树上摘
回复 使用道具 举报
lanqi0820 发表于 2018-5-29 15:27
作者写的很好,我说下我看过之后的观点,若我说的不对,请提出来一起交流。
关于快照和进程我想补充一点, ...

欢迎你这样的有志之士多多探讨,毕竟我就是给你搬过来东西,只是希望对你们有用!
回复 使用道具 举报
wuqiong 发表于 2018-5-31 09:53
欢迎你这样的有志之士多多探讨,毕竟我就是给你搬过来东西,只是希望对你们有用! ...

希望你多搬点,这样我也能多学点
回复 使用道具 举报
lanqi0820 发表于 2018-5-31 11:05
希望你多搬点,这样我也能多学点

回复 使用道具 举报
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马