关于Redis的知识点,你都学会了吗?2

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
日志服务 SLS,月写入数据量 50GB 1个月
简介: 关于Redis的知识点,你都学会了吗?2

1684221214872.png

  • String 简单动态字符串 Simple Dynamic String, SDS
Redis没有直接使用C语言的传统字符串表示,而是自己构建了一种名为简单动态字符串(Simple Dynamic String, SDS)的抽象类型,并将SDS用作Redis的默认字符串表示。
每个sds.h/sdshdr结构表示一个SDS值:
struct sdshdr {
  int len; // 记录buf数组中已经使用的字节数量
  int free; // 记录buf数组中未使用字节的数量
  char buf[]; // 字节数组,用于保存字符串。SDS遵循C字符串以空字符结尾的惯例
}

Redis持久化

  • 持久化的作用:redis所有数据保存在内存中,对数据的更新将异步地保存到磁盘上。
  • 主流数据库持久化实现方式:快照(MySQL Dump/Redis RDB),写日志(MySQL Binlog/Redis AOF)


RDB:

  • 创建RDB文件(二进制文件)到硬盘中,启动后载入RDB文件到内存


三种触发机制


save(同步) - 会产生阻塞

文件策略:如存在老的RDB文件,新的替换老的,新的会先生成到一个临时文件

bgsave(异步) - 不会阻塞

客户端执行bgsave之后,redis会使用linux的一个fork()命令生成主进程的一个子进程(fork的操作会执行一个内存页的拷贝,使用copy-on-write策略),子进程会创建RDB文件,创建完毕后将成功的消息返回给redis。fork()出来的子进程执行快的话不会阻塞主进程,否则也会阻塞redis,阻塞的实际点就是生成出来这个子进程。由于是异步,在创建的过程中还有其他命令在执行,如何保证RDB文件是最新的呢?在数据量大的时候bgsave才能突出优点。

