7. Redis持久化有哪几种方式?不同的持久化机制都有什么优缺点?持久化机制具体底层是如何实现的?

为什么要持久化?

如果只是存在内存里,如果redis宕机再重启,内存数据就丢失了,所以要用持久化机制。

将数据写入内存的同时,异步的慢慢将数据写入磁盘文件,定期同步或备份到云存储服务上,进行持久化。

如果redis宕机重启,自动从磁盘加载之前持久化的一些数据,也许会丢失少量数据,但至少不会丢所有数据。

Redis持久化的两种方式: RDBAOF

RDB(Redis Database): 是对redis中的数据执行周期性的持久化。

简单说就是每个几分钟或几个小时,生成redis内存中数据的一份全量快照副本。

AOF(Append-Only-File):是对每条写入命令作为日志,以append-only的模式写入一个日志文件中,在redis重启的时候,可以通过回放AOF日志中的写入指令来重新构建整个数据集。

现在操作系统中,写文件不是直接写磁盘,会先写os cache(linux),然后到一定时间从os cache写到磁盘文件。

如果同时使用 RDB 和 AOF 两种持久化机制,那么在 redis 重启的时候,会使用 AOF 来重新构建数据,因为 AOF 中的数据更加完整。

RDB优缺点:

优点:

1. 非常适合做冷备份。

RDB生成多个文件,每个文件代表一个时刻的完整快照。

有redis去控制固定时长生成快照文件,比较方便,隔一段时间备份数据到远程安全的存储上。

2. 对redis影响小,让redis保持高性能。

因为redis主进程只需fork一个子进程,让子进程进行IO操作完成RDB持久化。

RDB每次写,都是直接写redis内存,只是在一定时候,才会将数据写入磁盘中;

AOF每次都是要写文件,虽然可以快速写os cache,但是还是有一定时间开销,速度肯定比RDB慢一些。

3. 相对于AOF,RDB数据文件重启和恢复redis进程更快

AOF是存放的指令日志,做数据恢复时,其实是回放和执行所有指令日志来恢复内存中数据;RDB本身就是一份数据文件,恢复时,直接加载到内存即可。

缺点:

1. RDB丢失数据更多。

一般RDB设置间隔5分钟一次,那么这时宕机需要接受丢失最近5分钟数据。

2. 不适合做第一优先的恢复方案。

如果数据文件特别大,会导致对客户端提供服务暂停数毫秒甚至数秒,一般不要让RDB间隔太长时间。

AOF优缺点:

优点:

1. 更好保证数据不丢失或丢得很少。

每个1秒做一次fsync,保证os cache中数据写入磁盘,最多丢1秒数据。

2. 写入性能高。

以append-only写入,没有磁盘寻找开销,即使文件尾部破损,也容易恢复。

3. AOF日志文件即使过大也不影响客户端读写

因为在rewrite log时,会压缩指令,创建一份需要恢复数据的最小日志。

创建新日志时,老日志继续写,当新的merge后的日志文件ready时,再交换新老日志,删除老日志。

4. AOPF日志是人类易读的,非常适合做灾难性的误删除的紧急恢复。

比如某人不小心flushall命令清空了所有数据,只要rewrite还没有发生,可立即拷贝AOF文件,将最后一个flushall命令删掉,再将AOF放回去,

通过恢复机制,自动恢复所有数据。(很少用)

缺点:

1. 占用磁盘比RDB多。

对同一份数据,AOF文件比RDB数据快照文件大。

2. QPS比RDB低一些。

AOF开启,写QPS比RDB的写QPS低。因为AOF一般会配置成每秒fsync一次日志文件,性能还OK。

3. 唯一的比较大缺点,做数据恢复比较慢;还有做冷备不合适,需要写复制脚本算法。

RDB和AOF的选择:

1. 不能仅仅用RDB,会导致丢失很多数据

2. 也不能只用AOF,其一AOPF做冷备,没有RDB恢复快;其二,RDB每次通过生成数据快照,恢复更健壮,避免AOF的复制备份和恢复机制的bug。

