如果你觉得是非是真实存在的,那你一定是出现幻觉了
常见的nosql一般是
1、redis
2、mongodb
3、memcache
4、HBase
Redis
Redis 是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。目前由VMware主持开发工作。
Redis 都有哪些应用场景?
1、缓存:这应该是 Redis 最主要的功能了,也是大型网站必备机制,合理地使用缓存不仅可以加 快数据的访问速度,而且能够有效地降低后端数据源的压力。
2、共享Session:对于一些依赖 session 功能的服务来说,如果需要从单机变成集群的话,可以选择 redis 来统一管理 session。
3、消息队列系统:消息队列系统可以说是一个大型网站的必备基础组件,因为其具有业务 解耦、非实时业务削峰等特性。Redis提供了发布订阅功能和阻塞队列的功 能,虽然和专业的消息队列比还不够足够强大,但是对于一般的消息队列功 能基本可以满足。比如在分布式爬虫系统中,使用 redis 来统一管理 url队列。
4、分布式锁:在分布式服务中。可以利用Redis的setnx功能来编写分布式的锁,虽然这个可能不是太常用。
5、诸如排行榜、点赞功能都可以使用 Redis 来实现,但是 Redis 也不是什么都可以做,比如数据量特别大时,不适合 Redis,我们知道 Redis 是基于内存的,虽然内存很便宜,但是如果你每天的数据量特别大,比如几亿条的用户行为日志数据,用 Redis 来存储的话,成本相当的高。
Redis 性能为什么这么快?
Redis 官方给出的答案是读写速度 10万/秒
1、纯内存操作:Redis 是完全基于内存的,所以读写效率非常的高,当然 Redis 存在持久化操作,在持久化操作是都是 fork 子进程和利用 Linux 系统的页缓存技术来完成,并不会影响 Redis 的性能。
2、单线程操作:单线程并不是坏事,单线程可以避免了频繁的上下文切换,频繁的上下文切换也会影响性能的。
合理高效的数据结构
3、采用了非阻塞 I/O 多路复用机制:多路I/O复用模型是利用 select、poll、epoll 可以同时监察多个流的 I/O 事件的能力,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有 I/O 事件时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流(epoll 是只轮询那些真正发出了事件的流),并且只依次顺序的处理就绪的流,这种做法就避免了大量的无用操作。
redis能否将数据持久化,如何实现?有什么优缺点?
将内存中的数据异步写入硬盘中,两种方式:RDB(默认)和AOF和REDIS4.0混合持久化
RDB持久化原理:通过bgsave命令触发,然后父进程执行fork操作创建子进程,子进程创建RDB文件,根据父进程内存生成临时快照文件,完成后对原有文件进行原子替换(定时一次性将所有数据进行快照生成一份副本存储在硬盘中)
优点:是一个紧凑压缩的二进制文件,Redis加载RDB恢复数据远远快于AOF的方式。
缺点:由于每次生成RDB开销较大,非实时持久化,恢复时容易落数据
AOF持久化原理:开启后,Redis每执行一个修改数据的命令,都会把这个命令添加到AOF文件中。
优点:实时持久化。
aof同步频率选项👇,默认是1秒一次,如果突然宕机,有可能丢失一秒的数据
缺点:所以AOF文件体积逐渐变大,需要定期执行重写操作来降低文件体积,加载慢,恢复速度慢;
REDIS 4.0混合持久化
重启redis恢复数据集时,很少会使用rdb来恢复内存状态,因为会丢失大量数据。通常会使用aof日志恢复数据,但是重放aof日志性能相对rdb来说要慢很多,这样在redis实例很大的情况下,启动需要花费很长时间。Redis4.0为了解决这个问题,带来了新的持久化选项——混合持久化。
配置是👇
aof-use-rdb-preamble yes
redis优点是什么?
1、非常丰富的数据结构,相对于memcache
2、Redis提供了事务的功能,可以保证一串 命令的原子性,中间不会被任何操作打断;
3、 数据存在内存中,读写非常的高速,可以达到10w/s的频率。
redis缺点是啥?
1、 Redis3.0后才出来官方的集群方案;
2、持久化功能体验不佳——通过快照方法实现的话,需要每隔一段时间将整个数据库的数据写到磁盘上,代价非常高;而aof方法只追踪变化的数据,类似于mysql的binlog方法,但追加log可能过大,同时所有操作均要重新执行一遍,恢复速度慢;
3、由于是内存数据库,所以,单台机器,存储的数据量,跟机器本身的内存大小。虽然redis本身有key过期策略,但是还是需要提前预估和节约内存。如果内存增长过快,需要定期删除数据。
redis适用场景?
适用于数据变化快且数据库大小可遇见(适合内存容量)的应用程序
redis的高可用?
redis的高可用分两种实现,一种是redis-cluster集群,一种是哨兵模式
哨兵模式:
Redis Sentinel是社区版本推出的原生高可用方案,其部署架构主要包括两部分:Redis Sentinel集群和Redis数据集群。
其中Redis Sentinel集群是由若干Sentinel节点组成的分布式集群,可以实现故障发现、故障自动转移、配置中心和客户端通知。Redis Sentinel的节点数量要满足2n+1(n>=1)的奇数个,选举算法基于Raft。
优点:
1、能够解决Redis主从模式下的高可用切换问题;
2、很方便实现Redis数据节点的线形扩展,轻松突破Redis自身单线程瓶颈,可极大满足Redis大容量或高性能的业务需求;
3、可以实现一套Sentinel监控一组Redis数据节点或多组数据节点。
缺点:
1、部署相对Redis主从模式要复杂一些,原理理解更繁琐;
2、资源浪费,Redis数据节点中slave节点作为备份节点不提供服务;
3、Redis Sentinel主要是针对Redis数据节点中的主节点的高可用切换,对Redis的数据节点做失败判定分为主观下线和客观下线两种,对于Redis的从节点有对节点做主观下线操作,并不执行故障转移。
4、不能解决读写分离问题,实现起来相对复杂。
redis-cluster集群:
从redis 3.0之后版本支持redis-cluster集群,Redis-Cluster采用无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有节点连接。
特点:
1、无中心架构(不存在哪个节点影响性能瓶颈)。
2、数据按照 slot (16384)分布在多个节点,节点间数据共享,可动态调整数据分布。
3、可扩展性,可线性扩展到 1000 个节点,节点可动态添加或删除。
4、高可用性,部分节点不可用时,集群仍可用。通过增加 Slave 做备份数据副本
5、实现故障自动 failover,节点之间通过 gossip 协议交换状态信息,用投票机制完成 Slave到 Master 的角色提升。
缺点:
1、资源隔离性较差,容易出现相互影响的情况。
2、数据通过异步复制,不保证数据的强一致性
Redis Sentinal 主要着眼于高可用,在master宕机时会自动将slave提升为master,继续提供服务。
Redis Cluster 主要着眼于扩展性,在单个redis内存不足时,使用Cluster进行分片存储。
Redis的同步机制?
Redis可以使用主从同步,从从同步。第一次同步时,主节点做一次bgsave,并同时将后续修改操作记录到内存buffer,待完成后将RDB文件全量同步到复制节点,复制节点接受完成后将RDB镜像加载到内存。加载完成后,再通知主节点将期间修改的操作记录同步到复制节点进行重放就完成了同步过程。后续的增量数据通过AOF日志同步即可,有点类似数据库的binlog。
RDB的原理是什么?
两个词汇就可以说明白,fork和cow。fork是指redis通过创建子进程来进行RDB操作,cow指的是copy on write,子进程创建后,父子进程共享数据段,父进程继续提供读写服务,写脏的页面数据会逐渐和子进程分离开来。
注:回答这个问题的时候,如果你还能说出AOF和RDB的优缺点,我觉得我是面试官在这个问题上我会给你点赞,两者其实区别还是很大的,而且涉及到Redis集群的数据同步问题等等。想了解的伙伴也可以留言,我会专门写一篇来介绍的。
redis如何做队列?
一般使用list结构作为队列,rpush生产消息,lpop消费消息。当lpop没有消息的时候,要适当sleep一会再重试(也可以不用sleep,list还有个指令叫blpop,在没有消息的时候,它会阻塞住直到消息到来。)redis还可以用zset做延时队列
Redis的出队性能都表现的非常优秀,而入队性能在数据比较大时,慢的无法忍受
相比之下rabbitmq 性能比较稳定,redis的消息队列适合快产快消,消息量较小的情况
pub/sub有什么缺点?
虽然redis实现了发布订阅(publish/subscribe)的功能(可以实现 1:N 的消息队列),但是在通常的情况下是不推荐使用的,如果想使用消息队列这种功能,最好还是使用专业的各种MQ中间件,例如rabbitMQ,rockedMQ,activitedMQ等.
第一个原因是和redis系统的稳定性有关。对于旧版的redis来说,如果一个客户端订阅了某个或者某些频道,但是它读取消息的速度不够快,那么不断的积压的消息就会使得redis输出缓冲区的体积越来越大,这可能会导致redis的速度变慢,甚至直接崩溃。也可能会导致redis被操作系统强制杀死,甚至导致操作系统本身不可用。新版的redis不会出现这种问题,因为它会自动断开不符合client-output-buffer-limit pubsub配置选项要求的订阅客户端
第二个原因是和数据传输的可靠性有关。任何网络系统在执行操作时都可能会遇到断网的情况。而断线产生的连接错误通常会使得网络连接两端中的一端进行重新连接。如果客户端在执行订阅操作的过程中断线,那么客户端将会丢失在断线期间的消息,这在很多业务场景下是不可忍受的。
主要是在消费者下线的情况下,生产的消息会丢失,得使用专业的消息队列
假如Redis里面有1亿个key,其中有10w个key是以某个固定的已知的前缀开头的,如何将它们全部找出来?
使用keys指令可以扫出指定模式的key列表。但是keys会堵塞,可以用scan,keys导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕。可以用scan指令,scan指令可以无阻塞的提取出指定模式的key列表,但是有重复的概率,需要客户端去重,但是整体所花费的时间会比直接用keys指令长
redis怎么实现分布式锁?
加锁应该使用set指令
错误方法❌👇
setnx获取锁,拿到锁用expire给锁加一个过期时间,防止锁忘记释放。如果setnx执行之后expire执行之前,线程死掉,那锁就永远得不到释放,发生死锁。
Long result = jedis.setnx(lockKey, requestId);
if (result == 1) {
// 线程死掉,无法设置过期时间,发生死锁
jedis.expire(lockKey, expireTime);
}
最佳实践👉set指令=setnx和expire,一条指令,把setnx和expire原子化结合起来。
set key value [ex seconds] [px milliseconds] [nx|xx]
ex seconds: 为键设置秒级过期时间。
px milliseconds: 为键设置毫秒级过期时间。
nx: 键必须不存在, 才可以设置成功, 用于添加。
xx: 与nx相反, 键必须存在, 才可以设置成功, 用于更新。
解锁使用lua脚本(lua保证原子性)
错误示例❌👇
最常见的解锁代码就是直接使用jedis.del()方法删除锁,这种不先判断锁的拥有者而直接解锁的方式,会导致任何客户端都可以随时进行解锁,即使这把锁不是它的。
public static void wrongReleaseLock1(Jedis jedis, String lockKey) {
jedis.del(lockKey);
}
正确示例👇
/**
* 释放分布式锁
* @param jedis Redis客户端
* @param lockKey 锁
* @param requestId 请求标识
* @return 是否释放成功
*/
public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));
if (RELEASE_SUCCESS.equals(result)) {
return true;
}
return false;
}
redis pipeline(管道)?
pipeline是Redis的一个提高吞吐量的机制,适用于多key读写场景,比如同时读取多个key的value,或者更新多个key的value
Redis本身是基于Request/Response协议(停等机制)的,正常情况下,客户端发送一个命令,等待Redis返回结果,Redis接收到命令,处理后响应。在这种情况下,如果同时需要执行大量的命令,那就是等待上一条命令应答后再执行,这中间不仅仅多了RTT(交互往返的时间),而且还频繁调用系统IO,发送网络请求。为了提升效率,这时候pipeline出现了,它允许客户端可以一次发送多条命令,而不等待上一条命令执行的结果,这和网络的Nagel算法有点像(TCP_NODELAY选项)。pipeline不仅减少了RTT,同时也减少了IO调用次数(IO调用涉及到用户态到内核态之间的切换)。
对于 pipeline 是不能有其它操作,结果也不能一下返回,需要等批量操作结果全部一起返回。有些系统可能对可靠性要求很高,每次操作都需要立马知道这次操作是否成功,是否数据已经写进redis了,那这种场景就不适合操作
如何保证缓存与数据库双写时的数据一致性?
先更新数据库,然后再删除缓存,Facebook就是这么干的。
上述还是有可能脏数据,比如获取缓存并写缓存和 更新缓存删除缓存并发操作有可能有并发问题
可以使用订阅binlog的方式,数据变更统一走订阅mysql的binlog
缓存降级?
缓存降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。
在进行降级之前要对系统进行梳理,看看系统是不是可以丢卒保帅;从而梳理出哪些必须誓死保护,哪些可降级;比如可以参考日志级别设置预案:
一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;
警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;
错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;
严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。
服务降级的目的,是为了防止Redis服务故障,导致数据库跟着一起发生雪崩问题。因此,对于不重要的缓存数据,可以采取服务降级策略,例如一个比较常见的做法就是,Redis出现问题,不去数据库查询,而是直接返回默认值给用户。
缓存预热?
缓存预热就是系统上线后,将相关的缓存数据直接加载到缓存系统。这样就可以避免在用户请求的时候,先查询数据库,然后再将数据缓存的问题!用户直接查询事先被预热的缓存数据!
解决方案
直接写个缓存刷新页面,上线时手工操作一下;
数据量不大,可以在项目启动的时候自动进行加载;
定时刷新缓存;
Redis支持的Java客户端都有哪些?官方推荐用哪个?
Redisson、Jedis、lettuce等等,官方推荐使用Redisson。
Redis和Redisson有什么关系?
Redisson是一个高级的分布式协调Redis客服端,能帮助用户在分布式环境中轻松实现一些Java的对象 (Bloom filter, BitSet, Set, SetMultimap, ScoredSortedSet, SortedSet, Map, ConcurrentMap, List, ListMultimap, Queue, BlockingQueue, Deque, BlockingDeque, Semaphore, Lock, ReadWriteLock, AtomicLong, CountDownLatch, Publish / Subscribe, HyperLogLog)。
Jedis与Redisson对比有什么优缺点?
Jedis是Redis的Java实现的客户端,其API提供了比较全面的Redis命令的支持;Redisson实现了分布式和可扩展的Java数据结构,和Jedis相比,功能较为简单,不支持字符串操作,不支持排序、事务、管道、分区等Redis特性。Redisson的宗旨是促进使用者对Redis的关注分离,从而让使用者能够将精力更集中地放在处理业务逻辑上。
redis高阶装逼?
String、Hash、List、Set、SortedSet。
这里我相信99%的读者都能回答上来Redis的5个基本数据类型。
但是,如果你是Redis中高级用户,而且你要在这次面试中突出你和其他候选人的不同,还需要加上下面几种数据结构HyperLogLog、Geo、Pub/Sub。
如果你还想加分,那你说还玩过Redis Module,像BloomFilter(使用string是实现,string是二进制安全的),RedisSearch,Redis-ML,这个时候面试官得眼睛就开始发亮了,心想这个小伙子有点东西啊。
注:面试回答到Redis相关的问题的时候,经常提到BloomFilter(布隆过滤器)并且使用场景比较多
HyperLogLog
redis的HyperLogLog是能够提供不精确的去重计数。能够使用极少的内存来统计巨量的数据(12k能统计2^64个数据),计数存在一定的误差,误差率整体较低。标准误差为 0.81% ,不过误差可以被设置辅助计算因子进行降低。
HyperLogLog是Probabilistic data Structures的一种,这类数据结构的基本大的思路就是使用统计概率上的算法,牺牲数据的精准性来节省内存的占用空间及提升相关操作的性能。最典型的使用场景就是统计网站的每日UV
Geo
用于存储经纬度坐标信息,可以搜索坐标半径据类内的坐标信息,底层用zset实现
布隆过滤器思想?
Hash存在一个冲突(碰撞)的问题,用同一个Hash得到的两个URL的值有可能相同。为了减少冲突,我们可以多引入几个Hash,如果通过其中的一个Hash值我们得出某元素不在集合中,那么该元素肯定不在集合中。只有在所有的Hash函数告诉我们该元素在集合中时,才能确定该元素存在于集合中。这便是Bloom-Filter的基本思想。
Bloom-Filter一般用于在大数据量的集合中判定某元素是否存在。
Bloom-Filter由一个很长的二进制向量和一系列随机映射的函数组成,通过多个Hash函数将一个元素映射到一个Bit Array中的多个点,查询的时候仅当所有的映射点都为1才能判断元素存在于集合内;BF用于检索一个元素是否在一个集合中,记忆集合求交集;优点是空间和时间效率都超过一般查询算法,缺点是有一定的误判概率和删除困难;
redis bigkeys 解决
大Key会带来的问题
注:redis key单个最大512M,通常情况下1m就算比较大了
1、如果是集群模式下,无法做到负载均衡,导致请求倾斜到某个实例上,而这个实例的QPS会比较大,内存占用也较多;对于Redis单线程模型又容易出现CPU瓶颈,当内存出现瓶颈时,只能进行纵向库容,使用更牛逼的服务器。
2、涉及到大key的操作,尤其是使用hgetall、lrange 0 -1、get、hmget 等操作时,网卡可能会成为瓶颈,也会到导致堵塞其它操作,qps 就有可能出现突降或者突升的情况,趋势上看起来十分不平滑,严重时会导致应用程序连不上,实例或者集群在某些时间段内不可用的状态。
3、假如这个key需要进行删除操作,如果直接进行DEL 操作,被操作的实例会被Block住,导致无法响应应用的请求,而这个Block的时间会随着key的变大而变长。
排查大key方法
1、在redis实例上执行bgsave,然后我们对dump出来的rdb文件进行分析,找到其中的大KEY
2、有个不太推荐的命令,debug object xxx 可以看到这个key在内存中序列化后的大小,当然我们可以通过SCAN+debug object xxx 得到当前实例所有key的大小。
3、redis-cli 原生自带 –bigkeys 功能,可以找到某个实例 5种数据类型(String、hash、list、set、zset)的最大key。
生产环境Redis中的热点key如何发现并优化?
与上期叶问一样,Redis的热点key问题同样值得我们关注,Redis的热点key出现主要有以下几种情况
1、用户消费的数据远大于生产的数据(热卖商品、热点新闻、热点评论、明星直播)
2、请求分片集中,超过单Server的性能极限。
热点key可能造成如下问题:
1、流量集中,达到物理网卡上限
2、请求过多,缓存分片服务被打垮
3、DB击穿,引起业务雪崩
如何发现热点key:
1、 凭借经验,进行预估:例如提前知道了某个活动的开启,那么就将此Key作为热点Key
2、 客户端收集:在操作Redis之前对数据进行统计
3、抓包进行评估:Redis使用TCP协议与客户端进行通信,通信协议采用的是RESP,所以能进行拦截包进行解析
4、 在proxy层,对每一个 redis 请求进行收集上报
5、Redis自带命令查询:Redis4.0.4版本提供了redis-cli –hotkeys就能找出热点Key
6、redis的monitor命令可以实时打印出 redis 服务器接收到的命令
抓取几分钟的数据,然后手动终止命令
redis-cli monitor >19-2.txt
如果要用Redis自带命令查询时,要注意需要先把内存逐出策略设置为allkeys-lfu或者volatile-lfu,否则会返回错误。进入Redis中使用config
set maxmemory-policy allkeys-lfu即可。
热点key解决方案如下:
1、服务端缓存:将热点数据缓存至服务端的内存中,二级缓存,用ehcache或者HashMap
2、备份热点Key:即将热点Key+随机数,随机分配至Redis其他节点中,value完全相同。这样访问热点key的时候就不会全部命中到一台机器上了。
3、读写分离:对于”get”类型的热点key,通常可以为redis添加slave,通过slave承担读压力来缓解
Redis Cluster集群的实现原理
一、 Redis Cluster架构
Redis Cluster是Redis在3.0版本推出的分布式解决方案。Redis Cluster由多个Redis节点组成。不同节点之间数据无交集,每个节点对应多个数据分片。节点内部分为主备节点,通过主备复制的方式保证数据的一致性。一个主节点可以有多个从节点,主节点提供读写服务,从节点提供读服务。
1.1 内部通信机制
Redis Cluster各节点之间通过Redis Cluster Bus交互信息。Redis Cluster每个节点都会记录集群的配置信息:如信息版本号Epoch、集群状态State等数据。
Redis Cluster的每个节点都保存着Node视角的集群结构。它描述了数据的分片方式,节点主备关系,并通过Epoch作为版本号实现集群结构信息的一致性,同时控制着数据迁移和故障转移的过程。
使用的协议是Gossip协议,此协议主要职责就是信息交换。信息交换的载体就是节点彼此发送的Gossip消息,常用的Gossip消息可分为:ping消息、pong消息、meet消息、fail消息
1.2 Redis Cluster去中心化
在Redis Cluster中,原则上每个主节点都有一个或多个Slave节点。集群中所有的Master节点都可以进行读写数据,不分主次。每个主节点与从节点间通过Goossip协议内部通信,异步复制。但是异步复制会导致某些特定情况下的数据丢失。所以,Redis Cluster不能保证数据的强一致性。
Redis Cluster设计的核心思想:数据拆分、去中心化。
二、 Redis Cluster数据分片
2.1 数据分片规则
在分布式集群中,如何保证相同请求落到相同的机器上,并且后面的集群机器可以尽可能的均分请求,需要使用合理的策略,有两种传统的方法:
1、哈希算法:采用固定节点数量,当某一节点宕机,缓存重建。
2、一致性哈希算法:当某一结点宕机,只有此节点数据受影响。会将压力压到数据库。
Redis Cluster使用的时hash slot算法通过采用固定节点数量和可配置映射节点,来避免取模的不灵活性和一致性哈希的部分影响。
Redis Cluster将所有数据按照hash slot算法分布到16384[0-16383]个哈希槽上面,哈希槽分布在各节点上,各节点维护自己的哈希槽。
2.2 客户端路由
当Client访问的Key不在当前节点的哈希槽中时,Redis Cluster会返回moved命令,并告知正确路由信息。当Client接收到moved命令时,会再次请求Redis并更新其内部路由缓存信息。
当Redis Cluster在数据重新分布时,Redis Cluster会使用ask命令用于重定向。因为在数据重新分布时,某个哈希槽的数据可能同时存在于新旧两个节点。所以ask只会重定向,并不会更新路由信息。
2.3 数据分片迁移
当有新主节点加入集群中、从集群中移除节点或者因数据分布不均衡时需要数据重新分布时,就需要对数据分片的迁移。数据迁移分为三个步骤:
1、向目标节点发送状态变更命令,将目标节点的对应哈希槽状态置为importing。
2、向源节点发送状态变更命令,将源节点对应的哈希槽状态置为migrating。
3、针对源节点上的哈希槽的所有key,向源节点发送migrate命令,告知源节点将对应的key迁移到目标节点。
当源节点的状态置为migrating后。此时源节点提供的服务和通常状态下有所区别:
1、如果Client访问的key尚未迁出,则正常的处理该key;
2、如果key已经迁出或者key不存在,则回复Client ASK,信息让其跳转到目标节点处理;
当目标节点状态变成importing后。表示对应的slot正在向目标节点迁入。目标节点和通常情况下有所区别:
1、对于该slot上所有非ask跳转的操作,目标节点不会进行操作,而是通过moved让Client跳转至源节点执行。这样就保证了同一个key在迁移之前总是在源节点执行。迁移后总是在目标节点执行,从而杜绝了双写的冲突。
2、迁移过程中,新增加的key会在目标节点执行,源节点不会新增key。使得迁移有界限,可以在某个确定的时刻结束。
单个key的迁移过程可以通过原子化的migrate命令完成。对于源节点和目标节点的从节点,是通过主备复制,从而达到增删数据。当所有key迁移完成后,Client 通过Redis Cluster的setslot命令设置目标节点的分片信息,从而包含了迁入的slot。设置过程中会让Epoch自增,并且是Cluster中的最新值。然后通过相互感知,传播到Cluster 中的其他节点。
三、Redis Cluster故障转移
3.1 节点故障判断
首先,在Redis Cluster中每个节点都存有集群中所有节点的信息。它们之间通过互相ping-pong判断节点是否可以连接。如果有一半以上的节点去ping一个节点的时候没有回应,集群就认为这个节点宕机。
3.2 slave选举
当主节点被集群公认为fail状态,那么它的从节点就会发起竞选,如果存在多个从节点,数据越新的节点越有可能发起竞选。集群中其他主节点返回响应信息。
3.3 结构变更
当竞选从节点收到过半主节点同意,便会成为新的主节点。此时会以最新的Epoch通过PONG消息广播,让Redis Cluster的其他节点尽快的更新集群信息。当原主节点恢复加入后会降级为从节点。
四、Redis Cluster高可用性
4.1 主节点保护
当集群中某节点中的所有从实例宕机时,Redis Cluster会将其他节点的非唯一从实例进行副本迁移,成为此节点的从实例。
这样集群中每个主节点至少有一个slave,使得Cluster 具有高可用。集群中只需要保持 2*master+1 个节点,就可以保持任一节点宕机时,故障转移后继续高可用。
4.2 集群fail条件
Redis Cluster保证基本可用的特性,在达到一定条件时才会认定为fail:
1、某个主节点和所有从节点全部挂掉,则集群进入fail状态。
2、如果集群超过半数以上主节点挂掉,无论是否有从节点,集群进入fail状态。
3、如果集群任意主节点挂掉,且当前主节点没有从节点,集群进入fail状态。
五、总结
Redis Cluster实现了在分布式环境下各节点之间自动分割数据集、多节点读写与高可用的能力!
主从复制的核心流程
概要流程:
- slave服务器配置master的连接信息(slaveof属性);
- slave连接上master,发送psync指令
- master判断是否为全量复制:如果是全量复制,则进入下一步;否则可以看增量复制的子流程。
- master启动一个后台线程,生成一份RDB快照文件,同时将从客户端收到的所有写命令缓存在内存中。
- RDB文件生成完毕之后,master会将RDB发送给slave。
- slave收到RDB文件之后,清空自己的旧数据,然后持久化到本地磁盘,再从本地磁盘加载到内存中。
- 最后salve node保存了RDB文件之后,master会将内存中缓存的写命令发送给slave,slave也会同步这些数据。
- 如果slave node开启了AOF,那么会立即执行BGREWRITEAOF,重写AOF
增量复制子流程:如果全量复制过程中,master-slave网络连接断掉,salve重新连接master时,会触发增量复制;master直接从自己的backlog中获取部分丢失的数据,发送给slave node,默认backlog就是1MB;msater就是根据slave发送的psync中的offset来从backlog中获取数据的
断点续传
从redis 2.8开始,就支持主从复制的断点续传,如果主从复制过程中,网络连接断掉了,那么可以接着上次复制的地方,继续复制下去,而不是从头开始复制一份
master node会在内存中常见一个backlog,master和slave都会保存一个replica offset还有一个master id,offset就是保存在backlog中的。如果master和slave网络连接断掉了,slave会让master从上次的replica offset开始继续复制;如果没有找到对应的offset,那么就会执行一次full resynchronization。
无磁盘化复制
master在内存中直接创建rdb,然后发送给slave,不会在自己本地落地磁盘了
repl-diskless-sync
repl-diskless-sync-delay,等待一定时长再开始复制,因为要等更多slave重新连接过来
过期key处理
slave不会过期key,只会等待master过期key。如果master过期了一个key,或者通过LRU淘汰了一个key,那么会模拟一条del命令发送给slave。
其他概念
主从节点互相都会发送heartbeat信息,master默认每隔10秒发送一次heartbeat,salve node每隔1秒发送一个heartbeat。 master每次接收到写命令之后,现在内部写入数据,然后异步发送给slave node
offset
master会在自身不断累加offset,slave也会在自身不断累加offset
slave每秒都会上报自己的offset给master,同时master也会保存每个slave的offset。主要是master和slave都要知道各自的数据的offset,才能知道互相之间的数据不一致的情况
backlog
master node有一个backlog,默认是1MB大小;master node给slave node复制数据时,也会将数据在backlog中同步写一份;主要是用来做全量复制中断候的增量复制的
master run id
info server,可以看到master run id
如果根据host+ip定位master node,是不靠谱的,如果master node重启或者数据出现了变化,那么slave node应该根据不同的run id区分,run id不同就做全量复制
如果需要不更改run id重启redis,可以使用redis-cli debug reload命令
psync
从节点使用psync从master node进行复制,psync runid offset
master node会根据自身的情况返回响应信息,可能是FULLRESYNC runid offset触发全量复制,可能是CONTINUE触发增量复制
mongodb
MongoDB 是一个高性能,开源,无模式的文档型数据库,开发语言是C++。它在许多场景下可用于替代传统的关系型数据库或键/值存储方式
数据格式:
在 MongoDB 中,文档是对数据的抽象,它的表现形式就是我们常说的 BSON(Binary JSON ),数据结构上json是按字符串存储,bson是按结构存储
BSON有JSON有什么区别?
BSON主要实现下面三点:
1、更快的遍历速度
对json格式来说,太大的json结构会导致数据遍历非常慢。在json中,要跳过一个文档进行数据读取,需要对此文档进行扫描才行,需要进行麻烦的数据结构匹配,比如括号的匹配。
而bson对json的一大改进就是,它会将json的每一个元素的长度存在元素的头部,这样你只需要读取到元素长度就能直接seek到指定的点上进行读取了。
2、操作更简易
对json来说,数据存储是无类型的,比如你要修改基本一个值,从9到10,由于从一个字符变成了两个,所以可能其后面的所有内容都需要往后移一位才可以。
而使用bson,你可以指定这个列为数字列,那么无论数字从9长到10还是100,我们都只是在存储数字的那一位上进行修改,不会导致数据总长变大。
当然,在mongoDB中,如果数字从整形增大到长整型,还是会导致数据总长变大的。
3、增加了额外的数据类型
json是一个很方便的数据交换格式,但是其类型比较有限。
bson在其基础上增加了“byte array”数据类型。这使得二进制的存储不再需要先base64转换后再存成json,大大减少了计算开销和数据大小
redis和mongodb 的区别?
Redis主要把数据存储在内存中,其“缓存”的性质远大于其“数据存储“的性质,其中数据的增删改查也只是像变量操作一样简单;
MongoDB却是一个“存储数据”的系统,增删改查可以添加很多条件,就像SQL数据库一样灵活
MongoDB的特点是?
1、面向文档
2、高性能
3、高可用
4、易扩展
5、丰富的查询语言
mongoDB适合大数据量的存储,吃内存也比较厉害,服务不要和别的服务在一起
mongodb高可用?
mongoDB支持master-slave,replicaset(内部采用paxos选举算法,自动故障恢复),auto sharding机制,对客户端屏蔽了故障转移和切分机制,也就是通过副本集以及分片实现高可用
mongodb持久化?
MongoDB从1.8版本开始采用binlog方式支持持久化的可靠性
mongodb的所有数据实际上是存放在硬盘的,所有要操作的数据通过mmap的方式映射到内存某个区域内。
然后,mongodb就在这块区域里面进行数据修改,避免了零碎的硬盘操作。
至于mmap上的内容flush到硬盘就是操作系统的事情了,所以,如果,mongodb在内存中修改了数据后,mmap数据flush到硬盘之前,系统宕机了,数据就会丢失。
mongodb的内存管理机制?
Redis 数据全部存在内存,定期写入磁盘,当内存不够时,可以选择指定的 LRU 算法删除数据。
MongoDB 数据存在内存,由 linux系统 mmap 实现,当内存不够时,只将热点数据放入内存,其他数据存在磁盘。
mongodb 会将热点数据存储在内存中,这也是它需要大量内存的原因。所以你的同一条查询语句第二次会比第一次快很多
mongodb与redis适用的应用场景?
redis:数据量较小的更性能操作和运算上
MongoDB:主要解决海量数据的访问效率问题
1 、实时的CRU操作,如网站、论坛等实时数据存储
2、高伸缩性,可以分布式集群,动态增删节点
3、存储大尺寸、低价值数据
4、 缓存
5、BSON结构对象存储
6、表结构不明确且数据不断变大
MongoDB是非结构化文档数据库,扩展字段很容易且不会影响原有数据。内容管理或者博客平台等,例如圈子系统,存储用户评论之类的。
7、更高的写入负载
MongoDB侧重高数据写入的性能,而非事务安全,适合业务系统中有大量“低价值”数据的场景。本身存的就是json格式数据。例如做日志系统。
8、数据量很大或者将来会变得很大
Mysql单表数据量达到5-10G时会出现明细的性能降级,需要做数据的水平和垂直拆分、库的拆分完成扩展,MongoDB内建了sharding、很多数据分片的特性,容易水平扩展,比较好的适应大数据量增长的需求
9、mongoDB内置了数据分析的功能(mapreduce),其他不支持
mongodb不适用的应用场景?
1、高度事务性操作,如银行或会计系统
2、传统商业智能应用,如提供高度优化的查询方式
3、需要SQL的问题
4、重要数据,关系型数据
5、MongoDB不支持事务操作(不过mongodb 4.0已经支持副本集级别的事务了),MongoDB目前不支持join操作,复杂查询的应用也不建议使用MongoDB。
MongoDB的缺点?
1、MongoDB不支持事务操作
所以事务要求严格的系统,比如银行系统就不能用它。
2、MongoDB占用空间过大
2.1、空间的预分配:
当MongoDB的空间不足时它就会申请生成一大块硬盘空间,而且申请的量都是有64M、128M、256M来增加直到2G为单个文件的较大体积,并且随着数量叠增,可以在数据目录下看到整块生成而且不断递增的文件。
2.2、删除记录不释放空间:
这很容易理解,为避免记录删除后的数据的大规模挪动,原记录空间不删除,只标记“已删除”即可,以后还可以重复利用。
3、(开发和IT运营要注意)MongoDB没有MySQL那样成熟的维护工具
mongodb的优势?
为什么MongoDB的数据文件很大?
MongoDB采用的预分配空间的方式来防止文件碎片。
MongoDB在A:{B,C}上建立索引,查询A:{B,C}和A:{C,B}都会使用索引吗?
不会,只会在A:{B,C}上使用索引。
数据一致性(事务支持)?
redis事务支持比较弱,只能保证事务中的每个操作连续执行
mongoDB不支持事务,新版本支持,也不支持join、复杂查询
关系型数据库和非关系型数据库的应用场景对比
关系型数据库适合存储结构化数据,如用户的帐号、地址:
1、这些数据通常需要做结构化查询,比如join,这时候,关系型数据库就要胜出一筹
2、这些数据的规模、增长的速度通常是可以预期的
3、事务性、一致性
NoSQL适合存储非结构化数据,如文章、评论:
1、这些数据通常用于模糊处理,如全文搜索、机器学习
2、这些数据是海量的,而且增长的速度是难以预期的,
3、根据数据的特点,NoSQL数据库通常具有无限(至少接近)伸缩性
4、按key获取数据效率很高,但是对join或其他结构化查询的支持就比较差
Memcached
redis与memcached区别?
Redis更多场景是作为Memcached的替代者来使用
redis与Memcached仅支持简单的key-value结构的数据记录不同,Redis支持的数据类型要丰富得多。Memcached基本只支持简单的key-value存储,不支持枚举,不支持持久化和复制等功能。Redis支持服务器端的数据操作相比Memcached来说,拥有更多的数据结构和并支持更丰富的数据操作,支持list、set、sorted set、hash等众多数据结构,还同时提供了持久化和复制等功能。
redis的应用场景更丰富一些
1、redis有数据持久化机制(持久化机制有两种:1、定期将内存数据dump到磁盘;2、aof(append only file)持久化机制–用记日志的方式记录每一条数据更新操作,一旦出现灾难事件,可以通过日志重放来恢复整个数据库)
2、redis支持集群模式(容量可以线程扩展),也有redis-trib 工具
3、redis相比其他缓存工具(ehcach/memcached),有一个鲜明的优势,支持丰富的数据结构
4、Memcached本身并不支持分布式,因此只能在客户端通过像一致性哈希这样的分布式算法来实现Memcached的分布式存储。相较于Memcached只能采用客户端实现分布式存储,Redis更偏向于在服务器端构建分布式存储。
5、redis支持队列与订阅