01
—
前言
Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
Redis是一个开源(BSD许可)的,用C语言编写的基于内存的数据结构存储系统(是一个高性能的 key-value存储系统)。而且会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,实现数据的持久化。Redis可以用在数据库,缓存和消息中间件。
Redis作为一种高性能的key-value内存缓存数据库,在各大互联网公司的面试中也是经常被问到。
02
—
常用基本数据类型
Redis支持丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
1、String 字符串
字符串有C字符串和SDS字符串两种,C字符串里面不能包含空字符,所以C字符串只能保存文本数据,不能保存图片、音频、视频、压缩文件等二进制数据。
SDS是二进制安全的字符串,不仅可以保存文本数据,还可以保存任意格式的二进制数据。
总结:Redis只会使用C字符串作为字面量,在大多数情况下,Redis使用SDS作为字符串表示。
比起C字符串,SDS具有以下优点:
2、链表List
List结构为链表提供了表头指针head、表尾指针tail,以及链表长度计数器len,特性如下:
双端:链表节点带有prev和next指针,获取某个节点的前置节点和后置节点的复杂度都是O(1)
无环:表头节点的prev指针额表尾节点的next指针都指向null,对链表的访问以null为终点
带表头指针和表尾指针:通过list结构的head指针和tail指针,程序获取链表的表头节点和表尾节点的复杂度为O(1)
带链表长度计数器:程序使用list结构的len属性来对list持有的链表节点进行计数,程序获取链表节点数量的复杂度为O(1)
多态:链表节点使用void*指针来保存节点的值,并且可以通过list结构的dup、free、match、三个属性为节点值设置类型特定的函数,所以链表可以用于保存各种不同类型的值
总结:链表被广泛用于实现Redis的各种功能,比如列表键,发布与订阅,慢查询,监视器等
每个链表节点由一个listNode结构来表示,每个节点都有一个指向前置节点和后置节点的指针,redis的链表是双向链表。
每个链表使用一个list结构来表示,这个结构带有表头节点指针,表尾节点指针,以及链表长度信息
链表表头节点的前置节点和表尾节点的后置节点都指向null,所以redis的链表实现是无环链表。
通过为链表设置不同的类型特定函数,redis的链表可以用于保存各种不同类型的值。
3、字典hash(或map)
又称为符号表、关联数组或映射,是一种用于保存键值对(key和value进行关联)的抽象数据结构。
Redis的字典使用哈希表作为底层实现,一个哈希表里面可以有多个哈希表节点,而每个哈希表节点就保存了字典中的一个键值对。
解决键冲突:当有两个或以上数量的键被分配到了哈希表数组的同一个索引上面时,称这些键发生了冲突。
Redis的哈希表使用链地址法来解决冲突,每个哈希表节点都有一个next指针,多个哈希表节点可以用next指针构成一个单向链表,被分配到同一个索引上的多个节点可以用这个单向链表连接起来,如此解决键冲突。
Rehash:随着操作的不断执行,哈希表报存的键值对会逐渐地增多或减少,为了让哈希表的负载因子维持在一个合理的范围之内,当哈希表保存的键值对数量太多或太少时,程序需要对哈希表进行扩展或收缩。此时就是执行rehash(重新散列)的操作来完成,步骤如下:
以下条件的任意一个被满足时,程序会自动开始对哈希表执行扩展操作:
回顾总结:字典被广泛用于实现redis的各种功能,其中包括数据库和哈希键。
a、Redis中的字典使用哈希表作为底层实现,每个字典带有两个哈希表,一个平时使用,一个仅在进行rehash时使用。
b、当字典被用作数据库的底层实现,或者哈希键的底层实现时,redis使用murmurHash2算法来计算键的哈希值
c、哈希表使用链地址法来解决键冲突,被分配到同一个索引上的多个键值对会连接成一个单向链表
d、在对哈希表进行扩展或收缩操作时,程序需要将现有的哈希表包含的所有键值对redhs到新哈希表里面,并且这个rehash过程并不是一次性地完成的,而是渐进式地完成的。
4、集合set
整数集合是redis用于保存整数值得集合抽象数据结构,它可以保存类型为int16 int32 或int64的整数值,并且保证集合中不会出现重复元素。
整数集合是集合键的底层实现之一。
整数集合的底层实现为数组,这个数组以有序、无重复的方式保存集合元素,在有需要时,程序会根据新添加的元素的类型,改变这个数组的类型。
升级操作作为整数集合带来了操作上的灵活性,并且可能地节约了内存。
整数集合只支持升级操作,不支持降级操作。
5、sorted set 集合
sorted set有序集合的编码可以是ziplist或者skiplist.
Ziplist编码的压缩列表对象使用压缩列表作为底层实现,每个集合元素使用两个紧挨在一起的压缩列表节点来保存,第一个节点保存元素的成员,第二个元素则保存元素的分值。
压缩列表内的集合元素按分值从小到大进行排序,分值较小的元素被放置在靠近表头的方向,分值较大的元素则被放置在靠近表尾的地方。
6、压缩列表
压缩列表是列表键和哈希键的底层实现之一,当一个列表键只包含少量列表项,并且每个列表项要么就是小整数值,要么就是长度比较短的字符串,那么redis就会使用压缩列表来做列表键的底层实现。
03
—
Redis的高可用
3.1 Sentinel哨兵
哨兵是Redis的高可用性解决方案:由一个Sentinel 或多个Sentinel 实例组成的Sentinel 系统可以监视任意多个主服务器,以及这些主服务器属下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器,然后由新的主服务器代替已下线的主服务器继续处理命令请求。
当一个主服务器被判断为客观下线时,监视这个下线主服务器的各个Sentinel会进行协商,选举出一个领头Sentinel 对下线主服务器执行故障转移操作。
3.3 故障转移
在选举产生出领头Sentinel 后,领头Sentinel 将对已下线主服务器执行故障转移操作,该操作包含以下三个步骤:
在已下线主服务器属下的所有从服务器里面,挑选出一个从服务器,并将其转换为主服务器。
让已下线主服务器属下的所有从服务器改为复制新的主服务器。
将已下线主服务器设置为新的主服务器的从服务器,当这个旧的主服务器重新上线时,它就会成为新的主服务器的从服务器。
04
—
Redis持久化
Redis是一个内存数据库,它将自己的数据库状态存储在内存里面,所以如果不想办法将储存在内存中的数据库状态保存到磁盘里面,那么一旦服务器进程退出,服务器中的数据库状态也会消失不见。为了解决这个问题,Redis提供了RDB持久化功能,这个功能可以将Redis在内存中的数据库状态保存奥磁盘里面,避免数据意外丢失。
RDB文件是保存在磁盘里面的,所以即使Redis服务器进程退出,甚至运行Redis服务器的计算机停机,但只要RDB文件仍然存在,Redis服务器就可以用它来还原数据库状态。
RDB文件写入:有2个命令可以用于生成RDB文件:一个是SAVE,一个是BGSAVE。
重点内容:
RDB文件用于保存和还原Redis服务器所有数据库中的所有键值对数据。
SAVE命令由服务器进程直接执行保存操作,所以该命令会阻塞服务器。
BGSAVE命令由子进程执行保存操作,所以该命令不会阻塞服务器。
服务器状态中会保存所有有用save选项设置的保存条件,当任意一个保存条件被满足时,服务器会自动执行BGSAVE命令。
RDB文件是一个经过压缩的二进制文件,由多个部门组成。
对于不同类型的键值对,RDB文件会使用不同的方式来保存他们。
概念:AOF持久化是通过保存Redis服务器所执行的写命令来记录数据库状态的,如图
AOF持久化功能的实现可以分为命令追加、文件写入、文件同步三个步骤。
命令追加:当AOF持久化功能处于打开状态时,服务器在执行完一个写命令后,会以协议格式将被执行的写命令追加到服务器状态的aof_buf缓冲区的末尾:
重点内容:
AOF文件通过保存所有修改数据库的写命令请求来记录服务器的数据库状态
AOF文件中的所有命令都以Redis命令请求协议的格式保存
命令请求会先保存到AOF缓冲区里面,之后再定期写入并同步到AOF文件
Appendfsync选项的不同值对AOF持久化功能的安全性及Redis服务器的性能有很大影响
服务器只要载入并重新执行保存在AOF文件中的命令,就可以还原数据库本来的状态
AOF重写可以产生一个新的AOF文件,这个新的AOF文件和原有的AOF文件所保存的数据库状态一样,但体积更小
AOF重写是一个有歧义的名字,该功能是通过读取数据库中的键值对来实现的,程序无需对现有AOF文件进行任何读入、分析或者写入操作
在执行BGREWIRTEAOF命令时,Redis服务器会维护一个AOF重写缓冲区,该缓冲区会在子进程创建新的AOF文件的工作之后,服务器会将重写缓冲区中的所有内容追加到新的AOF文件的末尾,使得新旧两个AOF文件所保存的数据库状态一致。最后,服务器用新的AOF文件替换旧的AOF文件,以此来完成AOF文件的重写操作。
05
—
生存时间或过期时间
Redis通过expire命令或者pexpire命令,客户端可以以秒或者毫秒精度为数据库中的某个键设置生存时间(Time to Live TTL),在经过指定的秒数或者毫秒数之后,服务器就会自动删除生存时间为0的键
客户端可以通过expire命令或者pexpireat命令,以秒或者毫秒精度给数据库中的某个键设置过期时间(expire time)
5.2 过期键删除
有如下几种删除策略:
06
—
荐语
Redis的知识内容非常丰富,Redis的应用场景也很多,尤其是在高并发和高可用等场景下,它给我们带来很大便利的同时,也极大的保障了系统的性能和安全等。