Flutter 中的 DividerTheme 小部件:全面指南

阅读 12

2024-06-01

目录

今日良言:从不缺乏从头开始的勇气

一、持久化

1、RDB

2、AOF

二、Redis 的事务

三、主从复制

 四、哨兵模式

 五、集群模式

六、缓存

七、分布式锁


今日良言:从不缺乏从头开始的勇气

一、持久化

持久化就是把数据存储在硬盘上,无论是重启进程还是重启主机,数据都存在。

Redis 的持久化有两种方式:

1、RDB

RDB(Redis DataBase) 定期的把 Redis 内存中的所有数据,写入到硬盘中,并生成一个“快照”

RDB 就是 Redis 给当前内存中的所有数据拍个照片,生成一个文件,存储在磁盘中。后续当 Redis 重启以后(内存中的数据无了),就可以根据刚才的 “快照” 文件恢复内存中的数据。

RDB 是定期持久化的方式,定期具体来说,有两种方式:

1)手动触发

2)自动触发 

查看 Redis 配置文件中的默认配置:

上述这里默认配置的意思就是:900秒至少修改一次 key....

Redis 服务器默认采用的是 RDB持久化方式。

Redis 自动触发持久化机制主要是有三种方式:

通过前面的介绍可以直到,默认的 rdb 生成的文件路径是 /var/lib/redis 下,文件名通过 dbfilename 指定(默认是 dump.rdb)。那么,试想一下,如果这个 dump.rdb 文件被故意改坏了,会发生什么情况?

最后来介绍一下 RDB 持久化的优缺点:

优点:

缺点:

rdb 最大的问题是:不能实时的持久化保存数据,在两次生成快照之间,实时的数据可能会随着重启而丢失。而 aof 则可以避免这种问题。


2、AOF

aof(append only file)类似于 mysql 的 binlog,会把用户的每次操作记录到文件中,当 redis 重新启动的时候,就会读取这个 aof 文件中的内容,用来恢复数据。

aof 默认一般是关闭状态,修改配置文件可以开启 aof 功能。

默认路径和 rdb 在同一目录下(/var/lib/redis):

查看 aof 文件中的内容:

aof 是一个文本文件,每次进行的操作都会被记录到文本文件中,通过一些特殊符号作为分隔符,来对命令的细节做出区分。

aof 的工作流程如下:

为什么 AOF 要使用 aof_buf 这个缓冲区呢?

主要目的是为了降低写磁盘的次数。redis 使用单线程响应命令,如果每次写 AOF 文件都直接同步硬盘,性能从内存的读写变成了 IO 读写,势必会下降。先写入缓冲区可以有效减少 IO 次数,同时,redis 还可以提供多种缓冲区同步策略,让程序员根据自己的需求做出合理的平衡。

如果把数据写入到缓冲区中,本质上还是在内存中,如果这个时候,进程突然挂掉或者主机掉电,缓冲区的数据就丢了。这种这种情况,redis 提供了一些缓冲区同步策略,让程序员可以根据实际情况来进行取舍。

AOF 缓冲区同步文件策略主要有三种:

在配置文件中也可以找到这三种策略:

随着 AOF 文件持续增长,体积越来越大,导致 redis 下次启动时间被影响,因此,redis 就存在一个机制,能够针对 AOF 文件进行“整理”操作,剔除其中的冗余操作,达到压缩 AOF 文件的效果。

redis 引入 AOF 重写机制压缩文件体积。

AOF 重写机制可以手动触发和自动触发

bgrewriteaof 命令执行流程如下:

 

以上流程有一个需要关注的点:父进程 fork 完毕之后,子进程写新的 aof 文件,并且随着时间的推移,子进程很快就写完了新的文件,要让新的 aof 替换旧的 aof 文件,父进程此时还在写这个即将消亡的旧的 aof 文件是否还有意义?

rdb 对于 fork 之后的数据直接不管,而 aof 则对于 fork 之后的新数据,采用了 aof_rewrite_buf 缓冲区的方式来处理。rdb 本身的设计理念,就是用来“定时备份”的,只要是定时备份,就难以和最新的数据保持一致。而 aof 的理念则是实时备份。

