Redis学习笔记

每周一主题,每周一进步~

大家好,本周的主题是Redis,小E对一周来的学习做个笔记和总结,希望能对从零开始学Redis的小伙伴有所帮助。


一、Redis基础:

1.简介:

    缓存大致可以分为两类,一种是应用内缓存,比如Map(简单的数据结构),以及EH Cache(Java第三方库),另一种就是缓存组件,比如Memached,Redis。

    Redis是一种支持Key-Value等多种数据结构的内存存储系统,也可以称之为No-SQL型数据库。Redis数据均运行在内存中,我们知道,从内存中随机读取数据速度是从硬盘读取的10万倍以上,因此,Redis成为当前使用率较高的缓存策略之一。

2.存储结构:

大家一定对字典类型的数据结构非常熟悉,比如map ,通过key value的方式存储的结构。 redis的全称是remote dictionary server(远程字典服务器),它以字典结构存储数据,并允许其他应用通过TCP协议读写字典中的内容。数据结构如下:

Redis学习笔记_第1张图片
(1)Redis存储结构

3.优势:

    • 性能极高 : Redis读的速度能达到110000次/s,写的速度可达81000次/s 。

    • 支持数据类型丰富:

        1.string(字符串):字符串类型是redis中最基本的数据类型,它能存储任何形式的字符串,包括二进制数据。你可以用它存储用户的邮箱、json化的对象甚至是图片。一个字符类型键允许存储的最大容量是512M.

        在Redis内部,String类型通过 int、SDS(simple dynamic string)作为结构存储,int用来存放整型数据,sds存放字节/字符串和浮点型数据。

        2.hash(哈希): 类似于Java中的hashmap,是一个key+Entry的数据结构

        3.list(列表):列表类型(list)可以存储一个有序的字符串列表,常用的操作是向列表两端添加元素或者获得列表的某一个片段。列表类型内部使用双向链表实现,所以向列表两端添加元素的时间复杂度为O(1), 获取越接近两端的元素速度就越快。这意味着即使是一个有几千万个元素的列表,获取头部或尾部的10条记录也是很快的.

        redis3.2之前,List类型的value对象内部以linkedlist或者ziplist来实现, 当list的元素个数和单个元素的长度比较小的时候,Redis会采用ziplist(压缩列表)来实现来减少内存占用。否则就会采用linkedlist(双向链表)结构。            redis3.2之后,采用的一种叫quicklist的数据结构来存储list,列表的底层都由quicklist实现。

        这两种存储方式都有优缺点,双向链表在链表两端进行push和pop操作,在插入节点上复杂度比较低,但是内存开销比较大; ziplist存储在一段连续的内存上,所以存储效率很高,但是插入和删除都需要频繁申请和释放内存;quicklist仍然是一个双向链表,只是列表的每个节点都是一个ziplist,其实就是linkedlist和ziplist的结合,quicklist中每个节点ziplist都能够存储多个数据元素.

        4.set(集合):集合类型中,每个元素都是不同的,也就是不能有重复数据,同时集合类型中的数据是无序的。一个集合类型键可以存储至多232-1个 。集合类型和列表类型的最大的区别是有序性和唯一性集合类型的常用操作是向集合中加入或删除元素、判断某个元素是否存在。由于集合类型在redis内部是使用的值为空的散列表(hash table),所以这些操作的时间复杂度都是O(1).

        5.zset(有序集合):有序集合类型,顾名思义,和前面讲的集合类型的区别就是多了有序的功能在集合类型的基础上,有序集合类型为集合中的每个元素都关联了一个分数,这使得我们不仅可以完成插入、删除和判断元素是否存在等集合类型支持的操作,还能获得分数最高(或最低)的前N个元素、获得指定分数范围内的元素等与分数有关的操作。虽然集合中每个元素都是不同的,但是他们的分数却可以相同

正是因为Redis支持多种数据结构,才使得Redis具有比一般NoSQL数据库更多的特性支持。

    • 原子操作: Redis的所有操作都是原子性的,要么成功执行要么失败完全不执行。

    • 支持持久化操作:内存中的数据在遭遇一次停机或者重启之后就会全部置0,因此会大大影响执行效率,Redis支持多种本地化存储方案,RDB&AOF 等等

4.Redis 安装 :

redis约定次版本号(第一个小数点后的数字)为偶数版本是稳定版,如2.8、3.0, 奇数版本为非稳定版,生产环境需要使用稳定版;目前最新版本为Redis4.0.9。

    1. 下载redis的安装包:wget http://download.redis.io/releases/redis-4.0.2.tar.gz

    2. tar -zxvf 解压 : tar xzf redis-4.0.2.tar.gz

    3. cd 到解压后的目录 : cd redis-4.0.2

    4. 执行make 完成编译:make install

5.启动停止redis:

