Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
一,五大数据类型
1,String(字符串)
String 是 redis 最基本的类型。
2,Hash(哈希)
Redis hash 是一个键值(key -> value)对集合。适合用于存储对象。
3,List(链表)
Redis 列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边)。底层是一个链表。
4,Set(集合)
Redis的Set是string类型的无序集合。集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。
5,zset(有序集合)
zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。zset的成员是唯一的,但分数(score)却可以重复。
二,事务
Redis 事务可以一次执行多个命令, 并且带有以下三个重要的保证:
1,批量操作在发送 EXEC 命令前被放入队列缓存。
2,收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
3,在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。
一个事务从开始到执行会经历以下三个阶段:开始事务,命令入队,执行事务。
关系型数据中的事务都是原子性的,而redis 的事务是非原子性的。严格的说Redis的命令是原子性的,而事务是非原子性的,我们要让Redis事务完全具有事务回滚的能力,需要借助于命令WATCH来实现。
Redis使用WATCH命令来决定事务是继续执行还是回滚,那就需要在MULTI之前使用WATCH来监控某些键值对,然后使用MULTI命令来开启事务,执行对数据结构操作的各种命令,此时这些命令入队列。
当使用EXEC执行事务时,首先会比对WATCH所监控的键值对,如果没发生改变,它会执行事务队列中的命令,提交事务;如果发生变化,将不会执行事务中的任何命令,同时事务回滚(全部回滚,没有发生变化的值也会回滚)。当然无论是否回滚,Redis都会取消执行事务前的WATCH命令。
Redis事务相关命令:
- MULTI :开启事务,redis会将后续的命令逐个放入队列中,然后使用EXEC命令来原子化执行这个命令系列。
- EXEC:执行事务中的所有操作命令。
- DISCARD:取消事务,放弃执行事务块中的所有命令。
- WATCH:监视一个或多个key,如果事务在执行前,这个key(或多个key)被其他命令修改,则事务被中断,不会执行事务中的任何命令。
- UNWATCH:取消WATCH对所有key的监视。
通过watch实现事务回滚:
三,消息通知
消息通知分为任务队列和发布订阅模式。
1,任务队列:
任务队列:顾名思义,就是“传递消息的队列”。与任务队列进行交互的实体有两类,一类是生产者(producer),另一类则是消费者(consumer)。生产者将需要处理的任务放入任务队列中,而消费者则不断地从任务独立中读入任务信息并执行。任务队列的好处:松耦合、易于扩展。
实现任务队列,只需让生产者将任务使用LPUSH加入到某个键中,然后另一个消费者不断地使用RPOP命令从该键中取出任务即可。
BLPOP指令可以在队列为空时处于阻塞状态。就不用处于轮询的状态。
当一个队列中有许多任务仍然没有来得及被消费者及时消费时,如果出现紧急的消息,则不得不等待队列中的任务被一一取出,因此,需要实现一个优先级队列,当优先级队列不为空时,消费者优先取出优先级队列中的任务去执行。
BLPOP命令可以同时接收多个键BLPOP key [key ...] timeout
,当所有键(列表类型)都为空时,则阻塞,当其中一个有元素则会从该键返回。==如果多个键都有元素则按照从左到右的顺序取第一个键中的一个元素,因此可以借此特性实现优先级队列。
消费者命令格式:BRPOP|BLPOP KEY [KEY...] TIMEOUT;从key、key1、key2...中获取消息,一次只获取一条,按照从左往右的顺序,只有当key没有消息的时候,才会从key1中获取消息,当key和key1中都没有消息的时候,才会从key2中获取消息。TIMEOUT指定超时时间,当阻塞时间达到TIMEOUT时(单位秒),会终止消息的获取。如果TIMEOUT=0,表示永远不会超时,会一直等待下去。
2,发布/订阅模式
发送者 (pub) 发送消息,订阅者 (sub) 接收消息。Redis 客户端可以订阅任意数量的频道。任务队列解决了一对一的消息生产消费模式 , 发布/订阅模式则是一对多。
PUBLISH : 将信息 message 发送到指定的频道 channel。返回收到消息的客户端数量。
SUBSCRIBE :订阅给指定频道的信息。一旦客户端进入订阅状态,客户端就只可接受订阅相关的命令SUBSCRIBE、PSUBSCRIBE、UNSUBSCRIBE和PUNSUBSCRIBE除了这些命令,其他命令一律失效。
UNSUBSCRIBE:取消订阅指定的频道,如果不指定,则取消订阅所有的频道。
按规则订阅频道消息:PSUBSCRIBE channel[pattern] [channel[pattern]...]
PUNSUBSCRIBE应该注意一下两点:使用PUNSUBSCRIBE命令只能退订通过PSUBSCRIBE命令订阅的规则,不会影响SUBSCRIBE订阅的频道。使用PUNSUBSCRIBE命令退订某个规则时不会将其中通配符展开,而是严格的进行==字符串匹配==,所以PUNSUBSCRIBE *
无法退订PUNSUBSCRIBE channal1.*
规则,而必须使用PUNSUBSCRIBE channal1.*
才能退订
四,管道(Pipeline)
在讲解管道前,我们首先来了解一下redis的交互,redis的一次交互是由客户端发起,由服务端接收,那么我们连续操作一些指令,如下图所示:
客户端无需等待单个请求响应再进行下一次请求,而是可以一次发送多个命令给服务器,服务器处理完后一次性将结果返回给客户端,这种工作方式在Redis中被称作管道。可以可出,通过管道的方式,能够明显的减少请求的次数,在于网络状况不好的环境中,能明显的提生处理的速度。
Redis客户端与服务器之间使用TCP协议进行通信,并且很早就支持管道(pipelining)技术了。Redis 管道 (Pipeline) 本身并不是 Redis 服务器直接提供的技术,这个技术本质上是由客户端提供的,跟服务器没有什么直接的关系。管道技术其实已经非常成熟并且得到广泛应用了,例如POP3协议由于支持管道技术,从而显著提高了从服务器下载邮件的速度。
在Redis客户端中,客户端只能处于一种模式下,要么是普通的模式,一次发送一个请求,要么处于管道模式,二者不能混用。在普通模式下,命令写入到outputStream缓冲区后会立即触发flush,而在管道模式模式下,flush的触发需要显示调用Pipeline.sync()函数,因此可以实现一次发送多个命令。
总结:
- 使用管道技术可以显著提升Redis处理命令的速度,其原理就是将多条命令打包,只需要一次网络开销,在服务器端和客户端各一次
read()
和write()
系统调用,以此来节约时间。 - 管道中的命令数量要适当,并不是越多越好。
- Redis2.6版本以后,脚本在大部分场景中的表现要优于管道。
五,持久化
Redis 的数据全部在内存里,如果突然宕机,数据就会全部丢失,因此必须有一种机制来保证 Redis 的数据不会因为故障而丢失,这种机制就是 Redis 的持久化机制。
Redis 的持久化机制有两种,第一种是RDB快照,第二种是 AOF 日志。快照是一次全量备份,AOF 日志是连续的增量备份。快照是内存数据的二进制序列化形式,在存储上非常紧凑,而 AOF 日志记录的是内存数据修改的指令记录文本。
1,RDB快照
RDB快照是某个时间点的一次全量数据备份,是二进制文件,在存储上非常紧凑。原理是将Reids在内存中的数据库记录定时dump到磁盘上的RDB持久化(生成rdb文件)。
1.1 触发机制
RDB持久化触发机制分为:手动触发和自动触发。
手动触发:
save()命令,会阻塞当前服务器,直到RDB完成为止,如果数据量大的话会造成长时间的阻塞,线上环境一般禁止使用。
bgsave()命令,就是background save,执行bgsave命令时Redis主进程会fork一个子进程来完成RDB的过程。完成后自动结束(操作系统的多进程Copy On Write机制,简称COW)。所以Redis主进程阻塞时间只有fork阶段的那一下。相对于save,阻塞时间很短。
自动触发:
场景一:配置redis.conf,触发规则,自动执行。
场景二:执行shutdown命令关闭服务器时,如果没有开启AOF持久化功能,那么会自动执行一次bgsave
场景三:主从同步(slave和master建立同步机制)
1.2 RDB执行流程
Redis 使用操作系统的多进程 cow(Copy On Write) 机制来实现RDB快照持久化。
- 执行bgsave命令的时候,Redis主进程会检查是否有子进程在执行RDB/AOF持久化任务,如果有的话,直接返回
- Redis主进程会fork一个子进程来执行执行RDB操作,fork操作会对主进程造成阻塞(影响Redis的读写),fork操作完成后会发消息给主进程,从而不再阻塞主进程。(阻塞仅指主进程fork子进程的过程,后续子进程执行操作时不会阻塞)
- RDB子进程会根据Redis主进程的内存生成临时的快照文件,持久化完成后会使用临时快照文件替换掉原来的RDB文件。(该过程中主进程的读写不受影响,但Redis的写操作不会同步到主进程的主内存中,而是会写到一个临时的内存区域作为一个副本)
- 子进程完成RDB持久化后会发消息给主进程,通知RDB持久化完成(将上阶段内存副本中的增量写数据同步到主内存)
1.3 RDB的优缺点
优点:RDB文件小,非常适合定时备份,用于灾难恢复。Redis加载RDB文件的速度比AOF快很多,因为RDB文件中直接存储的时内存数据,而AOF文件中存储的是一条条命令,需要重演命令。
缺点:DB无法做到实时持久化,若在两次bgsave间宕机,则会丢失区间(分钟级)的增量数据,不适用于实时性要求较高的场景。RDB的cow机制中,fork子进程属于重量级操作,并且会阻塞redis主进程。
2、AOF(append only file)日志
AOF日志是持续增量的备份,是基于写命令存储的可读的文本文件。AOF日志会在持续运行中持续增大,由于Redis重启过程需要优先加载AOF日志进行指令重放以恢复数据,恢复时间会无比漫长。所以需要定期进行AOF重写,对AOF日志进行瘦身。目前AOF是Redis持久化的主流方式。
2.1 开启方式
AOF默认是关闭的,通过redis.conf配置文件进行开启。
2.2 重写(rewrite)机制
AOF日志会在持续运行中持续增大,需要定期进行AOF重写,对AOF日志进行瘦身。
AOF Rewrite: 虽然是“压缩”AOF文件的过程,但并非采用“基于原AOF文件”来重写或压缩,而是采取了类似RDB快照的方式:基于Copy On Write,全量遍历内存中数据,然后逐个序列到AOF文件中。因此AOF rewrite能够正确反应当前内存数据的状态。
重写过程中,对于新的变更操作将仍然被写入到原AOF文件中,同时这些新的变更操作也会被Redis收集起来。当内存中的数据被全部写入到新的AOF文件之后,收集的新的变更操作也将被一并追加到新的AOF文件中。然后将新AOF文件重命名为appendonly.aof,使用新AOF文件替换老文件,此后所有的操作都将被写入新的AOF文件。
2.3 触发机制
和RDB类似,AOF触发机制也分为:手动触发和自动触发。
手动触发 直接调用bgrewriteaof命令。
根据auto-aof-rewrite-min-size和auto-aof-rewrite-percentage参数确定自动触发时机。
2.4 AOF的优缺点
优点:AOF只是追加写日志文件,对服务器性能影响较小,速度比RDB要快,消耗的内存较少。数据完整性高。
缺点:AOF方式生成的日志文件太大,需要不断AOF重写,进行瘦身。AOF重演命令式的恢复数据,速度显然比RDB要慢。
3、Redis 4.0 混合持久化
Redis 4.0 为了解决这个问题,带来了一个新的持久化选项——混合持久化。将 rdb 文件的内容和增量的 AOF 日志文件存在一起。这里的 AOF 日志不再是全量的日志,而是自持久化开始到持久化结束的这段时间发生的增量 AOF 日志,通常这部分 AOF 日志很小。
大量数据使用粗粒度(时间上)的rdb快照方式,性能高,恢复时间快。
增量数据使用细粒度(时间上)的AOF日志方式,尽量保证数据的不丢失。
在 Redis 重启的时候,可以先加载 rdb 的内容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,重启效率因此大幅得到提升。
六,集群
1,主从模式(master / slave)
一般是一主多从,读写分离,容灾快速恢复。
* 主数据库可以进行读写操作,当读写操作导致数据变化时会自动将数据同步给从数据库。
* 从数据库一般都是只读的,并且接收主数据库同步过来的数据。
* 一个master可以拥有多个slave,但是一个slave只能对应一个master。
* slave挂了不影响其他slave的读和master的读和写,重新启动后会将数据从master同步过来(主动请求主服务器做数据同步)。
* master挂了以后,不影响slave的读,但redis不再提供写服务,master重启后redis将重新对外提供写服务。
* master挂了以后,不会在slave节点中重新选一个master。
工作机制(复制原理):
当slave启动后,主动向master发送SYNC命令。master接收到SYNC命令后在后台保存快照(RDB持久化)和缓存保存快照这段时间的命令,然后将保存的快照文件和缓存的命令发送给slave。slave接收到快照文件和命令后加载快照文件和缓存的执行命令。
复制初始化后,master每次接收到的写命令都会同步发送给slave(增量复制),保证主从数据一致性。
缺点:从上面可以看出,master节点在主从模式中唯一,若master挂掉,则redis无法对外提供写服务。
2,哨兵模式(Sentinel)
主从模式的弊端就是不具备高可用性,当master挂掉以后,Redis将不能再对外提供写入操作,因此sentinel应运而生。sentinel中文含义为哨兵,顾名思义,它的作用就是监控redis集群的运行状况。
特点:
* sentinel模式是建立在主从模式的基础上,如果只有一个Redis节点,sentinel就没有任何意义
* 当master挂了以后,sentinel会在slave中选择一个做为master,并修改它们的配置文件,其他slave的配置文件也会被修改,比如slaveof属性会指向新的master
* 当master重新启动后,它将不再是master而是做为slave接收新的master的同步数据
* sentinel因为也是一个进程有挂掉的可能,所以sentinel也会启动多个形成一个sentinel集群
* 多sentinel配置的时候,sentinel之间也会自动监控
* 当主从模式配置密码时,sentinel也会同步将配置信息修改到配置文件中,不需要担心
* 一个sentinel或sentinel集群可以管理多个主从Redis,多个sentinel也可以监控同一个redis
* sentinel最好不要和Redis部署在同一台机器,不然Redis的服务器挂了以后,sentinel也挂了
工作机制:
* 每个sentinel以每秒钟一次的频率向它所知的master,slave以及其他sentinel实例发送一个 PING 命令
* 如果一个实例距离最后一次有效回复 PING 命令的时间超过 down-after-milliseconds 选项所指定的值, 则这个实例会被sentinel标记为主观下线。
* 如果一个master被标记为主观下线,则正在监视这个master的所有sentinel要以每秒一次的频率确认master的确进入了主观下线状态
* 当有足够数量的sentinel(大于等于配置文件指定的值)在指定的时间范围内确认master的确进入了主观下线状态, 则master会被标记为客观下线
* 在一般情况下, 每个sentinel会以每 10 秒一次的频率向它已知的所有master,slave发送 INFO 命令
* 当master被sentinel标记为客观下线时,sentinel向下线的master的所有slave发送 INFO 命令的频率会从 10 秒一次改为 1 秒一次
* 若没有足够数量的sentinel同意master已经下线,master的客观下线状态就会被移除;
若master重新向sentinel的 PING 命令返回有效回复,master的主观下线状态就会被移除
当使用sentinel模式的时候,客户端就不要直接连接Redis,而是连接sentinel的ip和port,由sentinel来提供具体的可提供服务的Redis实现,这样当master节点挂掉以后,sentinel就会感知并将新的master节点提供给使用者。
集群模式(cluster):
Redis集群实现了redis的水平扩容,即启动N个redis节点,将整个数据库分布存储在了这N个节点上。哨兵模式基本可以满足一般生产的需求,具备高可用性。但是当数据量过大到一台服务器存放不下的情况时,主从模式或哨兵模式就不能满足需求了,这个时候需要对存储的数据进行分片,将数据存储到多个Redis实例中。集群模式的出现就是为了解决单机Redis容量有限的问题,将Redis的数据根据一定的规则分配到多台机器。数据分布式存储,每个节点存储的数据不同。
使用集群,只需要将redis配置文件中的cluster-enable
配置打开即可。每个集群中至少需要三个主数据库才能正常运行,新增节点非常方便。
集群模式特点:
* 多个redis节点网络互联,数据共享
* 所有的节点都是一主一从(也可以是一主多从),其中从不提供服务,仅作为备用
* 不支持同时处理多个key(如MSET/MGET),因为redis需要把key均匀分布在各个节点上,
并发量很高的情况下同时创建key-value会降低性能并导致不可预测的行为
* 支持在线增加、删除节点
* 客户端可以连接任何一个主节点进行读写
实例:
Redis Cluster是一种服务器Sharding技术,3.0版本开始正式提供。