中间件系列【二点一】 redis究竟适不适合做数据库?

Redis官方介绍:

Redis 是一个 Key-Value 类型的分布式缓存数据库, 以用作数据库、缓存、消息中间件等
官方测试性能数据:

  • Intel® Xeon® CPU E5520 @ 2.27GHz(带流水线)/ (无流水线)
$ ./redis-benchmark -r 1000000 -n 2000000 -t get,set,lpush,lpop -P 16 -q # 优于无流水线。
SET: 552028.75 requests per second
GET: 707463.75 requests per second
LPUSH: 767459.75 requests per second
LPOP: 770119.38 requests per second

$ ./redis-benchmark -r 1000000 -n 2000000 -t get,set,lpush,lpop -q
SET: 122556.53 requests per second
GET: 123601.76 requests per second
LPUSH: 136752.14 requests per second
LPOP: 132424.03 requests per second

看起来相当大哇塞吗,那么redis究竟适合做数据库吗?跟我们其他的分布式内存数据库又有什么区别?
Redis能否做数据库用取决于如下几个条件:

  • 1:数据量,毕竟内存数据库,还是受限于内存的容量,虽然可以redis可以持久化。
  • 2:数据的结构,是否能够将关系型数据结构都转换为key/value的形式。
  • 3:查询的效率,对范围查询等,是否能转换为高效的hash索引查询
    因为需要当作数据库,所以我们就不考虑rdb的持久化模式了,只看AOF。

Redis AOF持久化

与全量式的RDB持久化功能不同,AOF提供的是增量式的持久化功能, 这种持久化的核心原理在于:服务器每次执行完写命令之后,都会以协议文本的方式将被执行的命令追加到AOF文件的末尾。这样一来,服务 器在停机之后,只要重新执行AOF文件中保存的Redis命令,就可以将 数据库恢复至停机之前的状态。

中间件系列【二点一】 redis究竟适不适合做数据库?_第1张图片
中间件系列【二点一】 redis究竟适不适合做数据库?_第2张图片
从表15-3中可以看到,随着服务器不断地执行命令,被执行的命令也会 不断地被保存到AOF文件中(文件中唯一不是用户执行的命令SELECT 0是服务器根据用户正在使用的数据库号码自动加上的)。这样一来, 即使服务器在T4停机,它也可以在重启时通过重新执行AOF文件包含的 命令来恢复数据。对于表15-3中展示的例子来说,服务器只要重新执行 AOF文件中包含的4个命令,就可以让数据库重新回到停机之前的状 态。

为了方便展示,本书在介绍AOF相关内容时,通常会直接写出被执行的命令,但是在实际的AOF文件中,命令都是以Redis网络协议的方式保存的。比如,对于表15-3所示的情况,服务器将创建一个包含代码清单 15-1所示内容的AOF文件(为了能够清晰地辨别各个命令,清单将每个 命令都单独列为一行)。
中间件系列【二点一】 redis究竟适不适合做数据库?_第3张图片
用户可以通过服务器的appendonly选项来决定是否打开AOF持久化功能: 反之,如果用户想要关闭AOF持久化功能,那么只需要将这个值设置为 no即可。当AOF持久化功能处于打开状态时,Redis服务器在默认情况下将创建 一个名为appendonly.aof的文件作为AOF文件

设置AOF文件的冲洗频率

    为了提高程序的写入性能,现代化的操作系统通常会把针对硬盘的多次写操作优化为一次写操作。具体的做法是,当程序调用write系统调用对 文件进行写入时,系统并不会直接把数据写入硬盘,而是会先将数据写 入位于内存的缓冲区中,等到指定的时限到达或者满足某些写入条件 时,系统才会执行flush系统调用,将缓冲区中的数据冲洗至硬盘。  这种优化机制虽然提高了程序的性能,但是也给程序的写入操作带来了不确定性,特别是对于AOF这样的持久化功能来说,AOF文件的冲洗机制将直接影响AOF持久化的安全性。为了消除上述机制带来的不确定 性,Redis向用户提供了appendfsync选项,以此来控制系统冲洗AOF文件的频率:

appendfsync选项拥有always、everysec和no 3个值可选,它们代表的意义分别为:

  • always——每执行一个写命令,就对AOF文件执行一次冲洗操作。

  • everysec——每隔1s,就对AOF文件执行一次冲洗操作。

  • no——不主动对AOF文件执行冲洗操作,由操作系统决定何时对AOF 进行冲洗。

  • 这3种不同的冲洗策略不仅会直接影响服务器在停机时丢失的数据量, 还会影响服务器在运行时的性能:

  • 在使用always值的情况下,服务器在停机时最多只会丢失一个命令的数 据,但使用这种冲洗方式将使Redis服务器的性能降低至传统关系数据 库的水平。

  • 在使用everysec值的情况下,服务器在停机时最多只会丢失1s之内产生的命令数据,这是一种兼顾性能和安全性的折中方案。

    在使用no值的情况下,服务器在停机时将丢失系统最后一次冲洗AOF 文件之后产生的所有命令数据,至于数据量的具体大小则取决于系统冲 洗AOF文件的频率。
    因为no策略给可能丢失的数据量带来了不确定性,而always策略对于安全性的追求又牺牲了服务器的性能,所以Redis使用everysec作为 appendfsync选项的默认值。除非有明确的需求,否则用户不应该随意修改appendfsync选项的值。

AOF重写

随着服务器不断运行,被执行的命令将变得越来越多,而负责记录这些 命令的AOF文件也会变得越来越大。与此同时,如果服务器曾经对相同 的键执行过多次修改操作,那么AOF文件中还会出现多个冗余命令。
举个例子,对于代码清单15-2所示的AOF文件:
中间件系列【二点一】 redis究竟适不适合做数据库?_第4张图片
随着服务器不断运行,被执行的命令将变得越来越多,而负责记录这些 命令的AOF文件也会变得越来越大。与此同时,如果服务器曾经对相同 的键执行过多次修改操作,那么AOF文件中还会出现多个冗余命令。
冗余命令的存在不仅增加了AOF文件的体积,并且因为Redis服务器在 停机之后需要通过重新执行AOF文件中保存的命令来恢复数据,所以 AOF文件中的冗余命令越多,恢复数据时耗费的时间也会越多。为了减 少冗余命令,让AOF文件保持“苗条”,并提供数据恢复操作的执行速 度,Redis提供了AOF重写功能,该功能能够生成一个全新的AOF文件,并且文件中只包含恢复当前数据库所需的尽可能少的命令。
中间件系列【二点一】 redis究竟适不适合做数据库?_第5张图片
对于上图几个关键点:
1、在重写期间,由于主进程依然在响应命令,为了保证最终备份的完整性;因此它依然会写入旧的AOF file中,如果重写失败,能够保证数据不丢失。
2、为了把重写期间响应的写入信息也写入到新的文件中,因此也会为子进程保留一个buf,防止新写的file丢失数据。如果主进程收到了写请求,子进程和父进程是通过管道的方式进行发送这个期间发生的请求,所以这个期间写操作并不会丢失。
3、重写是直接把当前内存的数据生成对应命令,并不需要读取老的AOF文件,最后通过 rename 完成文件的替换工作。

AOF持久化的优缺点

与RDB持久化可能会丢失大量数据相比,AOF持久化的安全性要高得多:通过使用everysec选项,用户可以将数据丢失的时间窗口限制在1s 之内,QPS据说也能上万,起码也比mysql高10倍了。其实你会发现内存数据库大多采用这种策略,比如es也是1s去刷一次盘,只不过es的数据结构又比redis复杂一些
但是与RDB持久化相比,AOF持久化也有相应的缺点:

  • 首先,因为AOF文件存储的是协议文本,所以它的体积会比包含相同 数据、二进制格式的RDB文件要大得多,并且生成AOF文件所需的时间 也会比生成RDB文件所需的时间更长。
  • 其次,因为RDB持久化可以直接通过RDB文件恢复数据库数据,而 AOF持久化则需要通过执行AOF文件中保存的命令来恢复数据库(前者 是直接的数据恢复操作,而后者则是间接的数据恢复操作),所以RDB 持久化的数据恢复速度将比AOF持久化的数据恢复速度快得多,并且数据库体积越大,这两者之间的差距就会越明显。
  • ·最后,因为AOF重写使用的BGREWRITEAOF命令与RDB持久化使用 的BGSAVE命令一样都需要创建子进程,所以在数据库体积较大的情况 下,进行AOF文件重写将占用大量资源,并导致服务器被短暂地阻塞。

Redis高可用

如果用做单独使用Redis的话,它是成当不起数据库的任务,比如你每日活跃用户是1万人,但是你那台redis里面已经积累了50万人了(这个比例很正常),那么每次redis启动,就需要把50万load内存,每次redis备份,又需要把50万dump到磁盘,这个恢复过程是无法承受之痛,当然你也是使用主备,主坏了切备就是。

  • 为什么redis之所以添加各种条件限制AOF的发生?
    上文中也说了,AOF主要耗时发生在fork一个子进程并且会阻塞主进程,这是为什么呢?因为fork子进程时,子进程是会拷贝父进程的页表,即虚实映射关系,但是fork不会把所有的内存数据都copy到子进程,只会copy一部分有用的数据到子进程中。
    中间件系列【二点一】 redis究竟适不适合做数据库?_第6张图片
    所以fork在复制内存页的时候会大量的消耗CPU资源,如果复制的内存页越大,fork阻塞的时间就会越久。拷贝内存页完成,子进程与父进程指向相同的内存地址,这个时候就会放开主进程的阻塞,对外提供操作。每当有新的写命令,就会触发操作系统的COW写时复制机制,此时就会把这新的命令写到AOF日志缓冲区,等待数据重写完成后,重写的日志与缓冲区修改的数据进行合并,这样保证了父子进程之间的数据同步。
    正常情况 fork 耗时应该是每 GB 消耗 20ms 左右,当然也可以用 info stats 命令查看 latest_fork_usec 指标, 获取最近一次 fork 操作耗时, 单位微秒。

总结:

  • redis能不能拿来当数据库,取决于你想要存储什么数据:
    如果一个业务系统,每次交互的数据,都是一个非常大的结果集,并涉及到非常复杂的统计、过滤工作,那么RDBMS是必须的;但如果一个系统,能够通过某个标识,快速定位到一类数据,这一类数据在可以预见的未来,是有限的,那就非常适合Redis存储。redis没有索引,没有外键,缺少int/date等基本数据类型,多条件查询需要通过集合内联(sinter,zinterstore) 和连接间接实现开发效率低,可维护性不佳,但是没有Scheme约束,数据结构的变更相对容易,一开始确定数据类型, 抗压能力强,性能极高,10万/qps。其实对比redis、manggo、elasticsearch、mysql你会发现这些数据库底层都差不多,就是把索引给玩出花了

参考:
1、https://blog.csdn.net/norang/article/details/119832677?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1-119832677-blog-122422724.pc_relevant_default&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-1-119832677-blog-122422724.pc_relevant_default&utm_relevant_index=2
2、https://blog.csdn.net/u012516914/article/details/122422724?spm=1001.2101.3001.6650.5&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-5-122422724-blog-119832677.pc_relevant_aa&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-5-122422724-blog-119832677.pc_relevant_aa&utm_relevant_index=10

你可能感兴趣的:(日常编程,redis,数据库,中间件)