安装完redis后的下一步就是怎么去启动和访问,我们首先先了解一下Redis包含哪些可执行文件:


Redis学习笔记_第2张图片
(2)Redis常用可执行文件

1.    我们常用的命令是redis-server和redis-cli:redis-server ../redis.conf 服务器默认启用6379端口。

2.    以守护进程的方式启动,需要修改redis.conf配置文件中daemonize yes

3.    停止redis:redis-cli SHUTDOWN。考虑到redis有可能正在将内存的数据同步到硬盘中,强行终止redis进程可能会导致数据丢失,正确停止redis的方式应该是向Redis发送SHUTDOW命令当redis收到SHUTDOWN命令后,会先断开所有客户端连接,然后根据配置执行持久化,最终完成退出


二、Redis原理分析

1.Redis是单进程单线程,性能为什么这么快:

•    完全基于内存:因为Redis是基于内存的操作,CPU不是Redis的瓶颈,Redis的瓶颈最有可能是机器内存的大小或者网络带宽。既然单线程容易实现,而且CPU不会成为瓶颈,那就顺理成章地采用单线程的方案了。

•    数据结构简单,对数据操作也简单

•    使用多路 I/O 复用模型:多路 I/O 复用模型是利用select、poll、epoll可以同时监察多个流的 I/O 事件的能力,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有I/O事件时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流(epoll是只轮询那些真正发出了事件的流),并且只依次顺序的处理就绪的流,这种做法就避免了大量的无用操作。这里“多路”指的是多个网络连接,“复用”指的是复用同一个线程。

简单的说,就是无论有多少网络链接过来,我就只有一个线程处理,按顺序排队,当然处理速度是相当快的,不会因为处理速度导致整个队列的阻塞。

2.Redis的数据持久化:

Redis支持两种方式的持久化,一种是RDB方式、另一种是AOF(append-only-file)方式。前者会根据指定的规则“定时”将内存中的数据存储在硬盘上,而后者在每次执行命令后将命令本身记录下来。两种持久化方式可以单独使用其中一种,也可以将这两种方式结合使用:

•  RDB方式:redis data base , 用于做内存数据的快照,此过程会fork一个子进程来操作,因此不用担心会影响当前的进程。

    RDB触发条件有以下几种:

    1.根据配置规则进行自动快照

    2.用户执行SAVE(阻塞, 只管保存快照,其他的等待)或者BGSAVE(异步)命令

    3.执行FLUSHALL命令

    4.执行复制(replication)时

    RDB 的优缺点

    优点:

    1.适合大规模的数据恢复。

    2.如果业务对数据完整性和一致性要求不高,RDB是很好的选择,因为在两次同步期间的数据有可能会丢失。

    缺点:

    1.数据的完整性和一致性不高,因为RDB可能在最后一次备份时宕机了。

    2.备份时占用内存,因为Redis 在备份时会独立创建一个子进程,将数据写入到一个临时文件(此时内存中的数据是原来的两倍),最后再将临时文件替换之前的备份文件。

•    AOF方式 :

Redis 默认不开启。它的出现是为了弥补RDB的不足(数据的不一致性),所以它采用日志的形式来记录每个写操作,并追加到文件中。Redis 重启的会根据日志文件的内容将写指令从前到后执行一次以完成数据的恢复工作。

AOF的重写原理:

Redis 可以在 AOF 文件体积变得过大时,自动地在后台对 AOF 进行重写: 重写后的新 AOF 文件包含了恢复当前数据集所需的最小命令集合。

重写的流程:主进程会fork一个子进程出来进行AOF重写,这个重写过程并不是基于原有的aof文件来做的,而是有点类似于快照的方式,全量遍历内存中的数据,然后逐个序列到aof文件中。在fork子进程这个过程中,服务端仍然可以对外提供服务,那这个时候重写的aof文件的数据和redis内存数据不一致了怎么办?不用担心,这个过程中,主进程的数据更新操作,会缓存到aof_rewrite_buf中,也就是单独开辟一块缓存来存储重写期间收到的命令,当子进程重写完以后再把缓存中的数据追加到新的aof文件。当所有的数据全部追加到新的aof文件中后,把新的aof文件重命名为,此后所有的操作都会被写入新的aof文件。

AOF 的优缺点

    优点:数据的完整性和一致性更高

    缺点:因为AOF记录的内容多,文件会越来越大,数据恢复也会越来越慢


三、分布式Redis:

1.集群

先来简单了解下redis中提供的集群策略, 虽然redis有持久化功能能够保障redis服务器宕机也能恢复并且只有少量的数据损失,但是由于所有数据在一台服务器上,如果这台服务器出现硬盘故障,那就是有备份也仍然不可避免数据丢失的问题。在实际生产环境中,我们不可能只使用一台redis服务器作为我们的缓存服务器,必须要多台实现集群,避免出现单点故障。

