redis单线程为什么还那么快
- 因为他所有的数据都在内存中,所有的运算都是内存级别的运算,而且单线程避免了多线程切换性能损耗问题,正因为是单线程,所以要小心使用redis指令耗时的操作
- redis单线程如何处理那么多的并发客户端链接
新操作都会在内存里,等到aof文件重写完成再追加到aof文件的末尾,aof重写的数据就是重写之前内存的数据
如何关闭rdb?
-
如果同时开启rdb和aof会用哪个?
dump和aof文件都会生成,但是如果重启redis会使用aof文件
混合持久化需要同时开启rdb和aof吗 还是只要开启混合的就好?
持久化
RDB
-
执行流程
- redis调用系统函数fork() ,创建一个子进程进行持久化。
- 子进程将数据集写入到一个临时 RDB 文件中(持久化,也就是将内存中的数据写入临时文件)。
- 当子进程完成对临时RDB文件的写入时,redis 用新的临时RDB 文件替换原来的RDB 文件,并删除旧 RDB 文件。
注:fork的作用是复制一个与当前进程一样的进程。新进程的所有数据(变量、环境变量、程序计数器等) 数值都和原进程一致,但是是一个全新的进程,并作为原进程的子进程。内存会翻倍。
AOF
- 配置
```shell
开启AOF
appendonly yesAOF持久化配置文件的名称
appendfilename appendonly.aofappendfsync always (同步持久化,每次发生数据变更会被立即记录到磁盘,性能差但数据完整性比较好)
appendfsync everysec (异步操作,每秒记录,如果一秒钟内宕机,有数据丢失)
appendfsync no (将缓存回写的策略交给系统,linux 默认是30秒将缓冲区的数据回写硬盘的)
appendfsync everysec
重写配置
no-appendfsync-on-rewrite no auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb
2. bgrewriteaof,也会fork子进程,内存翻倍
![image.png](https://cdn.nlark.com/yuque/0/2020/png/1934365/1597062981632-524bab95-c71a-4a4d-aab2-024773026ec0.png#align=left&display=inline&height=453&margin=%5Bobject%20Object%5D&name=image.png&originHeight=453&originWidth=870&size=62045&status=done&style=none&width=870)
- 定义:AOF采用文件追加的方式持久化数据,所以文件会越来越大,为了避免这种情况发生,增加了重写机制
- 当AOF文件的大小超过了配置所设置的阙值时,Redis就会启动AOF文件压缩,只保留可以恢复数据的最小指令集,可以使用命令bgrewriteaof
- 触发机制:Redis会记录上次重写时的AOF文件大小,默认配置时当AOF文件大小是上次rewrite后大小的一倍且文件大于64M时触发
- auto-aof-rewrite-percentage 100 (一倍)
- auto-aof-rewrite-min-size 64mb
4. no-appendfsync-on-rewrite参数
- 如果该参数设置为no,是最安全的方式,不会丢失数据,但是要忍受阻塞的问题。
- 如果设置为yes,这就相当于将appendfsync设置为no,这说明并没有执行磁盘操作,只是写入了缓冲区,因此这样并不会造成阻塞(因为没有竞争磁盘),但是如果这个时候redis挂掉,就会丢失数据。丢失多少数据呢?在linux的操作系统的默认设置下,最多会丢失30s的数据。
<a name="8wF5O"></a>
#### 混合持久化 redis4.0
1. 配置
```shell
aof-use-rdb-preamble yes
- redis采用的是定期删除+惰性删除策略。
- 为什么不用定时删除策略?
定时删除,用一个定时器来负责监视key,过期则自动删除。虽然内存及时释放,但是十分消耗CPU资源。在大并发请求下,CPU要将时间应用在处理请求,而不是删除key,因此没有采用这一策略.
- 定期删除+惰性删除是如何工作的呢?
定期删除,redis默认每个100ms检查,是否有过期的key,有过期key则删除。需要说明的是,redis不是每个100ms将所有的key检查一次,而是随机抽取进行检查(如果每隔100ms,全部key进行检查,redis岂不是卡死)。因此,如果只采用定期删除策略,会导致很多key到时间没有删除。
惰性删除也就是说在你获取某个key的时候,redis会检查一下,这个key如果设置了过期时间那么是否过期了?如果过期了此时就会删除。
- 采用定期删除+惰性删除就没其他问题了么?
不是的,如果定期删除没删除key。然后你也没即时去请求key,也就是说惰性删除也没生效。这样,redis的内存会越来越高。那么就应该采用内存淘汰机制。
缓存淘汰策略
- 在redis.conf中有一行配置
maxmemory-policy volatile-lru
- 策略
- noeviction 不会继续服务写请求(DEL请求可以继续服务),读请求可以继续。这样可以保证不丢失数据,但是会让线上的业务不能持续进行。是默认的淘汰策略
- volatile-lru 尝试淘汰设置了过期时间的key,最少使用的key优先被淘汰,没有设置过期时间的key不会被淘汰,这样可以保证需要持久化的数据不会突然丢失
- volatile-ttl 跟上面一样, 优先淘汰ttl值最小的key
- volatile-random 跟上面一样, 随机淘汰key
- allkeys-lru 区别于volatile-lru, 淘汰的对象是全体key
- allkeys-random 随机淘汰全体key
集群
单机
主从
- Redis的Replication的特点和优点:
- 同一个Master可以同步多个Slaves。
- Slave同样可以接受其它Slaves的连接和同步请求,这样可以有效的分载Master的同步压力。因此我们可以将Redis的Replication架构视为图结构。
- Master Server是以非阻塞的方式为Slaves提供服务。所以在Master-Slave同步期间,客户端仍然可以提交查询或修改请求。
- Slave Server同样是以非阻塞的方式完成数据同步。在同步期间,如果有客户端提交查询请求,Redis则返回同步之前的数据。
- 为了分载Master的读操作压力,Slave服务器可以为客户端提供只读操作的服务,写服务仍然必须由Master来完成。即便如此,系统的伸缩性还是得到了很大的提高。
- Master可以将数据保存操作交给Slaves完成,从而避免了在Master中要有独立的进程来完成此操作。
- 支持主从复制,主机会自动将数据同步到从机,可以进行读写分离。
Redis的Replication的缺点:
- Redis不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复。
- 主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性。
- Redis的主从复制采用全量复制,复制过程中主机会fork出一个子进程对内存做一份快照,并将子进程的内存快照保存为文件发送给从机,这一过程需要确保主机有足够多的空余内存。若快照文件较大,对集群的服务能力会产生较大的影响,而且复制过程是在从机新加入集群或者从机和主机网络断开重连时都会进行,也就是网络波动都会造成主机和从机间的一次全量的数据复制,这对实际的系统运营造成了不小的麻烦。
- Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。为避免这一问题,运维人员在系统上线时必须确保有足够的空间,这对资源造成了很大的浪费。
哨兵模式
优点
- Redis Sentinel 集群部署简单。
- 能够解决 Redis 主从模式下的高可用切换问题。
- 很方便实现 Redis 数据节点的线形扩展,轻松突破 Redis 自身单线程瓶颈,可极大满足 Redis 大容量或高性能的业务需求。
- 可以实现一套 Sentinel 监控一组 Redis 数据节点或多组数据节点。
缺点
优点
- 无中心架构。
- 数据按照 slot 存储分布在多个节点,节点间数据共享,可动态调整数据分布。
- 可扩展性:可线性扩展到 1000 多个节点,节点可动态添加或删除。
- 高可用性:部分节点不可用时,集群仍可用。通过增加 Slave 做 standby 数据副本,能够实现故障自动 failover,节点之间通过 gossip 协议交换状态信息,用投票机制完成 Slave 到 Master 的角色提升。
- 降低运维成本,提高系统的扩展性和可用性。
- 缺点
- Client 实现复杂,驱动要求实现 Smart Client,缓存 slots mapping 信息并及时更新,提高了开发难度,客户端的不成熟影响业务的稳定性。目前仅 JedisCluster 相对成熟,异常处理部分还不完善,比如常见的“max redirect exception”。
- 节点会因为某些原因发生阻塞(阻塞时间大于 clutser-node-timeout),被判断下线,这种 failover 是没有必要的。
- 数据通过异步复制,不保证数据的强一致性。
- 多个业务使用同一套集群时,无法根据统计区分冷热数据,资源隔离性较差,容易出现相互影响的情况。
- Slave 在集群中充当“冷备”,不能缓解读压力,当然可以通过 SDK 的合理设计来提高 Slave 资源的利用率。
- Key 批量操作限制,如使用 mset、mget 目前只支持具有相同 slot 值的 Key 执行批量操作。对于映射为不同 slot 值的 Key 由于 Keys 不支持跨 slot 查询,所以执行 mset、mget、sunion 等操作支持不友好。
- Key 事务操作支持有限,只支持多 key 在同一节点上的事务操作,当多个 Key 分布于不同的节点上时无法使用事务功能。
- Key 作为数据分区的最小粒度,不能将一个很大的键值对象如 hash、list 等映射到不同的节点。
- 不支持多数据库空间,单机下的 redis 可以支持到 16 个数据库,集群模式下只能使用 1 个数据库空间,即 db 0。
- 复制结构只支持一层,从节点只能复制主节点,不支持嵌套树状复制结构。
- 避免产生 hot-key,导致主库节点成为系统的短板。
- 避免产生 big-key,导致网卡撑爆、慢查询等。
- 重试时间应该大于 cluster-node-time 时间。
- Redis Cluster 不建议使用 pipeline 和 multi-keys 操作,减少 max redirect 产生的场景。
配置
daemonize yes
port 8001
dir /usr/local/rediscluster/8001/
cluster-enabled yes
cluster-config-file nodes-8001.conf
cluster-node-timeout 5000
# bind 127.0.0.1
protected-mode no
appendonly yes
requirepass xxx
masterauth xxx
启动
redis-cli -a xxx --cluster create --cluster-replicas 1 xxx:xx
集群原理
槽位计算
CRC16(key) mod 16384<br /> - redis-cli --cluster<br /> - reshard 手动分配槽位<br /> - rebalance 重新平衡槽位<br /> - add-node 增加节点 默认新增的都是主结点<br /> - del-node 删除节点<br /> - 主结点选举过程 (选举周期currentEpoch)<br /> - slave发现自己的master状态变为fail(超时 cluster-node-timeout)<br /> - 将集群选举周期currentEpoch加一, 并广播FAILOVER_AUTH_REQUEST信息, 经过一定延迟(500ms + random(0 ~ 500ms) + SLAVE_RANK * 1000ms))发送<br /> - 其他节电收到改信心,只有master响应,判断请求的合法性,并发送FAILOVER_AUTH_ACK,对每个epoch只发送一次ack<br /> - 同意数据大于一半的时候就成为新的主结点<br /> - 广播Pong通知其他节点<br />jedis<br /> - socket<br /> - message protocol RESP <br /> - *3 有3个数组<br /> - $4 4个字符