3. 综合用AOF和RDB,用AOF保证数据基本不丢,作为数据恢复第一选择;

用RDB做不同时间间隔的冷备,在AOF文件都丢失时或损坏不可用时,从远程云服务等备份的RDB来进行快速数据恢复,作为Redis数据可用的最后一道防线。

8. Redis 集群模式的工作原理能说一下么?在集群模式下,redis key 是如何寻址?分布式寻址都有哪些算法?了解一致性 hash 算法吗?RedisCluster高可用?

 

  8.1. redis单master架构面对海量数据的容量问题

问题背景:master节点的数据和slave节点的 数据一模一样,slave容量只能最大容纳和master一样容量的数据。

数据超过后,会采取LRU清除算法,把旧的很少使用的数据清除出内存,不可能超过内存上限。

解决方案:Redis Cluster架构 = 多master + 读写分离 + 高可用

支持N个redis master节点,每个master节点可以挂多个slave。

读写分离:写就写到master,读从master对应的slave读。

高可用:每个master都有slave节点,如果master挂了,redis cluster这套机制会自动将某个salve切换成master。

Redis Cluster   vs.  Replication + sentinel 比较:

如果数据量少,主要承载高并发高性能场景,如果缓存只有几个G,单机足矣。

Replication:一个 master 多个 slave,要几个 slave 跟你要求的读吞吐量有关,然后自己搭建一个 Sentinel 集群去保证 redis 主从架构的高可用性

    Redis Cluster:主要是针对海量数据+高并发+高可用的场景。

    Redis Cluster 支撑 N 个 redis master node,每个master node都可以挂载多个 slave node。这样整个 redis 就可以横向扩容了。

 8.2. 分布式数据存储的核心算法

hash算法 -> 一致性hash算法(memecached)  ->  Redis Cluster的hash slot算法

hash算法(缓存里现在用的少):计算key的hash值,对节点数量取模。

最大问题:某一个master宕机,所有请求会基于最新2个master取模,会导致几乎大部分请求无法拿到有效缓存。

原来对3取模,现在对2取模,缓存没有命中,就会涌向数据库。

一致性hash算法:

将整个 hash 值空间组织成一个虚拟的圆环,整个空间按顺时针方向组织,下一步将各个 master 节点(使用服务器的 ip 或主机名)进行 hash,

这样确定每个节点在哈希环上的位置。

来一个key,计算hash值,确定在环上的位置,从此位置沿环顺时针行走,遇到第一个master节点就是key所在位置。

如果3台中1台挂了,只有1/3的数据会找不到,涌向DB。只有1/N的流量在master宕机会失效。

受影响的数据仅仅时此节点到环的前一个节点之间的数据,增加一个节点也同理。

一致性哈希算法在节点太少时,容易因为节点分布不均匀而造成缓存热点的问题。

为了解决这种热点问题,一致性 hash 算法引入了虚拟节点机制,即对每一个节点计算多个 hash,每个计算结果位置都放置一个虚拟节点。

这样就实现了数据的均匀分布,负载均衡。

Redis Cluster的hash slot算法

Redis Cluster固定16384个hash slot, 对每个key计算出对应的hash slot。

hash slot让节点的增加和移除很简单,移动hash slot成本很低。

任何一台节点宕机,另外两个节点不受影响,因为key找到的是hash slot,不是机器。redis cluster会快速把宕机节点的hash slot均匀分布到其他节点。

 8.3. Redis节点内部通信机制

Redis Cluster节点间采用gossip协议进行通信

集群元数据集中式存储:

集群有很多元数据,包括hash slot,节点之间映射表,还有master-slave关系等。

通过zookeeper集中式元数据的维护和存储,典型是大数据的Storm,底层基于zookeeper(分布式协调中间件)。

gossip协议存储:

所有节点都持有一份元数据,不同节点如果出现元数据变更,就不断将元数据发送给其他节点,让其他节点也进行元数据更新。

集中式的优点: 元数据的更新和读取,时效性好。一旦元数据更新,立即更新到集中式存储,其他节点读取时能立刻感知。

