一、基本知识
1.1 redis有几种基本类型?
redis是key-value类型数据库,key都是字符串类型,此处的基本类型指的是value的类型。
常见5种:
String:结合Java中的对应的类来进行理解,其中String数据结构对应Object类 (任意对象都会序列化成string来存储)。
String类型的Value最多可以容纳的数据长度是512M。
相对于其他的几种数据结构,只有String类型的命令在写入key的时候可以带有默认的过期时间;对于其他的数据结构,key默认是不过期的,如果需要设置过期时间,必须显示调用expire函数设置过期时间;
List:对应java.util.List接口的实现类java.util.LinkedList;在头和尾插入/删除,非常高效;在中间插入删除则很低效;
Set:无序集合,提供交集、差集、并集等操作;对应java.util.Set接口;
SortedSet: 对应java.util.SortedSet接口
hash:键值对集合, value形式=[{field1,value1},...{fieldN,valueN}],键值对适合存储对象;对应java.util.HashMap类;
新增4种:bitmap位图、HyperLogLog、geo、stream
参考: https://www.cnblogs.com/chenpingzhao/p/6965164.html
1.2 每种类型的应用场景
String:常规计数(阅读量、点赞数、库存量等);分布式锁(set命令的nx参数实现 key不存在时插入);共享session(分布式系统中判断用户是否登录,session存到redis,不同服务器调用redis校验)
List:消息队列(保序、可靠、重复)
hash:购物车(key:用户ID, value:{商品ID:商品数量});存储用户信息,只取出某个值修改,比放String(一个大json)方便;对于海量数据的情况,可以自己对数据进行分桶,然后使用Hash结构来存储。对于很多value为简单的字符串,采用hash存储更节省空间。
Set: 跟踪唯一性的数据,如ip计数、点赞,共同好友,共同关注,抽奖
sort set: 排行榜(游戏积分排名,销量排名,成绩排名)
- 可以用于一个大型在线游戏的积分排行榜。每当玩家的分数发生变化时,可以执行ZADD命令更新玩家的分数,此后再通过ZRANGE命令获取积分TOP TEN的用户信息。当然也可以利用ZRANK命令通过username来获取玩家的排行信息。最后将组合使用ZRANGE和ZRANK命令快速的获取和某个玩家积分相近的其他用户的信息。
- SortedSet类型还可用于构建索引数据。
- 建立一个SortedSet中元素个数不要超过 1 W。
- 延迟消息队列(点外卖,商家超过10分钟没接单,自动取消订单)
bitmap: 适合取值只有0、1的场景,比如签到
HyperLogLog:大数据量下不精确的计数(百万网友的UV数)
geo: 地理信息,滴滴叫车
参考:https://www.cnblogs.com/xiaolincoding/p/16370783.html
1.3 数据结构
1.3.1 有序集合-跳表 :链表加多级索引的结构
针对有序单链表;redis中的有序集合sorted set使用跳表实现。
如果每2个节点抽一个到上级,会多一倍的空间
通过调节抽取节点的间隔,控制索引节点占用的存储空间,以此来达到空间复杂度 和 时间复杂度的平衡
作为一种动态数据结构,为了避免性能下降,我们需要在数据插入,删除的过程中,动态地更新跳表的索引结构。 当向跳表中插入数据时,我们选择同时将这个数据插入到部分索引层中。 如何决定插入到哪些索引层中呢? 通过一个随机函数来决定,比如通过 随机函数得到某个值 K, 那么就将这个节点插入第1级到第k级的索引中
为什么Redis中的有序集合用跳表而非红黑树来实现呢?
- 1.对于插入,删除,查找 以及 输出有序序列 这几个操作,红黑树也可以完成,时间复杂度 与 用跳表实现是相同的。 但是,对于按照区间查找数据这个操作(比如 [20,300]), 红黑树的效率没有跳表高。,跳表可以做到 O(logn)的时间复杂度定位区间的起点,然后在原始链表中顺序向后遍历输出,直到遇到值大于区间终点的节点为止。
- 2.跳表更加灵活,通过改变节点的抽取间隔,灵活的平衡空间复杂度和时间复杂度
- 3.相比红黑树,跳表更容易实现,代码更简单
hash类型:
实现方式ziplist(数据少,连续空间)、hashtable(数据大)
list类型:
实现方式ziplist(数据少)、quicklist(数据大)
set类型:
实现方式intset(数据少,有序数组)、hashtable(数据大,基于dict字典失效)
zset类型:
实现方式ziplist(数据少)、skiplist(数据大)
1.4 缓存雪崩 vs 缓存穿透vs 缓存击穿
缓存雪崩:由于大量的key在同一时间失效,导致流量直接打到数据库,导致数据库挂了
解决方案:
- 将key的过期时间设置随机值,避免同一时间过期
- 互斥锁,并发量不多的时候可以采用加锁排队
- 定时任务更新缓存,定时检查缓存是否过期
如果是redis宕机- 服务熔断、限流
- 使用高可用集群
缓存击穿:就是大量用户访问热点key失效,导致流量直接打到数据库,导致数据库挂了
解决方案:
- 设置热点数据永不过期
- 加互斥锁(setNX,保证同一时间只有一个业务线程请求缓存)
缓存穿透:就是用户频繁使用缓存和数据库中不存在的数据进行访问,导致流量直接打到数据库,导致数据库挂了
解决方案:
• 接口层增加校验
• 如果缓存中不存在该值,就缓存空值到缓存中
• 使用布隆过滤器,布隆过滤器是一个位图,如果它说不存在就一定不存在,如果说存在只能是可能存在,可以将可能存在的key放入bitmap进行过滤
1.5 缓存过期
将key+过期时间 --存入-->过期字典中,查找---》过期字典有该key && 系统时间>过期时间 -》key过期
删除策略: 惰性删除(访问key时过期才删除)+ 定时删除(定时抽一定数量的key,删除其中过期的;时间没超上限 && 比例>25%,循环抽、删)
持久化时如何处理过期key?
- • RDB:
- • 文件生成阶段:过期key不写入RDB文件;
- • 文件加载阶段:主服务器对key进行检查,过期key不写入;从服务器全加载;
- • AOF:
- • 文件写入阶段:过期key正常写入, 等key被删除时,再往文件追加删除命令
- • 文件重写阶段:过期key不再写入
主从模式下,从服务器不处理过期key,同步主库删除命令,再进行删除key
1.6 缓存淘汰
redis内存满了,会触发内存淘汰机制,支持的淘汰机制如下:
不淘汰:内存满了,再存入就报错
从所有key里挑淘汰:
- 随机淘汰
- 最久未使用(对象头记录最近一次访问时间戳)
- 最少使用(对象头记录最近一次访问时间戳+访问次数)
设置了过期时间的key里挑选:
- 淘汰最少使用
- 随机淘汰
- 淘汰最久未使用
- 淘汰最快要过期的key
1.7 缓存&DB数据一致性
Cache Aside(旁路缓存策略):先更新DB,再删除缓存。会使缓存命中率降低。
如果要求高命中率,可使用先更新DB、再更新缓存。但这样会出现数据不一致,为保证数据一致性,可①更新缓存时加上分布式锁(保证没有并发问题),锁会降低写入性能;
或②给缓存加上较短的过期时间。
延时双删:
1 删除缓存
2 更新DB
3 sleep(设置时间)
4 删除缓存
1.8 处理热点key
如何发现热点key?
1.客户端统计
2.根据业务,预测热点key
3.redis集群如果有代理层,可由代理层收集
4.使用redis提供的命令进行扫描发现,会影响性能
5.抓包
如何处理
1.避免流量全打到一台机器上,对热点key进行分片。将流量分散
2.使用多级缓存,本地缓存热点key,避免请求redis
3.
参考文档:一文了解如何发现并解决Redis热key与大key问题-腾讯云开发者社区-腾讯云
https://www.cnblogs.com/rjzheng/p/10874537.html
二、高可用
单机、主从复制、哨兵、Redis Cluster
2.1 单机模型和主从复制
单机模型 主从复制模型图
工作机制:
- slave启动后,向master发送SYNC命令,master接收到SYNC命令后通过bgsave保存快照(RDB持久化),并使用缓冲区记录保存快照这段时 间内执行的写命令
- master将保存的快照文件发送给slave,并继续记录执行的写命令
- slave接收到快照文件后,加载快照文件,载入数据
- master快照发送完后开始向slave发送缓冲区的写命令,slave接收命令并执行,完成复制初始化
- 此后master每次执行一个写命令都会发送给slave,保持master与slave之间数据的一致性
一致性保持:
一旦主节点不可达
由于一主多从会导致主节点压力过大(主从复制风暴),所以可以考虑 主-从-从 形式将压力分摊出去
主从复制风暴 就是 主从复制过程中,由于某种原因导致从节点重新连接到主节点时,大量的数据需要被同步,从而导致网络拥塞和性能下降的情况。
总结:
单机 | 主从 | |
优点 | 1:架构简单,部署方便 2:高性价比 缓存使用时无需备用节点,当然为了满足业务的高可用性,也可以牺牲一个备用节点,但同时刻只有一个实例对外提供服务; 3:高性能 | 1. master能自动将数据同步到slave,可以进行读写分离,分担master的读压力 2. master、slave之间的同步是以非阻塞的方式进行的,同步期间,客户端仍然可以提交查询或更新请求 |
缺点 | 1,不保证数据的可靠性; 2,在缓存使用,进程重启后,数据丢失,即使有备用的节点解决高可用性,但是仍然不能解决缓存预热问题,因此不适用于数据可靠性要求高的业务; 3,高性能受限于单核 CPU 的处理能力(Redis 是单线程机制),CPU 为主要瓶颈,所以适合操作命令简单,排序、计算较少的场景。 | 1. 不具备自动容错与恢复功能,master或slave的宕机都可能导致客户端请求失败,需要等待机器重启或手动切换客户端IP才能恢复 |
2.2 哨兵
2.2.1 工作原理
哨兵主要功能:
1. 监控master、slave是否正常运行
2. 当master出现故障时,能自动将一个slave转换为master
3. 多个哨兵可以监控同一个Redis,哨兵之间也会自动监控
哨兵模式的具体工作机制:
1、在哨兵配置文件中通过sentinel monitor <master-name> <ip> <redis-port> <quorum> 来定位master的IP、端口,一个哨兵可以监控多个master数据库,只需要提供多个该配置项即可。
哨兵启动后,会与要监控的master建立两条连接:
- 一条连接定期向master发送INFO等命令获取master本身的信息
- 一条连接用来订阅master的_sentinel_:hello 频道,与获取其他监控该master的哨兵节点信息(发布/订阅)
2、与master建立连接后,哨兵会执行三个操作:
- 定期(一般10s一次,当master被标记为主观下线时,改为1s一次)向master和slave发送INFO命令(发现slave:INFO命令可以获取当前redi s的相关信息从而实现新节点的自动发现。所以说哨兵只需要配置master数据库信息就可以自动发现其slave信息)
- 定期向master和slave的_sentinel_:hello 频道发送自己的信息(与其他哨兵通信:发现新哨兵,哨兵间共享信息)
示意图:
3、定期(1s一次)向master、slave和其他哨兵发送PING命令(当实现了自动发现slave和其他哨兵节点后,哨兵就可以通过定期发送PING命令 定时监控这些数据库和节点有没有停止服务)
2.2.2 如何进行自动容错和恢复?
正常情况下,客户端访问集群的逻辑如下:
问:Client连接的是master还是哨兵?
client先连接哨兵(遍历哨兵集合,取出一个可用的哨兵),从获取master节点信息后,客户端直接连master执行操作;
当master更换时,哨兵通知client,client更新master的IP,建立新连接。
哨兵节点本质上是Redis节点。
每个哨兵节点,只需要配置监控主节点,便可以自动发现其他的哨兵节点和从节点。
在哨兵节点启动和故障转移阶段,各个节点的配置文件会被重写
哨兵只会对master进行客观下线&进行后续的故障转移
2.2.3 思考
当一个哨兵A向一个master 发送 PING 命令没有收到回复的时候
1、是否认为这个master故障了,需要下线?
2、多个slave怎么选出一个成为新master?
3、既然哨兵可以自动故障修复,哪个哨兵进行修复
4,哨兵和从节点的区别?
5,客户端配置的地址是谁的地址?
怎么修复
1、哨兵A会向其它哨兵发送命令询问它们是否也认为该master主观下线,(down-after-milliseconds 配置时间 若master重新向sentinel进程 发送的PING命令返回有效回复,master的主观下线状态也会被移除。) 2、如果达到一定数目(即配置文件中的quorum)投票,哨兵们会认为该master已经客观下线,(没有足够的sentinel进程同意master下线,mas ter的客观下线状态会被移除) 3、选举领头的哨兵节点对主从系统发起故障恢复。 |
怎么选领头哨兵
1. 发现master下线的哨兵A 向每个哨兵发送命令,要求对方选自己为领头哨兵 2. 如果目标哨兵节点没有选过其他人,则会同意选举A为领头哨兵 3. 如果有超过一半的哨兵同意选举A为领头,则A当选 4. 如果有多个哨兵节点同时参选领头,此时有可能存在一轮投票无竞选者胜出,此时每个参选的节点等待一个随机时间后再次发起参选请求,进行下一轮投票竞选,直至选举出领头哨兵 选举领头的哨兵节点对主从系统发起故障恢复。 |
怎么从slave中选新master
从出现故障的master对应的slave中挑选一个来当选新的master 1. 所有在线的slave中选择优先级最高的,优先级可以通过slave-priority配置 2. 如果有多个最高优先级的slave,则选取复制偏移量最大(即复制越完整)的当选 3. 如果以上条件都一样,选取id最小的slave |
新master挑选出来之后,怎么继任
1、领头哨兵向新master发送命令slaveof no one不再slave别人,使其升为master, 2、再向其他slave发送命令slave新的master 3、最后更新数据 4、将已经停止的旧的master更新为新的master的slave,使其恢复服务后以slave的身份继续运行。 |
平常redis配置,在redis连接池配置jedisPoolConfig中,没有redis的 IP 配置?
客户端在通过哨兵获得主节点信息后,会直接建立到主节点的连接,后续的请求(如set/get)会直接发向主节点
org.springframework.data.redis.connection.jedis.JedisConnectionFactory#createPool:
问:之前的主从模式支持读写分离的,为什么这个哨兵模式就不能支持了?
手动修改读写分离(读写分离需要考虑主从切换,不建议使用)
@Bean(name = "redisTemplateMaster") public RedisTemplate<Object, Object> redisTemplateMaster() { RedisTemplate<Object, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisMasterConnectionFactory()); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new StringRedisSerializer()); return template; } @Bean(name = "redisTemplateSlave") public RedisTemplate<Object, Object> redisTemplateSlave() { RedisTemplate<Object, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisSlaveConnectionFactory()); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new StringRedisSerializer()); return template; } |
3.4 小结
优点 | 1. 哨兵模式基于主从复制模式,所以主从复制模式有的优点,哨兵模式也有 2. 哨兵模式下,master挂掉可以自动进行切换,系统可用性更高 |
缺点 | 1. 同样也继承了主从模式难以在线扩容的缺点,Redis的容量受限于单机配置 2. 需要额外的资源来启动sentinel进程,实现相对复杂一点,同时slave节点作为备份节点不提供服务 3.可能因为网络问题导致无法选主;只有一个主节点无法实现高并发 |
2.3 Redis Cluster
2.3.1 简介
无中心结构,每个节点保存数据和整个集群状态,每个节点都和其他所有节点连接。
Redis 集群引入了哈希槽的概念,有16384(2^7)个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽。集群的每个节点负责一部分hash槽,
eg 现在三个主节点分别是:A, B, C 三个节点,它们可以是一台机器上的三个端口,也可以是三台不同的服务器。那么,采用哈希槽 (hash slot)的方式来分配16384个slot 的话,它们三个节点分别承担的slot 区间是:
- 节点A覆盖0-5460;
- 节点B覆盖5461-10922;
- 节点C覆盖10923-16383.
获取数据:
如果存入一个值,按照redis cluster哈希槽的算法: CRC16('key')384 = 6782。 那么就会把这个key 的存储分配到 B 上了。同样,当我连接(A,B,C)任何一个节点想获取'key'这个key时,也会这样的算法,然后内部跳转到B节点上获取数据
这种结构很容易添加或者删除节点. 比如如果我想新添加个节点D, 我需要从节点 A, B, C中得部分槽到D上. 如果我想移除节点A,需要将A中的槽移到B和C节点上,然后将没有任何槽的A节点从集群中移除即可。Redis提供了迁移命令,借助 redis-trib 工具进行迁移, 由于从一个节点将哈希槽移动到另一个节点并不会停止服务,所以无论添加删除或者改变某个节点的哈希槽的数量都不会造成集群不可用的状态。
同样删除一个节点也是类似,移动完成后就可以删除这个节点了。
2.3.2 Redis Cluster主从模式
redis cluster 为了保证数据的高可用性,加入了主从模式,一个主节点对应一个或多个从节点,主节点提供数据存取,从节点则是从主节点拉取数据备份,当这个主节点挂掉后,就会有这个从节点选取一个来充当主节点,从而保证集群不会挂掉。
Cluster模式集群节点最小配置6个节点(3主3从),业务配置15主-45从
上面那个例子里, 集群有ABC三个主节点, 如果这3个节点都没有加入从节点,如果B挂掉了,我们就无法访问整个集群了。A和C的slot也无法访问。
所以我们在集群建立的时候,一定要为每个主节点都添加了从节点, 比如像这样, 集群包含主节点A、B、C, 以及从节点A1、B1、C1, 那么即使B挂掉系统也可以继续正确工作。
B1节点替代了B节点,所以Redis集群将会选择B1节点作为新的主节点,集群将会继续正确地提供服务。 当B重新开启后,它就会变成B1的从节点。
不过需要注意,如果节点B和B1同时挂了,Redis集群就无法继续正确地提供服务了。
事务支持
Keys hash tags标签是破坏上述计算规则的实现,Hash tag是一种保证多个键被分配到同一个槽位的方法。 hashtag的计算规则是:取一对大括号{}之间的字符进行计算,如果key存在多对大括号,那么就取第一个左括号和第一个右括号之间的字符。如果大括号之间没有字符,则会对整个字符串进行计算。 1. {Jackeyzhe}.following和{Jackeyzhe}.follower这两个key都是计算Jackey的hash值2. foo{{bar}}这个key就会对{bar进行hash计算3. follow{}{Jackey}会对整个字符串进行计算 |
2.3.3 故障转移
自动 Failover
当一个 slave 发现自己正在复制的 master 进入了已下线(FAIL)状态时,slave 将开始对已下线状态的 master 进行故障转移,以下是故障转移执行的步骤
- 该下线的 master 下所有 slave 中,会有一个 slave 被选中。具体的选举流程为:slave 自增它的 currentEpoch 值,然后向其他 masters 请求投票,每个 slave 都向集群其他节点广播一条 CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST 消息用于拉票,集群中具有投票权的 master 收到消息后,如果在当前选举纪元中没有投过票,就会向第一个发送来消息的 slave 返回 CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK 消息,表示投票给该 slave。某个 slave 如果在一段时间内收到了大部分(过半) master 的投票,则表示选举成功。
- 被选中的 slave 会执行 SLAVEOF no one 命令,成为新的 master
- 新的 master 会撤销所有对已下线 master 的 slot 指派,并将这些 slot 全部指派给自己
- 新的 master 向集群广播一条 PONG 消息,这条 PONG 消息可以让集群中的其他节点立即知道自己已经由 slave 变成了 master ,并且这个 master 已经接管了原本由已下线节点负责处理的 slot
- 新的 master 开始接收和自己负责处理的 slot 有关的命令请求,故障转移完成
手动 Failover
Redis 集群支持手动故障转移,也就是向 slave 发送 CLUSTER FAILOVER 命令,使其在 master 未下线的情况下,发起故障转移流程,升级为新的 master ,而原来的 master 降级为 slave。
为了不丢失数据,向 slave 发送 CLUSTER FAILOVER 命令后,流程如下:
- slave 收到命令后,向 master 发送 CLUSTERMSG_TYPE_MFSTART 命令
- master 收到该命令后,会将其所有客户端置于阻塞状态,也就是在 10s 的时间内,不再处理客户端发来的命令,并且在其发送的心跳包中,会带有 CLUSTERMSG_FLAG0_PAUSED 标记
- slave 收到 master 发来的,带 CLUSTERMSG_FLAG0_PAUSED 标记的心跳包后,从中获取 master 当前的复制偏移量,slave 等到自己的复制偏移量达到该值后,才会开始执行故障转移流程:发起选举、统计选票、赢得选举、升级为 master 并更新配置
2.3.4 存在的问题
Redis 集群尽可能保证数据的一致性,但在特定条件下会丢失数据,原因有两点:集群是用了异步复制;网络分区造成脑裂问题。
- 异步复制
master 以及对应的 slaves 之间使用异步复制机制,eg:
(1)写命令提交到 master,
(2)master 执行完毕后向客户端返回 OK;
(3)master向Slave同步写操作
但由于复制的延迟此时数据还没传播给 slave;如果此时 master 不可达的时间超过阀值,此时集群将触发 failover,将对应的 slave 选举为新的master,此时由于该 slave 没有收到复制流,因此没有同步到 slave 的数据将丢失;
为什么采用异步复制? ——同步复制,耗时久影响性能;一种性能和一致性的权衡。后续可能会提供同步写
- 脑裂(split-brain)
Redis 集群另外一种可能会丢失命令的情况是集群出现了网络分区, 并且一个客户端与至少包括一个主节点在内的少数实例被孤立。
eg 假设集群包含 A 、 B 、 C 、 A1 、 B1 、 C1 六个节点, 其中 A 、B 、C 为主节点, A1 、B1 、C1 为A,B,C的从节点, 还有一个客户端 Z1 假设集群中发生网络分区,那么集群可能会分为两方,大部分的一方包含节点 A 、C 、A1 、B1 和 C1 ,小部分的一方则包含节点 B 和客户端 Z1 .
Z1仍然能够向主节点B中写入, 如果网络分区发生时间较短,那么集群将会继续正常运作,如果分区的时间足够让大部分的一方将B1选举为新的master,那么Z1写入B中得数据便丢失了.
注意, 在网络分裂出现期间, 客户端 Z1 可以向主节点 B 发送写命令的最大时间是有限制的, 这一时间限制称为节点超时时间(node timeout), 是 Redis 集群的一个重要的配置选项:
- 大多数master宕机
少数的宕机,集群可以依赖自身机制切换主从,对外提供服务;大多数master宕机时,集群就不可用了。
优点 | 无中心架构,数据按照slot分布在多个节点。 集群中的每个节点都是平等的关系,每个节点都保存各自的数据和整个集群的状态。每个节点都和其他所有节点连接,而且这些连接保持活跃,这样就保证了我们只需要连接集群中的任意一个节点,就可以获取到其他节点的数据。 可线性扩展到1000多个节点,节点可动态添加或删除能够实现自动故障转移,节点之间通过gossip协议交换状态信息,用投票机制完成slave到master的角色转换 |
缺点 | 客户端实现复杂,驱动要求实现Smart Client,缓存slots mapping信息并及时更新,提高了开发难度。目前仅JedisCluster相对成熟,异常处理还不完善,比如常见的“max redirect exception” 节点会因为某些原因发生阻塞(阻塞时间大于 cluster-node-timeout)被判断下线,这种failover是没有必要的 数据通过异步复制,不保证数据的强一致性 slave充当“冷备”,不能缓解读压力 批量操作限制,目前只支持具有相同slot值的key执行批量操作,对mset、mget、sunion等操作支持不友好 key事务操作支持有线,只支持多key在同一节点的事务操作,多key分布不同节点时无法使用事务功能 不支持多数据库空间,单机redis可以支持16个db,集群模式下只能使用一个,即db 0 |
为什么是2^14个槽位?
1.如果槽位为65536,发送心跳信息的消息头达8k,发送的心跳包过于庞大。 在消息头中,最占空间的是myslots[CLUSTER_SLOTS/8]。 当槽位为65536时,这块的大小是: 65536÷8÷1024=8kb。 因为每秒钟,redis节点需要发送一定数量的ping消息作为心跳包,如果槽位为65536,这个ping消息的消息头太大了,浪费带宽。 2.redis的集群主节点数量基本不可能超过1000个。 集群节点越多,心跳包的消息体内携带的数据越多。如果节点过1000个,也会导致网络拥堵。因此redis作者,不建议redis cluster节点数量超过1000个。 那么,对于节点数在1000以内的redis cluster集群,16384个槽位够用了。没有必要拓展到65536个。 超过1000个会怎样? 3.槽位越小,节点少的情况下,压缩率高 Redis主节点的配置信息中,它所负责的哈希槽是通过一张bitmap的形式来保存的,在传输过程中,会对bitmap进行压缩,但是如果bitmap的填充率 slots / N很高的话(N表示节点数),bitmap的压缩率就很低。 如果节点数很少,而哈希槽数量很多的话,bitmap的压缩率就很低。而16384÷8÷1024=2kb,也就是说使用2k的空间创建了16k的槽数 |
补充
1 持久化使用情况
为了避免Redis节点宕机,内存数据丢失的问题,Redis提供了包含全部命令的AOF和基于快照的RDB两种方式;在生产环境上,默认是关闭持久化的,但也可以同时开启AOF、RDB;
并且Redis 4.0 开始支持 RDB 和 AOF 的混合持久化;
master-Slave同步机制-几种: (初始化时)全量同步;部分同步;异步复制;
2 虚拟IP,地址漂移
虚拟IP不指定给具体某个网卡,指向正常工作的那个IP-1。当IP-1发生故障时,虚拟IP指向备份的IP,保证外部访问高可用。
其原理是TCP/IP的ARP协议,每台主机都有个ARP高速缓存,存储逻辑地址IP和物理地址MAC的映射关系,数据根据唯一的MAC地址进行物理传输
eg: 一主一从两台机器,假设每台机器只有一块网卡,每个网卡对应一个IP,另外有一个虚拟IP不绑定网卡。
主IP(192.168.1.217)、虚拟IP(192.168.1.219)对应同一个MAC地址,访问217、219都能到达同一台机器
(192.168.1.219) at 00:21:5A:DB:68:E8 [ether] on bond0
(192.168.1.217) at 00:21:5A:DB:68:E8 [ether] on bond0
(192.168.1.218) at 00:21:5A:DB:7F:C2 [ether] on bond0
如果217机器挂了(心跳检测不到)被218监控到,218会更新地址映射关系,并发送ARP包广播通知其他机器,219地址现在绑定的是7F地址的机器
(192.168.1.219) at 00:21:5A:DB:7F:C2 [ether] on bond0
(192.168.1.217) at 00:21:5A:DB:68:E8 [ether] on bond0
(192.168.1.218) at 00:21:5A:DB:7F:C2 [ether] on bond0
Squirrel是在集群的基础上演进的,有什么特点,
Squirrel 使用自研的 Squirrel migrate 进行数据迁移和分片 rebalance。
3 分布式系统CAP原则
CAP原则又称CAP定理,指的是在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)。 CAP原则的精髓就是要么AP,要么CP,要么AC,但是不存在CAP。
一致性(C):数据在多个副本间保持严格的一致性。在分布式系统中的所有数据备份,在同一时刻是否同样的值,即写操作之后的读操作,必须返回该值。(分为弱一致性、强一致性和最终一致性) ,意思是,写操作之后的读操作,必须返回该值
可用性(A):每次请求都能获取到非错的响应——但是不保证获取的数据为最新数据 ,
分区容错性(P):分布式系统在遇到任何网络分区故障的时候,仍然能够对外提供满足一致性和可用性的服务,除非整个网络环境都发生了故障。大多数分布式系统都分布在多个子网络。每个子网络就叫做一个区(partition)。
组 合 | 分析结果 |
CA | 满足原子和可用,放弃分区容错。说白了,就是一个整体的应用。 |
CP | 满足原子和分区容错,也就是说,要放弃可用。当系统被分区,为了保证原子性,必须放弃可用性,让服务停用。 |
AP | 满足可用性和分区容错,当出现分区,同时为了保证可用性,必须让节点继续对外服务,这样必然导致失去原子性。 |
与数据库的关系
传统的关系型数据库(CA)在功能支持上通常很宽泛,从简单的键值查询,到复杂的多表联合查询再到事务机制的支持。而与之不同的是,NoSQL系统通常注重性能和扩展性,而非事务机制(事务就是强一致性的体现)。
传统的SQL数据库的事务通常都是支持ACID的强事务机制。A代表原子性,即在事务中执行多个操作是原子性的,要么事务中的操作全部执行,要么一个都不执行;C代表一致性,即保证进行事务的过程中整个数据库的状态是一致的,不会出现数据花掉的情况;I代表隔离性,即两个事务不会相互影响,覆盖彼此数据等;D表示持久化,即事务一旦完成,那么数据应该是被写到安全的,持久化存储的设备上(比如磁盘)。
NoSQL系统仅提供对行级别的原子性保证,也就是说同时对同一个Key下的数据进行的两个操作,在实际执行的时候是会串行的执行,保证了每一个Key-Value对不会被破坏。
推荐资料: