目录

Redis学习(二)-redis进阶

上篇主要讲到redis的基础命令,这篇涉及到redis的配置、持久化、主从、事务、内存淘汰策略

redis的配置

启动redis服务器的时候会指定配置文件,主要参数如下:

  • daemonize: #是否以后台守护进程方式运行
  • pidfile: #pid 文件位置
  • port: #监听的端口号
  • timeout: #请求超时时间
  • loglevel: #log 信息级别,总共支持四个级别:debug、verbose、notice、warning , 默认为 verbose
  • logfile: #默认为标准输出(stdout),如果配置为守护进程方式运行,而这里又配 置为日志记录方式为标准输出,则日志将会发送给/dev/null
  • databases: #开启数据库的数量。使用“SELECT 库 ID”方式切换操作各个数据库
  • save:保存快照的频率,第一个表示多长时间,第二个表示执行多少次写操作。在一定时间内执行一定数量的写操作时,自动保存快照。可设置多个条件。
  • rdbcompression:#保存快照是否使用压缩
  • dbfilename: #数据快照文件名(只是文件名,不包括目录)。默认值为 dump.rdb
  • dir: #数据快照的保存目录(这个是目录)
  • requirepass: #设置 Redis 连接密码,如果配置了连接密码,客户端在连接 Redis 时需 要通过 AUTH 命令提供密码,默认关闭。

redis持久化

redis在内存中存储数据,但重启的时候我们还是希望能恢复数据,那么可以把数据持久化到数据库中。你也可以同时开启两种持久化方式, 在这种情况下, 当redis重启的时候会优先载入AOF文件来恢复原始的数据,因为在通常情况下AOF文件保存的数据集要比RDB文件保存的数据集要完整.

快照方式RDB (Redis Database)

快照方式也是redis默认使用方式,这种方式就是将内存中数据以快照的方式写入到二进制文件中,默认的文件名为dump.rdb。当然我们也可以主动调用命令来手动持久化,使用save 或者bgsave 命令通知 redis 做一次快照持久化。 配置参数
save 900 1
save 300 10
save 60 10000
分别表示900秒(15分钟)内有1个更改,300秒(5分钟)内有10个更改以及60秒内有10000个更改。

RDB文件通过两个命令来生成:

  • SAVE:阻塞redis的服务器进程,直到RDB文件被创建完毕。在主线程中保存快照,redis是用一个主线程来处理所有请求的,这种方式会阻塞所有客户端的请求。

  • BGSAVE:Fork出一个子进程来创建RDB文件,不阻塞服务器进程,记录接收BGSAVE当时的数据库状态,父进程继续处理接收到的命令,子进程完成文件的创建之后,会发送信号给父进程。

自动化触发RDB持久化的方式 1>根据配置redis.conf的save就可以(用的bgsave) 2>主从复制时,主节点自动触发 3>执行Debug Reload 4>执行shutdown且没有开启AOF持久化

注意:

  • save 操作是在主线程中保存快照的,由于 redis 是用一个主线程来处理所有客户端的请求,这种方式会阻塞所有客户端请求。所以不推荐使用。
  • 每次快照持久化都是将内存数据完整写入到磁盘一次,并不是增量的只同步增量数据。如果数据量大的话,写操作会比较多,必然会引起大量的磁盘 IO 操作,可能会严重影响性能。

日志追加方式AOF(Append Only File)

这种方式 redis 会将每一个收到的写命令都通过 write 函数追加到文件中(默认appendonly.aof)。当 redis 重启时会通过重新执行文件中保存的写命令来在内存中重建整个数据库的内容。当然由于操作系统会在内核中缓存 write 做的修改,所以可能不是立即写到磁盘上。这样的持久化还是有可能会丢失部分修改。不过我们可以通过配置文件告诉redis 我们想要通过 fsync 函数强制操作系统写入到磁盘的时机。有三种方式如下(默认是 :每秒 fsync 一次)

  • appendonly yes //启用日志追加持久化方式
  • appendfsync always //每次收到写命令就立即强制写入磁盘,最慢的,但是保证完全 的持久化,不推荐使用
  • appendfsync everysec //每秒钟强制写入磁盘一次,在性能和持久化方面做了很好的折 中,推荐
  • appendfsync no //完全依赖操作系统,性能最好,持久化没保证

日志追加方式同时带来了另一个问题。持久化文件会变的越来越大。为了压缩这种持久化方式的日志文件 。redis 提供了bgrewriteaof 命令。收到此命令 redis 将使用与快照类似的方式将内存中的数据以命令的方式保存到临时文件中,而不是把原来那种所有的操作记录,最后替换原来的持久化日志文件。 我们可以配置aof文件的大小扩张倍数即重写。