集中式的缺点:所有元数据都集中在一个地方,有存储压力(单点故障?)

gossip优点:元数据分布式存储,不是集中在一个地方,更新请求时陆陆续续,有一定延迟,减轻压力。

gossip缺点:元数据更新有延迟,可能导致集群的一些操作会有滞后。

gossip协议:

包含多种消息,包括ping, pong, meet, fail等等。

meet: 某个节点发送meet给新加入节点,让新节点加入集群中,然后新节点就开始与其他节点通信。

redis-trib.rb(官方提供的Redis Cluster的管理工具) add-node – 添加新节点

ping: 每个节点频繁与其他节点发送ping交换数据,包含自己状态和维护的集群元数据。

pong: 返回ping和meet,包含自己状态和其他信息,也可用于信息广播和更新

fail: 某个节点判断另外一个节点fail之后,发送fail给其他节点,通知其他节点,指定的节点宕机了。

 8.4. Jedis Cluster API与Redis Cluster集群交互的一些基本原理

1. 基于重定向的客户端

1)请求重定向

客户端可以挑选任意redis实例去发送命令,每个实例接收到命令后,会计算key对应的hash slot。

如果在本地就在本地处理,否则返回moved给客户端,让客户端进行重定向

redis-cli -c,-c参数,支持自动请求重定向,redis-cli接收到moved之后,会自动重定向到对应节点执行命令。

cluster keyslot mykey, 可以查看一个key对应的hash slot是多少。

2)计算hash slot

根据key计算CRC16值,然后对16384取模,拿到对应的hash slot。

用hash tag可以手动指定key对应的slot, 同一个hash tag下的key,都会在同一个hash slot中。

比如set key1:{100}和set key2:{100}

100:hash tag的值

3) hash slot查找

节点间通过gossip协议进行数据交换,就知道每个hash slot在哪个节点上。

  2. Smart Jedis

1) 什么是Smart Jedis

基于重定向客户端,很消耗网络IO,因为大部分情况下,可能都会出现一次请求重定向,才能找到正确的节点。

所以大部分客户端,比如java redis客户端(jedis),都是smart的。

本地维护一份hash slot -> node的映射表缓存,大部分情况下,直接走本地缓存就可以找到hashslot->node,不需要通过节点进行moved重定向。

2) JedisCluster的工作原理

在JedisCluster初始化时,就会随机选择一个node, 初始化hashslot->node映射表,同时为每个节点创建一个JedisPool连接池。

每次基于JedisCluster执行操作,首先JedisCluster都会在本地计算key的hashslot,然后在本地映射表找到对应的节点。

如果那个node正好还是持有那个hashslot,那没问题;如果说进行了reshard这样的操作,可能hashslot已经不在那个node上,就会返回moved。

如果发现JedisCluster api发现对应的节点返回moved,那么利用该节点的元数据,更新hashslot->node映射表。

重复上述步骤,直到找到对应节点。如果重试5次,就报错JedisClusterMaxRedirectException。

3)hash slot迁移和ask重定向

如果hash slot正在迁移,那么会返回ask重定向给jedis。

jedis接收到ask重定向后,会重新定位到目标节点去执行,但是因为ask发生在hash slot迁移过程中,

所以JedisCluster api收到ask是不会更新hash slot本地缓存,确定hash slot已经迁移完成,moved是会更新本地hashslot->node映射缓存的。

 8.5 Redis Cluster高可用和主备切换原理

高可用原理与哨兵类似。

    判断节点宕机:

在cluster-node-timeout内,某个节点一直没有返回pong,则被认为pfail。

如果一个节点认为某个节点 pfail 了,那么会在 gossip ping 消息中,ping 给其他节点,如果超过半数的节点都认为 pfail 了,那么就会变成 fail。

    从节点过滤:

对宕机的master node,从其所有的slave node中,选择一个切换成master node。

检查每个slave node与master node断开连接的时间,如果超过了 cluster-node-timeout * cluster-slave-validity-factor,那么就没有资格切换成 master。

    从节点选举:

每个从节点,都根据自己对 master 复制数据的 offset,来设置一个选举时间,offset 越大(复制数据越多)的从节点,选举时间越靠前,优先进行选举。

所有的 master node 开始 slave 选举投票,给要进行选举的 slave 进行投票,

如果大部分 master node(N/2 + 1)都投票给了某个从节点,那么选举通过,那个从节点可以切换成 master。

从节点执行主备切换,从节点切换为主节点。

    与哨兵比较

整个流程与哨兵非常类似,redis cluster 功能强大,直接集成了 replication 和 sentinel 的功能。

9. 了解什么是 redis 的缓存雪崩、穿透和击穿?redis 崩溃之后会怎么样?系统该如何应对这种情况?如何处理 redis 的穿透?

缓存雪崩:

假设高峰QPS=5000,缓存可以抗4000,但是缓存以外宕机,全部5000全都落在DB,DB扛不住,报警后,挂了。

如果没有特别方案来处理,就算DBA重启DB,还是会被新的流量打死。

解决方案:

  事前:Redis高可用,主从+哨兵,或Redis Cluster, 避免全盘崩溃。(仍存在全盘崩溃可能性)

  事中:本地ehcache缓存 + hystrix 限流&降级,避免DB被流量打死。

1. 用户发送请求,先查本地ehcache缓存,如果没有再查redis。如果echache和redis都没有,查DB。将DB的结果写入ehcache和redis。

2. 通过限流组件,设置每秒2000个请求,那么一秒过来5000个请求时,只有2000个通过限流组件,到达DB。

剩下的3000个请求走降级,会调用自定义的降级组件。比如返回默认值,友情提示,其他自定义处理等等。

虽然用户体验下降,但保证DB不会挂,系统仍可用!

  事后:Redis持久化机制,尽快恢复缓存集群,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。

                   

缓存穿透:

    某个key(可能是恶意攻击的)非常热点,访问非常频繁,数据库和缓存都没有这个数据,缓存穿透后把数据库打死。

解决方案:比如黑客发送查id(id应该都是正数)的请求,都是负数(-99, -999),每次从DB中没有查到的,就写一个空值到缓存。

比如set -99 UNKNOWN。这样再查就是走缓存。

10. 如何保证缓存与数据库的双写一致性?

  1. Cache Aside Pattern

最经典的缓存 + 数据库读写的模式,就是Cache Aside Pattern。

  • 读的时候,先读缓存,缓存没有,就读数据库,然后取出数据后放入缓存,同时返回响应。
  • 更新的时候,先更新数据库,然后再删除缓存。

2. 为什么是删除缓存,而不是更新缓存

原因是很多复杂缓存计算场景,因为缓存不简单是数据库中直接取出,而是需要涉及多个表。如果频繁修改缓存涉及的多个表,缓存也会频繁更新。

如果这个缓存是冷数据,用到缓存才去算缓存,降低开销。删除缓存,而不是更新缓存,就是一个lazy计算的思想(懒加载),它到需要被使用的时候再重新计算。

双写一致的解决方案:

    1. 最初级的缓存不一致问题及解决方案

场景:先修改数据库,再删除缓存。如果某个原因(网络超时等)删除缓存失败,就出现数据库与缓存双写不一致。

解决:调整顺序,先删除缓存,再修改数据库。

    2. 比较复杂的数据不一致问题分析

场景:数据发生了变更,先删除了缓存,然后要去修改数据库,此时还没修改。一个请求过来,去读缓存,发现缓存空了,去查询数据库,查到了修改前的旧数据,放到了缓存中。随后数据变更的程序完成了数据库的修改。结果数据库和缓存中的数据不一样了。

并发低时很少出现这种不一致,如果时高并发上亿流量,每秒并发几万,每秒只要有数据库更新请求,就可能出现上述数据库和缓存不一致。

解决:数据库与缓存更新进行异步串行化

更新数据库时,根据数据唯一标识,将操作路由之后,发送到一个JVM内部队列中

读取数据时,如果发现数据不在缓存中,将重新读取数据+更新缓存的操作,根据唯一标识路由之后,也发送同一个JVM内部队列中