aof 本来是按照文本的方式来写入文件的,但是文本的方式写文件,后续加载的成本比较高,于是 redis 引入了 “混合持久化”的方式,结合了 rdb 和 aof 的特点。

 如果当前 redis 上同时存在 aof 和 rdb 文件,此时以谁为主呢?

aof !因为 aof 中包含的数据比 rdb 更完整。rdb 文件直接被忽略。

redis 根据持久化文件进行数据恢复的流程如下:

以上就是 Redis 持久化相关内容


二、Redis 的事务

提到事务,当时小马的第一反应就是 Redis 事务和 MySQL 的事务有什么关系呢?

后来经过学习,才认识到 Redis 的事务和 MySQL 的事务相比,就是个“弟弟”。

我们知道,MySQL 的事务有四大特性:原子性、一致性、隔离性、持久性,而 Redis 的事务是否具有原子性,一直存在争议。

Redis 的事务和 MySQL 的事务的区别如下:

Redis 的事务主要的意义:就是为了“打包”(批量执行),避免其他客户端的命令,插队到中间。

Redis 中实现事务,本质上是在服务器上引入了一个“事务队列”(每个客户端都有一个)。

Redis 事务相关的几个命令如下:

执行 multi 命令后,服务器会返回 ok,进行一些操作:

此时,在服务器的事务队列中,保存了上述请求。

此时,如果开启另外一个客户端,尝试查询上述命令设置的三个 key 对应的数据,是没有结果的:

只有当执行了 exec 命令后,才会真正执行上述操作。

再次查询这三个 key 对应的数据是有结果的:

当开启事务,并且给服务器发送若干个命令之后,如果此时服务器重启,当前这个事务的效果等同于 discard。

 假设当前有如下两个客户端进行操作:

在上述这样的场景中,就可以使用 watch 来监控这个 key,监控这个 key 在事物的 multi 和 exex之间,set key 之后,是否被外部其它客户端修改了。

客户端 1 执行如下命令:

客户端 2 执行如下命令:

客户端 1 执行 exec:

此时的返回值为 nill,exec在执行上述事务的请求的时候,发现 key 被外部修改了,于是真正执行set key 111的时候就没有真正执行。

Redis 的 watch 就相当于是基于版本号这样的机制,来实现了“乐观锁”。

以上就是 Redis 事务相关内容。 


三、主从复制

分布式系统涉及到一个非常关键的问题:单点问题。

引入分布式系统主要就是为了解决上述的单点问题。

在分布式系统中,往往希望有多个服务器部署 redis 服务,从而构成一个 redis 集群,此时就可以让这个集群给整个分布式系统中的其他服务器,提供更稳定/更高效的数据存储功能。

在分布式系统中,有以下几种 redis 的部署方式:

本章节主要介绍主从模式。

在若干个 redis 节点中,有的是“主”节点,有的是“从”节点。每个从节点只能有一个主节点,而一个主节点可以有多个从节点。

 Redis 主从模式中,从节点的数据不允许修改,只能读取,更准确的说,主从模式,主要是针对“读操作”进行并发和可用性的提高,而写操作的话,无论是并发还是可用性,都是非常依赖主节点的,但主节点不能设置多个。

Redis 主从模式就可以解决单点问题,之前只是单个 redis 服务器节点,此时这个节点挂了,整个 redis 就挂了,而主从结构这些 redis 节点不太可能“同时挂了”,如果想要更高的可用性,可以采用“异地多活”。

可以在一台云服务器上运行三个 redis-server 进程来实现主从模式,此时需要保证这三个 redis-server 的端口不同(在配置文件中进行修改),还需要修改 daemonize yes(按照后台的方式运行),修改后重启服务。

假设 6379 是主节点,6380和6381是从节点。

实现主从复制如下:

主从节点建立复制流程图如下:

这个过程最关键的步骤是 第5步和第6步。

redis 提供了 psync 命令完成数据同步的过程。psync 命令不需要手动执行,当建立好主从同步关系之后,redis 服务器会自动执行这个命令。

从节点负责执行 psync 命令,从主节点拉取数据。

psync 可以从主节点获取全量数据,也可以获取部分数据。

