Redis的单线程和高性能
Redis 单线程为什么还能这么快?

因为它所有的数据都在内存中,所有的运算都是内存级别的运算,而且单线程避免了多线程的切换性能损耗问题。正因为 Redis 是单线程,所以要小心使用 Redis 指令,对于那些耗时的指令(比如keys),一定要谨慎使用,一不小心就可能会导致 Redis 卡顿。

官方提供的数据: 10万+QPS(query per second,每秒内查询次数)

完全基于内存,绝大部分请求是纯粹的内存操作,执行效率高

数据结构简单,对数据操作也简单(它不使用表,它的数据库不会预定义或者强制去要求用户对redis的存储进行关联。存储结构就是键值对,类似于hashMap,查找和操作时间的复杂度都是低的)

采用单线程,单线程也能处理高并发请求,想多核也可以启动多个实例。(redis的主线程是单线程的,主线程负责io事件的处理以及io对应的相关请求的事件处理,还负责过期键的处理,赋值协调,集群协调等等。这些除了io事件的逻辑会被封装成周期性的任务,由主线程周期性处理。所有对于客户端的所有读写请求都是由一个主线程的串行处理。因此多个客户端对一个键进行写操作的时候,不会有并发的问题。避免了频繁的上下文切换和锁竞争的问题。这效率更高。)

使用多路I/O复用模型,非阻塞IO

Redis 单线程如何处理那么多的并发客户端连接?

Redis的IO多路复用:redis利用epoll来实现IO多路复用将连接信息和事件放到队列中,依次放到文件事件分派器,事件分派器将事件分发给事件处理器。

Nginx也是采用IO多路复用原理解决C10K问题

持久化
RDB快照(snapshot)

在默认情况下, Redis 将内存数据库快照保存在名字为 dump.rdb 的二进制文件中。保存某个时间点的全量数据快照。

  1. 配置文件redis.conf

save 900 1 ==>900秒内有1条是更改(增删改)指令,就产生一次快照,进行了一次备份 save 300 10 ==>300秒内有10条是更改(增删改)指令,就产生一次快照,进行了一次备份,如果300秒内没有到达10条写入,就要等待900秒。 save 60 10000 ==>60秒内有10000条是更改(增删改)指令,就产生一次快照,进行了一次备份,如果60秒内没有到达10000条写入,就要等待300秒。

stop-writes-on-bgsave-error yes ==>yes:当备份进程出错的时候,主进程就停止接受新的写入操作,保护持久化的数据一致性的问题。

rdbcompression yes ==>在备份的时候,需要将rdb文件进行压缩后采取保存。这边一般不开启,设置为no,因为redis本身属于cpu密集型服务器,在开启压缩会带来更多的cpu消耗,相比硬盘成本,cpu更值钱。

dbfilename dump.rdb ===>创建备份文件名

dir ./ ===>备份文件的路径

在save的最后配置 : save "" ===>此时将RDB的配置开启了

RDB文件在: src/dump.rdb。此文件是一个二进制文件

SAVE:阻塞Redis的服务进程,直到RDB文件被创建完成

BGSAVE:Fork出一个子进程来传解RDB文件,不阻塞服务器进程。

举例: 先删除dump.rdb,开启一个客户端:save ===>此时服务器卡顿,直到dump.rdb创建出来。

lastsave ===>返回一个类似时间戳的数字,记录上次执行save的时间。

bgsave ===>保存数据,并且服务器不卡顿,一般在设计的时候按照:mv dump.rdb dumpxxxx.rdb,以这种时间戳的方式记录某个时间点的备份。

自定触发RDB持久化

根据redis.conf配置里的SAVE m n定时触发(用的是BGSAVE)

主从复制时,主节点自定触发

直到Debug Reload

执行Shutdown且没有开启AOF持久化

系统调用fork():创建进程,实现Copy- on -write

RDB持久化缺点:

内存数据的全量同步,数据量大会由于I/O而严重影响性能

可能会因为Redis挂掉而丢失从当前最忌一次快照期间的数据

查看rdb: src/redis-check-rdb dump.rdb

AOF快照(append-only file)

快照功能并不是非常耐久(durable): 如果 Redis 因为某些原因而造成故障停机, 那么服务器将丢失最近写入、且仍未保存到快照中的那些数据.

AOF(Append-Only-File)持久化:保存写状态 ===>redis默认是关闭的

记录下除了查询以外的所有变更数据库状态的指令

以append的形式追加保存到AOP文件中(增量)