命令savebgsaveIO类型同步异步阻塞是是(阻塞发生在fork子进程复杂度O(n)O(n)优点不会消耗额外内存不阻塞客户端命令缺点阻塞客户端命令需要fork,消耗内存


自动触发:多少秒内有多少changes会异步(bgsave)生成一个RDB文件,如60秒内有1W条changes,默认的规则,可以改;不是很好吧,无法控制频率;另外两条是900秒内有1条changes, 300秒内有10条changes;

配置

  • dbfilename dump.rdb
  • dir ./
  • stop-writes-on-bgsave-error yes 当bgsave发生错误是停止写RDB文件
  • rdbcompression yes 采用压缩格式
  • rdbchecksum yes 采用校验和


其他不能忽视的点:

全量复制;debug reload;shutdown save会执行rdb文件的生成


AOF:


RDB现存问题:耗时,耗性能(fork,IO),不可控(突然宕机)

AOF:redis中的cmd会先刷新到缓冲区,然后更具配置AOF的策略,异步存追加到AOF文件中,发生宕机后,可以通过- AOF恢复,基本上数据是完整的

AOF的三种策略(配置的三种属性)

always:来一条命令写一条;不丢失数据,IO开销较大

everysec:每秒把缓冲区fsync到AOF文件;丢1秒数据

no:操作系统决定什么时候把缓冲区同步到AOF就什么时候追加;不用配置,但是不可控,取决于操作系统

AOF重写


如果AOF文件很大的话,恢复会很慢,AOF的重写是优化一些命名,使其变成1条,对于过期数据没必要Log,本质是把过期的没有用的,重复的过滤掉,以此减少磁盘占用量,加速恢复。极端的例子,1亿次incr,实际只需要set counter n就够了

重写的两种方式

bgrewriteaof:异步执行,redis fork出一个子进程,然后进行AOF重写

AOF重写配置

auto-aof-rewrite-min-size: AOF文件到达多大的时候才开始重写

auto-aof-rewrite-percentage: AOF文件的增长率到达了多大才开始重写

统计


aof_current_size AOF当前尺寸 字节

aof_base_size AOF上次重启和重写的尺寸 字节,方便自动重写判断

重写触发机制(同时满足如下两条)


aof_current_size > auto-aof-rewrite-min-size

(aof_current_size - aof_base_size) / aof_base_size > auto-aof-rewrite-percentage

其他配置


appendonly yes

appendfilename “”

appendfsync everysec

dir /xx

no-appendfsync-on-rewrite yes AOF在重启之后恢复,要权衡是否开启AOF日志追加的功能,这个时候IO很大,如果设置为yes,也就意味着在恢复之前的日志数据会丢失

RDB & AOF最佳策略:RDB优先于AOF先启用


RDB:建议关掉,集中管理,在从节点开RDB

AOF:建议开启,每秒刷盘

最佳策略:小分片(log文件分片)

常见问题


fork操作:是一个同步操作,做一个内存页的拷贝;与内存量息息相关,内存越大,耗时越长;执行info命令,有个latest_fork_usec的值,看下上次fork执行耗时

进程外开销:

CPU:RDB AOF文件生成,属于CPU密集型操作(不要和CPU密集型应用部署在一起,减少RDB AOF频率);内存:fork内存开销;硬盘:IO开销大,选用SSD磁盘

AOF追加阻塞:主线程将命令刷到AOF缓冲区,同步线程同步命令到硬盘,同时主线程会对比上次fsync的时间,如果大于2秒就阻塞主线程,否则不阻塞,主线程这么做是为了达到每秒刷盘的目的,让子线程完成AOF,以此来达到数据同步。AOF发生阻塞怎么定位:redis日志/info persistence(aof_delayed_fsync累计阻塞次数,是累计,不好分清什么时候发生阻塞)

单机多实例部署

高可用

Redis主从复制

主从复制:单机故障/容量瓶颈/QPS瓶颈;一个master可以有多个slave,一个slave只能有一个master,数据必须是单流向,从master流向slave

复制的配置:


使用slaeof命令,在从redis中执行slave masterip:port使其成为master的从服务器,就能从master拉取数据了;执行slaveof no one清除掉不成为从节点,但是数据不清楚;

修改配置, slaveof ip port / slave-read-only yes(从节点只做都操作);配置要更改的话,要重启,所以选择的时候谨慎

全量复制


run_id(使用info server可以看到run_id),重启之后run_id就没有了,当从服务器去复制主服务器,主服务器run_id会在从服务器上做一个标识,当从服务器发现主服务器的run_id发生了变化,说明主服务器发生了变化(重启或者什么的),那么从服务器就要把主服务器的数据都同步过来

偏移量:部分复制中的一个依据,后面说

解析下上面的全量复制的过程,slave向master发送psync的命令要去master全量复制数据(PSYNC ,其中?表示我不知道master的runId啊,第一次连嘛,-1表示我都要,这时候slava咱啥也不知道),master大人收到了小弟的请求之后,大方的把自己的runId/offset发了过去,小弟收到后先存下来;在master大人把自个的信息发给小弟之后,立马投入了创建快照RDB的工作,一个bgsave命令立马开工,RDB生产了就发给slave;咦,细心的我们发现你这不对啊,你master创建快照到创建完成这之间新增的数据咋办,master吭吭了两声,我在开始快照的那一刻,后期的所有写命令都额外往buffer中存了一份,来保证我给你的是完整的,当我发送完RDB之后,立马给你发buffer;slave小弟内心对master大人产生了膜拜之情,收到了RDB/buffer之后,先把自己的老数据flush掉,然后load RDB,把最新的buffer刷一遍,分分钟让自己向master看齐。

开销:bgsave时间, RDB文件网络传输时间,从节点清空数据时间,从节点加载RDB的时间,可能的AOF重写时间

解释下上面的部分复制的过程,当遇到网络抖动,那这段时间内数据在slave上就会发生丢失,那么这些数据slave是不知道的,在2.8之前redis会重新做一次全量复制,但是很显然这样做开销很大,2.8之后提出部分复制的功能;当matster发现slave连接不上的时候,master在进行写操作的时候,也会往缓冲区写,等到下一次slave连上之后,slave会发送一条pysnc {offset}{runId}的命令,其中offset是slave自己的,相当于告诉master我的偏移量是多少,master判断slave的offset在缓冲区内(缓冲区有start/end offset)就向slave发送continue命令,然后把这部分数据发送给slave;当master发现slave这个offset偏移量很大的时候,也就意味着slave丢失了很多数据,那么就进行一次全量复制

故障处理:


master/slave宕机的情况,主从模式没有实现故障的完全自动转移

常见问题:

读写分离:读流量分摊到从节点,可能遇到复制数据延迟,也可能读到过期的数据,从节点故障怎么办

主从配置不一致:主从maxmemory不一致,可能会丢失数据;主从内存不一致

规避全量复制:第一次不可避免;小主节点,低峰处理(夜间);主节点重启后runId发生了变化

规避复制风暴

单机主节点复制风暴,如果是1主N从,当master重启之后,所有的slave都会发生全量复制,可想而知这样非常容易造成redis服务的不可用

Redis事务

Redis 事务可以一次执行多个命令, 并且带有以下三个重要的保证:


批量操作在发送 EXEC 命令前被放入队列缓存。

收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。

在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。

Redis事务从开始到执行会经历以下三个阶段:开始事务 -> 命令入队 -> 执行事务。单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的。事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。这是官网上的说明 From redis docs on transactions: It’s important to note that even when a command fails, all the other commands in the queue are processed – Redis will not stop the processing of commands.

Redis 通过监听一个 TCP 端口或者 Unix socket 的方式来接收来自客户端的连接,当一个连接建立后,Redis 内部会进行以下一些操作:

首先,客户端 socket 会被设置为非阻塞模式,因为 Redis 在网络事件处理上采用的是非阻塞多路复用模型。

然后为这个 socket 设置 TCP_NODELAY 属性,禁用 Nagle 算法

然后创建一个可读的文件事件用于监听这个客户端 socket 的数据发送

Redis 管道技术可以在服务端未响应时,客户端可以继续向服务端发送请求,并最终一次性读取所有服务端的响应。管道技术最显著的优势是提高了 redis 服务的性能。

Redis 分区


分区是分割数据到多个Redis实例的处理过程,因此每个实例只保存key的一个子集。

分区的优势:


通过利用多台计算机内存的和值,允许我们构造更大的数据库。

通过多核和多台计算机,允许我们扩展计算能力;通过多台计算机和网络适配器,允许我们扩展网络带宽。

分区的不足:


涉及多个key的操作通常是不被支持的。举例来说,当两个set映射到不同的redis实例上时,你就不能对这两个set执行交集操作。

涉及多个key的redis事务不能使用。

当使用分区时,数据处理较为复杂,比如你需要处理多个rdb/aof文件,并且从多个实例和主机备份持久化文件。

增加或删除容量也比较复杂。redis集群大多数支持在运行时增加、删除节点的透明数据平衡的能力,但是类似于客户端分区、代理等其他系统则不支持这项特性。然而,一种叫做presharding的技术对此是有帮助的。

分区类型:Redis 有两种类型分区。 假设有4个Redis实例 R0,R1,R2,R3,和类似user:1,user:2这样的表示用户的多个key,对既定的key有多种不同方式来选择这个key存放在哪个实例中。也就是说,有不同的系统来映射某个key到某个Redis服务,关注+转发后,私信【Redis】获取300多页的Redis实战学习笔记。

范围分区


最简单的分区方式是按范围分区,就是映射一定范围的对象到特定的Redis实例。比如,ID从0到10000的用户会保存到实例R0,ID从10001到 20000的用户会保存到R1,以此类推。这种方式是可行的,并且在实际中使用,不足就是要有一个区间范围到实例的映射表。这个表要被管理,同时还需要各 种对象的映射表,通常对Redis来说并非是好的方法。

哈希分区


另外一种分区方法是hash分区。这对任何key都适用,也无需是object_name:这种形式,像下面描述的一样简单:用一个hash函数将key转换为一个数字,比如使用crc32 hash函数。对key foobar执行crc32(foobar)会输出类似93024922的整数。对这个整数取模,将其转化为0-3之间的数字,就可以将这个整数映射到4个Redis实例中的一个了。93024922 % 4 = 2,就是说key foobar应该被存到R2实例中。注意:取模操作是取除的余数,通常在多种编程语言中用%操作符实现。


相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
30天前
|
运维 NoSQL 测试技术
从一个事故中理解Redis(几乎)所有知识点
作者从一个事故中总结了Redis(几乎)所有的知识点,供大家学习。
|
6月前
|
缓存 NoSQL 定位技术
深入探索Redis:面试中必须掌握的关键知识点
深入探索Redis:面试中必须掌握的关键知识点
|
存储 NoSQL 测试技术
关于redis涉及的知识点,C语言如何操作redis
关于redis涉及的知识点,C语言如何操作redis
|
存储 SpringCloudAlibaba 运维
Redis高级知识点总结
在 Redis 6.0 中,非常受关注的第一个新特性就是多线程。这是因为,Redis 一直被大家熟知的就是它的单线程架构,虽然有些命令操作可以用后台线程或子进程执行(比如数据删除、快照生成、AOF 重写),但是,**从网络 IO 处理到实际的读写命令处理,都是由单个线程完成的**。随着网络硬件的性能提升,Redis 的性能瓶颈有时会出现在网络 IO 的处理上,也就是说,单个主线程处理网络请求的速度跟不上底层网络硬件的速度
258 0
Redis高级知识点总结
|
存储 缓存 NoSQL
redis知识点
redis 知识点
80 0
|
缓存 监控 NoSQL
【Redis】Redis知识点阶段性总结 2
【Redis】Redis知识点阶段性总结
59 0
|
NoSQL Linux Redis
【Redis】Redis知识点阶段性总结 1
【Redis】Redis知识点阶段性总结
95 0
|
存储 缓存 监控
全新Redis6全部知识点,零基础入门3
全新Redis6全部知识点,零基础入门
12226 1
|
存储 缓存 NoSQL
全新Redis6全部知识点,零基础入门2
全新Redis6全部知识点,零基础入门
|
存储 缓存 JSON
全新Redis6全部知识点,零基础入门1
全新Redis6全部知识点,零基础入门