psync 运行流程图如下:

全量复制的时机:

部分复制的时机:

全量复制的流程图如下:

部分复制的流程图如下:

 实时复制:

 主从复制,最大的问题还是在主节点上,主节点挂了以后,从节点虽然能够提供读操作,但是从节点不能自动升级成主节点,不能替换原有主节点的角色。此时,就需要程序猿/运维手工的恢复主节点,但是这个手动恢复的过程非常繁琐。

哨兵模式可以自动的对挂了的主节点进行替换。

以上就是主从复制模式的相关内容


 四、哨兵模式

哨兵模式其实指的就是在主从复制的基础上增加一个或者多个“哨兵节点”,让 Redis 实现高可用。

Redis 哨兵就是为了解决主从复制模式下主节点发生故障后,进行主备切换的问题。

Redis Sentinel 架构如下:

 

redis 哨兵核心功能主要有:

 当主节点挂了后,哨兵重新选取主节点的流程:

 注:

以上就是哨兵模式的相关内容。


 五、集群模式

首先,来认识一下什么是“集群”

哨兵模式提高了系统的可用性,但是本质上还是 redis 主从节点存储数据,其中就要求一个主节点/从节点就得存储整个数据的“全集”,如果数据量很大,接近超出了 主/从 节点所在机器的物理内存,就可能出现严重问题了。

那么,如何获取更大的空间存储数据?

多加机器即可!一台机器装不下,多搞几台存储数据即可。

此时,只要机器的规模足够多,就可以存储任意大小的数据了。

Redis 集群的核心思路就是用多组机器来存储数据的每个部分,那么接下来的核心问题就是:给定一个数据(一个具体的 key),那么这个数据应该存在哪个分片上?读取的时候又应该去哪个分片读取?

有三种主流的方式:

1、哈希求余

借助哈希函数,把一个 key 映射到整数,再针对分片的个数求余就可以得到一个下标。

后续想要查询某个 key 时,使用相同的算法,key 相同,hash 函数相同,得到的分片是一样的。 

2、一致性哈希算法

在上述 hash 求余操作中,当前 key 属于哪个分片是交替的

而在一致性哈希这样的设定下,把交替出现改进成了连续出现,具体过程如下:

第一步,把 0-2^31-1 这个数据空间映射到一个圆环上,数据按照顺时针方向增长:

第二步,假设当前存在三个分片,就把分片放到圆环的某个位置上:

第三步,假设有一个 key,计算得到 hash 值 H,那么这个 key 映射的分片就是从 H 所在位置顺时针往下找,找到的第一个分片,就是该 key 所从属的分片。

这就相当于,N 个分片的位置,把圆环分成了 N 个管辖区间,key 的hash 值落在某个区间内,就归对应的分区管理。

那么,如果在这种情况下,想要扩容一个分片,该如何安排?

 3、哈希槽分区算法

为了解决上述搬运成本高以及数据分布不均匀的问题,Redis 集群引入了哈希槽(hash slots)算法。

相当于是把整个哈希值映射到 16384 个槽位上,也就是 [0,16383],然后再把这些槽位比较均匀的分给每个分片,每个分片的节点都需要记录自己持有哪些分片。

 这里涉及两个问题:

问题一:Redis 集群是最多有 16384 个分片吗?

 答:并非如此,如果一个分片只有一个槽位,此时就很难保证数据在各个分片上的均匀分布。实际上,Redis 的作者建议集群分片数不应该超过 1000。

问题二:为什么是 16384 个槽位?

答:节点之间通过心跳包进行通信,心跳包中包含了该节点持有哪些 slots,这个是使用位图这样的数据结构表示的。表示 16384(16k)个 slots,需要的位图大小是 2 kb,如果给定的 slots 数量更多,此时就需要消耗更多的空间,假设 slots 为 65535 个,此时需要 8kb 位图表示,8 kb对于内存来说不算什么,但是在频繁的网络心跳包中,还是一个不小的开销。另一方面,Redis 集群一般不建议超过 1000 个分片,所以 16k 对于最大 1000个分片来说是足够的。

如果 Redis 集群中,有节点挂了,会出现什么情况?