配置文件redis.conf:

appendonly no ==>修改为yes,开启aof持久化,默认生成文件的路径redis/src/appendonly.aof

appendfilename "appendonly.aof" ===>aof格式持久化文件名

appendfsync everysec ==>配置aof文件写入的方式:

always:一旦缓存区的数据发生变化,就总是及时将缓存区的内容写入到AOF中

everysec:将缓存区的内容每隔1秒写入AOF文件中 ===>常用的方式

no:将缓存区数据写入AOF文件的操作交给操作系统来决定。

推荐(并且也是默认)的措施为每秒 fsync 一次, 这种 fsync 策略可以兼顾速度和安全性。

第一次启动要开启AOF操作,生成文件,客户端命令:config set appendonly yes

日志重写解决AOF文件大小不断增大的问题,原理如下:

调用fork(),创建一个子进程

子进程把新的AOF写道一个临时文件里,不依赖原来的AOF文件

主进程持续将新的变动同时写到内存和原来的AOF里

主进程获取子进程重写AOF的完成信号,往新AOF同步增量变动

是用新的AOF文件替换调旧的AOF文件

RDB和AOF的比较:

RDB优点:全量数据快照,文件小,恢复快

RDB缺点:无法保存最忌一次快照之后的数据

AOF优点:可读性高,时候保存增量数据,数据不易丢失

AOF缺点:文件体积大,恢复时间长。

RDB 和 AOF ,我应该用哪一个?

如果你非常关心你的数据, 但仍然可以承受数分钟以内的数据丢失, 那么你可以只使用 RDB 持久化。有很多用户都只使用 AOF 持久化, 但我们并不推荐这种方式: 因为定时生成 RDB 快照(snapshot)非常便于进行数据库备份, 并且 RDB 恢复数据集的速度也要比 AOF 恢复的速度要快

Redis 4.0 混合持久化

重启 Redis 时,我们很少使用 rdb 来恢复内存状态,因为会丢失大量数据。我们通常使用 AOF 日志重放,但是重放 AOF 日志性能相对 rdb 来说要慢很多,这样在 Redis 实例很大的情况下,启动需要花费很长的时间。 Redis 4.0 为了解决这个问题,带来了一个新的持久化选项——混合持久化。AOF在重写(aof文件里可能有太多没用指令,所以aof会定期根据内存的最新数据生成aof文件)时将重写这一刻之前的内存rdb快照文件的内容和增量的 AOF修改内存数据的命令日志文件存在一起,都写入新的aof文件,新的文件一开始不叫appendonly.aof,等到重写完新的AOF文件才会进行改名,原子的覆盖原有的AOF文件,完成新旧两个AOF文件的替换;

AOF根据配置规则在后台自动重写,也可以人为执行命令bgrewriteaof重写AOF。 于是在 Redis 重启的时候,可以先加载 rdb 的内容,然后再重放增量 AOF 日志就可以完全替代之前的 AOF 全量文件重放,重启效率因此大幅得到提升。

开启混合持久化:aof-use-rdb-preamble yes

混合持久化aof文件结构: appendonly.aof = RDB格式+ AOF格式

开启混合持久化之后,set数据之后,查看生成的appendonly.aof文件:more appendonly.aof
此时生成的文集中包含RDB二进制快照数据+ AOF追加的命令数据

AOF重写功能: 一般是redis后台进程去做,可以手动触发命令: bgrewriteaof.

实际上是把那些大规模的对同一个key所有的操作把它变成最终的一条set值的操作.

eg: incr requestlock
incr requestlock
incr requestlock
incr requestlock
=====>重写之后的命令 : set requestlock 4

问题1: 混合重写必须要aof和rdb同时设置为yes么 还是混合的这个设为yes就行?

aof和混合那个都要设为yes,混合那个是基础aof的

问题2: 手动重写aof文件,如果文件很大,重写 需要花很长时间,这个时候刚好又有新操作的数据怎么办?

新操作会存在内存里,等到aof重写完再最加到aof文件的末尾,aof重写的数据就是重写开始之前的内存数据

问题3: redis hash冲突后进行rehash 如果再冲突继续rehash?到最后还会不会有冲突的结果?

不是hash冲突后马上进行rehash,是redis扩容后之前的元素再做rehash重新定位,扩容是根据redis内部的扩容因子来计算的,跟hashmap扩容类似,冲突一直都可能有,只不过redis会尽量通过扩容来减小冲突


水幕年华
0 声望0 粉丝