比如说redis现在做一个定时器,轮询100下,那其实我们想要的结果是最后的数据,但是AOF会把整个过程记录下来,所以AOF文件大小会不断增大。怎么办呢? BGREWRITEAOF命令来重写 1>调用fork(),创建一个子进程 2>子进程把新的AOF写到一个临时文件里,不依赖原来的AOF文件 3>主进程持续将新的变动同时写到内存和原来的AOF里 4>主进程获取子进程重写AOF的信号之后,往新的AOF同步增量变动 5>使用新的AOF文件替换旧的AOF文件

RDB和AOF的优缺点比较

1> RDB优点:全量数据快照,文件小,恢复快 RDB缺点:无法保存最近一次快照之后的数据 2> AOF优点:可读性高,适合保存增量数据,数据不易丢失 AOF缺点:文件体积大,恢复时间长

RDB-AOF混合 redis4.0之后推出RDB-AOF混合持久化方式,并且是默认配置。 BGSAVE做镜像全量持久化,AOF做增量持久化。 在redis实例重启时,会使用BGSAVE持久化文件重新构建内容,再使用AOF重放近期的操作指令。

redis主从简介

Redis 支持将数据同步到多台从库上,这种特性对提高 读取性能非常有益。

  • master 可以有多个 slave。
  • 除了多个 slave 连到相同的 master 外,slave 也可以连接其它 slave 形成图状结构。
  • 主从复制不会阻塞 master。也就是说当一个或多个 slave 与 master 进行初次同步数据
    时,master 可以继续处理客户端发来的请求。
  • 主从复制可以用来提高系统的可伸缩性,我们可以用多个 slave 专门用于客户端的读请求,比如 sort 操作可以使用 slave 来处理。也可以用来做简单的数据冗余。
  • 可以在 master 禁用数据持久化,只需要注释掉 master 配置文件中的所有 save 配置,然 后只在 slave 上配置数据持久化

redis主从原理

当设置好 slave 服务器后,slave 会建立和 master 的连接,然后发送 sync 命令。无论是第一次同步建立的连接还是连接断开后的重新连接,master 都会启动一个后台进程,将数据库快照保存到文件中,同时 master 主进程会开始收集新的写命令并缓存起来。后台进程完成写文件后,master 就发送文件给 slave,slave 将文件保存到磁盘上,然后加载到内存恢复数据库快照到 slave 上。接着 master 就会把缓存的命令转发给 slave。而且后续 master 收到的写命令都会通过开始建立的连接发送给slave。从master到slave的同步数据的命令和从客户端发送的命令使用相同的协议格式。当 master 和 slave 的连接断开时 slave 可以自动重新建立连接。如果 master 同时收到多个 slave 发来的同步连接命令,只会启动一个进程来写数据库镜像,然后发送给所有 slave。
配置 slave 服务器很简单,只需要在配置文件中加入如下配置:
slaveof 192.168.1.1 6379 #指定 master 的 ip 和端口

redis事务

如果是在入队时报错,那么都不会执行;比如入队时一个命令参数错误,就会把队列清空,回到正常模式。 exec命令后,开始执行队列中的命令,执行中发生错误,并不会把原来的回滚,而是继续执行后面的命令。

Redis 通过 MULTI 、DISCARD 、EXEC 和 WATCH 四个命令来实现事务功能。
事务提供了一种“将多个命令打包,然后一次性、按顺序地执行”的机制,并且事务在执行的期间不会主动中断——服务器在执行完事务中的所有命令之后,才会继续处理其他客户端的其他命令。
另外,Redis的事务是不可嵌套的,当客户端已经处于事务状态,而客户端又再向服务器发送 MULTI 时,服务器只是简单地向客户端发送一个错误,然后继续等待其他命令的入队。

redis事务流程

一个事务从开始到执行会经历以下三个阶段:

  1. 开始事务。//MUTI命令,这个命令唯一做的就是,将客户端的 REDIS_MULTI 选项打开,让客户端从非事务状态切换到事务状态。
  2. 命令入队。//…要执行的命令,当客户端处于非事务状态下时,所有发送给服务器端的命令都会立即被服务器执行,但是当客户端进入事务状态之后,服务器在收到来自客户端的命令时,不会立即执行命令,而是将这些命令全部放进一个事务队列里,然后返回 QUEUED ,表示命令已入队。
  3. 执行事务。//EXEC命令,前面说到,当客户端进入事务状态之后,客户端发送的命令就会被放进事务队列里。但其实并不是所有的命令都会被放进事务队列,其中的例外就是 EXEC 、DISCARD 、MULTI 和 WATCH 这四个命令——当这四个命令从客户端发送到服务器时,它们会像客户端处于非事务状态一样,直接被服务器执行

DISCARD、WATCH命令

DISCARD代表取消事务,它清空客户端的整个事务队列,然后将客户端从事务状态调整回非事务状态,最后返回字符串 OK 给客户端,说明事务已被取消。
WATCH 命令用于在事务开始之前监视任意数量的键:当调用 EXEC 命令执行事务时,如果任意一个被监视的键已经被其他客户端修改了,那么整个事务不再执行,直接返回失败。(不是单线程吗?怎么还有别的客户端改?其实你打开multi开始入队,并不耽误别的客户端改)