答:如果挂了的是节点是从节点,挂了就挂了;如果挂了的是主节点,此时集群需要做的工作和之前哨兵做的工作类似,就会自动的把从属于该主节点的从节点挑选出来提拔成主节点。

具体的处理流程如下:

上述选举主节点的流程和哨兵模式并不相同,此处是直接选举出主节点,而哨兵模式是先选举出 leader,由 leader 负责找一个从节点升级成主节点。

以上就是集群模式相关内容。


六、缓存

Redis 最主要的用途主要有三个:

本章节主要介绍 redis 作为缓存的一些知识点。

对于硬件的访问速度来说,一般是:CPU寄存器 > 内存 > 磁盘 > 网络,速度快的设备就可以作为速度慢的设备的缓存,最常见的是使用内存作为硬盘的缓存。

缓存的核心思路就是把一些常用的数据放到访问速度更快的地方,方便随时读取。缓存访问速度是快,但是空间往往是不足的,因此大部分的时候,缓存只存放一些热点数据(访问频繁的数据)。

通常使用 redis 作为数据库(mysql)的缓存。数据库是非常重要的组件,绝大部分商业项目都会用到,但 mysql 的速度(性能)又比较慢,因此,可以使用 redis 作为 mysql 的缓存。

正因为 mysql 等数据库效率比较低,所以承担的并发量比较有限,一旦请求量多了,数据库的压力就会很大,甚至很容易出现宕机(服务器每次处理一个请求,一定要消耗一些硬件资源,比如cpu、内存、硬盘、网络等,任意一种资源的消耗超出了机器能提供的上限,机器就很容易出现故障了)。

那么,如何提高 mysql 能承担的并发量?

核心思路主要有两个:

redis 作为缓存时,存储的是一些热点数据,那么,如何知道 redis 应该存储哪些热点缓存呢?

 在上述实时生成中,虽然可以生成热点数据, 但是,不停地写 redis,就会使 redis 的内存占用越来越多,逐渐达到设置的内存上限,此时,如果继续往里插数据,就会触发问题,为了解决这个问题,redis 就引入了 “内存淘汰策略”。

最后来介绍一下:缓存预热、缓存雪崩、缓存穿透、缓存击穿

缓存预热(Cache preheating)

缓存雪崩(Cache avalanche)

缓存穿透(Cache penetration)

缓存击穿 (Cache breakdown)

以上就是 redis 作为缓存的相关内容


七、分布式锁

本篇博客最后一章节介绍一下分布式锁。

什么是分布式锁呢?

那么分布式锁是如何实现的呢?

实现思路非常简单:本质上就是通过一个键值对来标识锁的状态。

举个例子:购买车票的场景

使用 setnx 命令可以得到“加锁”效果,针对解锁,就可以使用 del 命令来完成,但是,还是存在问题,试想一下:某个服务器加锁成功了(setnx 成功),执行后续逻辑过程中, 服务器直接掉电,进程异常终止(没有执行到解锁操作),此时就导致 redis 上设置的 key  无人删除,也就导致其它服务器无法获取到锁了。

所谓的加锁,就是给 redis 上设置一个 key-value;

所谓的解锁,就是将 redis 上这个 key-value 删除。

 还有可能出现如下情况:服务器1执行了加锁,而服务器2执行了解锁。

在解锁的时候,先进行判定,再进行 del,此处是两步操作(不是原子的),也会出现问题:

上述方案仍然存在一个很重要的问题,要在加锁的时候,给 key 设置过期时间,那么过期时间设置多少合适呢?

 注:“看门狗”这个线程是在业务服务器上的,不是在 redis 服务器上的。

实际使用 redis 时,一般是使用集群的方式进行部署的(至少是主从模式,而不是单机),那么就可能出现以下情况:

为了解决这个问题,Redis 的作者提供了 Redlock 算法。

 以上就是 Redis 分布式锁相关内容。


Redis 相关三部“曲”,从入门到入土(啊呸,精通),希望这三篇文章能够帮助铁铁,后续博主会继续更新精品文章,感兴趣的话不妨点点关注嗷~

精彩评论(0)

0 0 举报