一个队列对应一个工作线程,每个工作线程串行拿到对应的操作,然后一条一条执行

优化点:一个队列中,如果有一个读请求,不用再放入同样的读请求进入队列,排除重复读请求。

该方案在高并发场景要注意的问题:

1. 读请求长时阻塞

可能数据更新频繁,导致队列中积压大量更新操作,然后读请求发生大量超时,导致大量请求直接走数据库。

2. 读请求并发量过高

做好压力测试,看服务能不能抗住大量读请求,需要多少机器才能抗住最大极限峰值。

3. 多服务实例部署的请求路由

对同一个业务的读写请求,保证路由到相同的服务实例上。可自己实现请求参数hash路由,也可以用Nginx的hash路由功能。

4. 热点商品路由问题,导致请求倾斜

某个业务读写请求特别高,全部打到相同机器的相同队列了,操作某台机器压力过大。

总结,一般来说,如果系统不是严格要求缓存+数据库必须一致的话,最好不采用这个方案。

读写请求串行化,会导致系统吞吐量大幅度下降,需要用比正常情况下多几倍的机器来支撑。

11. Redis 的并发竞争问题是什么?如何解决这个问题?了解 redis 事务的 CAS 方案吗?

分布式锁确保同一个时间只有一个系统实例在操作某个key,其他实例不允许读和写。

每次写之前,判断当前value的时间戳是否比缓存里的value的时间戳更新,如果更新则可以写,如果更旧,就不能用旧数据覆盖新数据。

12. 生产环境中的redis是怎么部署的?

看看你了解不了解你们公司的 redis 生产集群的部署架构,如果你不了解,那么确实你就很失职了。

你的 redis 是主从架构?集群架构?用了哪种集群方案?有没有做高可用保证?有没有开启持久化机制确保可以进行数据恢复?

线上 redis 给几个 G 的内存?设置了哪些参数?压测后你们 redis 集群承载多少 QPS?

Redis Cluster, 10台机器, 5主5从。每个主实例挂一个从实例。5个节点对外提供读写服务,每个节点读写高峰QPS可能达5W/S,5台最多25W QPS/S。

8核CPU+32G内存+1T磁盘。分给Redis进程10G内存。一般生产环境,Redis内存不要超过10G。

[Java复习] 缓存Cache part2的更多相关文章

  1. [Java复习] 缓存Cache part1

    1. 在项目中是如何使用缓存的?为什么要用?不用行不行?用了可能会有哪些不良后果? 结合项目业务,主要两个目的:高性能和高并发.缓存走内存,天然支持高并发. 不良后果: 缓存与DB双写不一致 缓存雪崩 ...

  2. Java 中常用缓存Cache机制的实现

    所谓缓存,就是将程序或系统经常要调用的对象存在内存中,一遍其使用时可以快速调用,不必再去创建新的重复的实例.这样做可以减少系统开销,提高系统效率. 所谓缓存,就是将程序或系统经常要调用的对象存在内存中 ...

  3. Java 中常用缓存Cache机制的实现《二》

    所谓缓存,就是将程序或系统经常要调用的对象存在内存中,一遍其使用时可以快速调用,不必再去创建新的重复的实例.这样做可以减少系统开销,提高系统效率. AD: Cache 所谓缓存,就是将程序或系统经常要 ...

  4. Java中常用缓存Cache机制的实现

    缓存,就是将程序或系统经常要调用的对象存在内存中,一遍其使用时可以快速调用,不必再去创建新的重复的实例. 这样做可以减少系统开销,提高系统效率. 缓存主要可分为二大类: 一.通过文件缓存,顾名思义文件 ...

  5. Java中经常使用缓存Cache机制的实现

    缓存,就是将程序或系统常常要调用的对象存在内存中,一遍其使用时能够高速调用,不必再去创建新的反复的实例. 这样做能够降低系统开销.提高系统效率. 缓存主要可分为二大类: 一.通过文件缓存,顾名思义文件 ...

  6. 5个强大的Java分布式缓存框架推荐

    在开发中大型Java软件项目时,很多Java架构师都会遇到数据库读写瓶颈,如果你在系统架构时并没有将缓存策略考虑进去,或者并没有选择更优的 缓存策略,那么到时候重构起来将会是一个噩梦.本文主要是分享了 ...

  7. java 复习001

    java 复习001 比较随意的记录下我的java复习笔记 ArrayList 内存扩展方法 分配一片更大的内存空间,复制原有的数据到新的内存中,让引用指向新的内存地址 ArrayList在内存不够时 ...

  8. java 开源缓存框架--转载

    原文地址:http://www.open-open.com/13.htm  JBossCache/TreeCache  JBossCache是一个复制的事务处理缓存,它允许你缓存企业级应用数据来更好的 ...

  9. Java分布式缓存框架

    http://developer.51cto.com/art/201411/457423.htm 在开发中大型Java软件项目时,很多Java架构师都会遇到数据库读写瓶颈,如果你在系统架构时并没有将缓 ...