redis> WATCH name  
OK  
redis> MULTI
OK
redis> SET name peter
QUEUED
redis> EXEC

当一个客户端结束它的事务时,无论事务是成功执行,还是失败,watch内容都会被清空。

事务的 ACID 性质

Redis 事务保证了其中的一致性(C)和隔离性(I),但并不保证原子性(A)和持久性(D)。

原子性(Atomicity

单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的。
如果一个事务队列中的所有命令都被成功地执行,那么称这个事务执行成功。另一方面,如果 Redis 服务器进程在执行事务的过程中被停止——比如接到 KILL 信号、宿主机器停机,等等,那么事务执行失败。当事务失败时,Redis 也不会进行任何的重试或者回滚动作。

一致性(Consistency

Redis 的一致性问题可以分为三部分来讨论:入队错误、执行错误、Redis 进程被终结。

  1. 入队错误 在命令入队的过程中,如果客户端向服务器发送了错误的命令,比如命令的参数数量不对,等等,那么服务器将向客户端返回一个出错信息,并且将客户端的事务状态设为REDIS_DIRTY_EXEC,因此,带有不正确入队命令的事务不会被执行,也不会影响数据库的一致性。
  2. 执行错误 如果命令在事务执行的过程中发生错误,比如说,对一个不同类型的 key 执行了错误的操作, 那么 Redis 只会将错误包含在事务的结果中,这不会引起事务中断或整个失败,不会影响已执 行事务命令的结果,也不会影响后面要执行的事务命令,所以它对事务的一致性也没有影响。
  3. Redis 进程被终结 如果 Redis 服务器进程在执行事务的过程中被其他进程终结,或者被管理员强制杀死,那么根 据 Redis 所使用的持久化模式,可能有以下情况出现:
    • 内存模式:如果 Redis 没有采取任何持久化机制,那么重启之后的数据库总是空白的,所 以数据总是一致的。
    • RDB 模式:在执行事务时,Redis 不会中断事务去执行保存 RDB 的工作,只有在事务执 行之后,保存 RDB 的工作才有可能开始。所以当 RDB 模式下的 Redis 服务器进程在事 务中途被杀死时,事务内执行的命令,不管成功了多少,都不会被保存到 RDB 文件里。 恢复数据库需要使用现有的 RDB 文件,而这个 RDB 文件的数据保存的是最近一次的数 据库快照(snapshot),所以它的数据可能不是最新的,但只要 RDB 文件本身没有因为 其他问题而出错,那么还原后的数据库就是一致的。
    • AOF 模式:因为保存 AOF 文件的工作在后台线程进行,所以即使是在事务执行的中途, 保存 AOF 文件的工作也可以继续进行,因此,根据事务语句是否被写入并保存到 AOF 文件,有以下两种情况发生:
  • 如果事务语句未写入到 AOF 文件,或 AOF 未被 SYNC 调用保存到磁盘,那么当进 程被杀死之后,Redis 可以根据最近一次成功保存到磁盘的 AOF 文件来还原数据库,只 要 AOF 文件本身没有因为其他问题而出错,那么还原后的数据库总是一致的,但其中的 数据不一定是最新的。
  • 如果事务的部分语句被写入到 AOF 文件,并且 AOF 文件被成功保存,那么不完整的 事务执行信息就会遗留在 AOF 文件里,当重启 Redis 时,程序会检测到 AOF 文件并不 完整,Redis 会退出,并报告错误。需要使用 redis-check-aof 工具将部分成功的事务命令 移除之后,才能再次启动服务器。还原之后的数据总是一致的,而且数据也是最新的(直 到事务执行之前为止)。

隔离性(Isolation)

Redis 是单进程程序,并且它保证在执行事务时,不会对事务进行中断,事务可以运行直到执 行完所有事务队列中的命令为止。因此,Redis 的事务是总是带有隔离性的。

持久性(Durability)

因为事务不过是用队列包裹起了一组 Redis 命令,并没有提供任何额外的持久性功能,所以事 务的持久性由 Redis 所使用的持久化模式决定

redis淘汰策略

相关知识:redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略(回收策略)。redis 提供 6种数据淘汰策略:

volatile-lru:从已设置过期时间的数据集(server.db[i].expires)中挑选最近最少使用的数据淘汰

volatile-ttl:从已设置过期时间的数据集(server.db[i].expires)中挑选将要过期的数据淘汰

volatile-random:从已设置过期时间的数据集(server.db[i].expires)中任意选择数据淘汰

allkeys-lru:从数据集(server.db[i].dict)中挑选最近最少使用的数据淘汰

allkeys-random:从数据集(server.db[i].dict)中任意选择数据淘汰

no-enviction(驱逐):禁止驱逐数据,内存不足就报错(默认的策略)