2.主从复制

复制的作用是把redis的数据库复制多个副本部署在不同的服务器上,如果其中一台服务器出现故障,也能快速迁移到其他服务器上提供服务。 复制功能可以实现当一台redis服务器的数据更新后,自动将新的数据同步到其他服务器上主从复制就是我们常见的master/slave模式, 主数据库可以进行读写操作,当写操作导致数据发生变化时会自动将数据同步给从数据库。而一般情况下,从数据库是只读的,并接收主数据库同步过来的数据,一个主数据库可以有多个从数据库。


Redis学习笔记_第3张图片
(3)Redis主从结构

3.配置

在redis中配置master/slave是非常容易的,只需要在从数据库的配置文件中加入slaveof 主数据库地址 端口。 而master 数据库不需要做任何改变:

Redis学习笔记_第4张图片
(4)Redis主从配置

4.原理:

•    全量复制:

一般发生在Slave初始化阶段,这时Slave需要将Master上的所有数据都复制一份。具体步骤如下:


Redis学习笔记_第5张图片
(5)Redis主从全量复制原理

完成上面几个步骤后就完成了slave服务器数据初始化的所有操作,savle服务器此时可以接收来自用户的读请求。master/slave 复制策略是采用乐观复制,也就是说可以容忍在一定时间内master/slave数据的内容是不同的,但是两者的数据会最终同步。具体来说,redis的主从同步过程本身是异步的,意味着master执行完客户端请求的命令后会立即返回结果给客户端,然后异步的方式把命令同步给slave。这一特征保证启用master/slave后 master的性能不会受到影响。

•    增量复制:

从redis 2.8开始,就支持主从复制的断点续传,如果主从复制过程中,网络连接断掉了,那么可以接着上次复制的地方,继续复制下去,而不是从头开始复制一份master node会在内存中创建一个backlog,master和slave都会保存一个replica offset还有一个master id,offset就是保存在backlog中的。如果master和slave网络连接断掉了,slave会让master从上次的replica offset开始继续复制但是如果没有找到对应的offset,那么就会执行一次全量同步。

5.哨兵机制:

在前面讲的master/slave模式,在一个典型的一主多从的系统中,slave在整个体系中起到了数据冗余备份和读写分离的作用。当master遇到异常终端后,需要从slave中选举一个新的master继续对外提供服务,这种机制在前面提到过N次,比如在zk中通过leader选举、kafka中可以基于zk的节点实现master选举。所以在redis中也需要一种机制去实现master的决策,redis并没有提供自动master选举功能,而是需要借助一个哨兵来进行监控。

顾名思义,哨兵的作用就是监控Redis系统的运行状况,它的功能包括两个

1. 监控master和slave是否正常运行

2. master出现故障时自动将slave数据库升级为master

哨兵是一个独立的进程,使用哨兵后的架构图:


Redis学习笔记_第6张图片
(6)Redis哨兵机制

为了解决master选举问题,又引出了一个单点问题,也就是哨兵的可用性如何解决,在一个一主多从的Redis系统中,可以使用多个哨兵进行监控任务以保证系统足够稳定。此时哨兵不仅会监控master和slave,同时还会互相监控;这种方式称为哨兵集群,哨兵集群需要解决故障发现、和master决策的协商机制问题:


Redis学习笔记_第7张图片
(7)Redis高可用哨兵机制

sentinel节点之间会因为共同监视同一个master从而产生了关联,一个新加入的sentinel节点需要和其他监视相同master节点的sentinel相互感知,首先

1. 需要相互感知的sentinel都向他们共同监视的master节点订阅channel:sentinel:hello

2. 新加入的sentinel节点向这个channel发布一条消息,包含自己本身的信息,这样订阅了这个channel的sentinel

就可以发现这个新的sentinel

3. 新加入的sentinel和其他sentinel节点建立长连接

sentinel节点会定期向master节点发送心跳包来判断存活状态,一旦master节点没有正确响应,sentinel会把master设置为“主观不可用状态”,然后它会把“主观不可用”发送给其他所有的sentinel节点去确认,当确认的sentinel节点数大于>quorum时,则会认为master是“客观不可用”,接着就开始进入选举新的master流程;但是这里又会遇到一个问题,就是sentinel中,本身是一个集群,如果多个节点同时发现master节点达到客观不可用状态,那谁来决策选择哪个节点作为maste呢?这个时候就需要从sentinel集群中选择一个leader来做决策。这里会基于投票算法,只要保证过半数节点通过提议即可。


四、总结:

    本周对Redis做了一个系统的理论学习,对于实操环节,还需要进一步的实践,可能过程中会遇到很多问题,后面会将实操过程中遇到的问题做个小结,供大家一起分享,谢谢~

你可能感兴趣的:(Redis学习笔记)