随机推荐

  1. 使用Arduino开发板控制步进电机

    目前为止,我还没有真正深入了解过电机,特别是步进电机. 最近我在计划一个项目,需要相对精确的电机控制,所以可能会使用到步进电机,但很快就意识到我首先应该更多地了解这些. 本篇文章主要介绍我到目前为止学 ...

  2. Win10 hosts文件无法保存

    Win10无法修改编辑保存hosts文件怎么办?Win10系统默认是没有权限去编辑保存系统里的文件,这也是权限不够才导致修改编辑hosts后无法保存的原因,解决的办法就是把自己的帐户权限给提高就行了. ...

  3. SQL进阶17-变量的声明/使用(输出)--全局变量/会话变量--用户变量/局部变量

    /*进阶17 变量 系统变量: 全局变量: 会话变量: 自定义变量: 用户变量: 局部变量: */ /* #一: 系统变量 #说明: 变量由系统提供,不是用户定义的,属于服务器层面 #使用的语法 #1 ...

  4. P2280 [HNOI2003]激光炸弹[前缀和]

    题目描述 输入输出格式 输入格式: 输入文件名为input.txt 输入文件的第一行为正整数n和正整数R,接下来的n行每行有3个正整数,分别表示 xi,yi ,vi . 输出格式: 输出文件名为out ...

  5. POI进行导出时候发现有不可读取的内容

    通过后台查询数据,然后使用poi进行导出时候,excel进行打开会出现下面的异常: 但是在WPS中就没有问题, 如果点击否,则不会显示任何内容,点击是,就会弹出来 查看修改记录为: 刚开始也进行了很多 ...

  6. .NET Core SignalR 和 .NET SignalR 区别

    由于要转 .NET Core ,对于以前用到的一些进行迁移. 在迁移 SignalR 的时候发现 .NET Core 下的和 .NET 下的区别还是挺大的. 功能差异 自定重新连接 .NET 下的 S ...

  7. mac XXX 已损坏,打不开。 您应该将它移到废纸篓。

    mac 上的软件我改了某些配置,打开提示这个. 是因为安全认证问题. 在系统偏好设置里面,安全与隐私设置为允许任何来源就可以. 如果新版系统没有这个选项,那么在命令行输入:sudo spctl --m ...

  8. 使用jQuery快速高效制作网页交互特效---jQuery选择器

    一.什么是jQuery选择器 Query选择器继承了CSS与Path语言的部分语法,允许通过标签名.属性名或内容对DOM元素进行快速.准确的选择, 而不必担心浏览器的兼容性,通过jQuery选择器对页 ...

  9. SQL动态标签

    MyBatis的动态SQL详解MyBatis 的强大特性之一便是它的动态 SQL.如果你有使用 JDBC 或其他类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句有多么痛苦.拼接的时候要确保不 ...

  10. MongoDB 副本集主从切换方法

    一.方法一rs.setpDown() 将Primary节点降级为Secondary节点 myapp:PRIMARY> rs.stepDown() 这个命令会让primary降级为Secondar ...