redis 集群 2:分而治之 —— Codis

在大数据高并发场景下,单个 Redis 实例往往会显得捉襟见肘。首先体现在内存上,单个 Redis 的内存不宜过大,内存太大会导致 rdb 文件过大,进一步导致主从同步时全量同步时间过长,在实例重启恢复时也会消耗很长的数据加载时间,特别是在云环境下,单个实例内存往往都是受限的。其次体现在 CPU 的利用率上,单个 Redis 实例只能利用单个核心,这单个核心要完成海量数据的存取和管理工作压力会非常大。

正是在这样的大数据高并发的需求之下,Redis 集群方案应运而生。它可以将众多小内存的 Redis 实例综合起来,将分布在多台机器上的众多 CPU 核心的计算能力聚集到一起,完成海量数据存储和高并发读写操作。

img
img

Codis 是 Redis 集群方案之一,令我们感到骄傲的是,它是中国人开发并开源的,来自前豌豆荚中间件团队。绝大多数国内的开源项目都不怎么靠谱,但是 Codis 非常靠谱。有了 Codis 技术积累之后,项目「突头人」刘奇又开发出来中国人自己的开源分布式数据库 —— TiDB,可以说 6 到飞起。👍

从 Redis 的广泛流行到 RedisCluster 的广泛使用之间相隔了好多年,Codis 就是在这样的市场空缺的机遇下发展出来的。大型公司有明确的 Redis 在线扩容需求,但是市面上没有特别好的中间件可以做到这一点。

img
img

Codis 使用 Go 语言开发,它是一个代理中间件,它和 Redis 一样也使用 Redis 协议对外提供服务,当客户端向 Codis 发送指令时,Codis 负责将指令转发到后面的 Redis 实例来执行,并将返回结果再转回给客户端。

Codis 上挂接的所有 Redis 实例构成一个 Redis 集群,当集群空间不足时,可以通过动态增加 Redis 实例来实现扩容需求。

客户端操纵 Codis 同操纵 Redis 几乎没有区别,还是可以使用相同的客户端 SDK,不需要任何变化。

因为 Codis 是无状态的,它只是一个转发代理中间件,这意味着我们可以启动多个 Codis 实例,供客户端使用,每个 Codis 节点都是对等的。因为单个 Codis 代理能支撑的 QPS 比较有限,通过启动多个 Codis 代理可以显著增加整体的 QPS 需求,还能起到容灾功能,挂掉一个 Codis 代理没关系,还有很多 Codis 代理可以继续服务。

img
img

Codis 分片原理

Codis 要负责将特定的 key 转发到特定的 Redis 实例,那么这种对应关系 Codis 是如何管理的呢?

Codis 将所有的 key 默认划分为 1024 个槽位(slot),它首先对客户端传过来的 key 进行 crc32 运算计算哈希值,再将 hash 后的整数值对 1024 这个整数进行取模得到一个余数,这个余数就是对应 key 的槽位。

img
img

每个槽位都会唯一映射到后面的多个 Redis 实例之一,Codis 会在内存维护槽位和 Redis 实例的映射关系。这样有了上面 key 对应的槽位,那么它应该转发到哪个 Redis 实例就很明确了。

py复制代码hash = crc32(command.key)
slot_index = hash % 1024
redis = slots[slot_index].redis
redis.do(command)

槽位数量默认是1024,它是可以配置的,如果集群节点比较多,建议将这个数值配置大一些,比如2048、4096。

不同的 Codis 实例之间槽位关系如何同步?

如果 Codis 的槽位映射关系只存储在内存里,那么不同的 Codis 实例之间的槽位关系就无法得到同步。所以 Codis 还需要一个分布式配置存储数据库专门用来持久化槽位关系。Codis 开始使用 ZooKeeper,后来连 etcd 也一块支持了。

img
img

Codis 将槽位关系存储在 zk 中,并且提供了一个 Dashboard 可以用来观察和修改槽位关系,当槽位关系变化时,Codis Proxy 会监听到变化并重新同步槽位关系,从而实现多个 Codis Proxy 之间共享相同的槽位关系配置。

扩容

刚开始 Codis 后端只有一个 Redis 实例,1024 个槽位全部指向同一个 Redis。然后一个 Redis 实例内存不够了,所以又加了一个 Redis 实例。这时候需要对槽位关系进行调整,将一半的槽位划分到新的节点。这意味着需要对这一半的槽位对应的所有 key 进行迁移,迁移到新的 Redis 实例。

那 Codis 如何找到槽位对应的所有 key 呢?

Codis 对 Redis 进行了改造,增加了 SLOTSSCAN 指令,可以遍历指定 slot 下所有的 key。Codis 通过 SLOTSSCAN 扫描出待迁移槽位的所有的 key,然后挨个迁移每个 key 到新的 Redis 节点。

在迁移过程中,Codis 还是会接收到新的请求打在当前正在迁移的槽位上,因为当前槽位的数据同时存在于新旧两个槽位中,Codis 如何判断该将请求转发到后面的哪个具体实例呢?

Codis 无法判定迁移过程中的 key 究竟在哪个实例中,所以它采用了另一种完全不同的思路。当 Codis 接收到位于正在迁移槽位中的 key 后,会立即强制对当前的单个 key 进行迁移,迁移完成后,再将请求转发到新的 Redis 实例。

ini复制代码slot_index = crc32(command.key) % 1024
if slot_index in migrating_slots:do_migrate_key(command.key)  # 强制执行迁移redis = slots[slot_index].new_redis
else:redis = slots[slot_index].redis
redis.do(command)

我们知道 Redis 支持的所有 Scan 指令都是无法避免重复的,同样 Codis 自定义的 SLOTSSCAN 也是一样,但是这并不会影响迁移。因为单个 key 被迁移一次后,在旧实例中它就彻底被删除了,也就不可能会再次被扫描出来了。

自动均衡

Redis 新增实例,手工均衡slots太繁琐,所以 Codis 提供了自动均衡功能。自动均衡会在系统比较空闲的时候观察每个 Redis 实例对应的 Slots 数量,如果不平衡,就会自动进行迁移。

Codis 的代价

Codis 给 Redis 带来了扩容的同时,也损失了其它一些特性。因为 Codis 中所有的 key 分散在不同的 Redis 实例中,所以事务就不能再支持了,事务只能在单个 Redis 实例中完成。同样 rename 操作也很危险,它的参数是两个 key,如果这两个 key 在不同的 Redis 实例中,rename 操作是无法正确完成的。Codis 的官方文档中给出了一系列不支持的命令列表。

同样为了支持扩容,单个 key 对应的 value 不宜过大,因为集群的迁移的最小单位是 key,对于一个 hash 结构,它会一次性使用 hgetall 拉取所有的内容,然后使用 hmset 放置到另一个节点。如果 hash 内部的 kv 太多,可能会带来迁移卡顿。官方建议单个集合结构的总字节容量不要超过 1M。如果我们要放置社交关系数据,例如粉丝列表这种,就需要注意了,可以考虑分桶存储,在业务上作折中。

Codis 因为增加了 Proxy 作为中转层,所有在网络开销上要比单个 Redis 大,毕竟数据包多走了一个网络节点,整体在性能上要比单个 Redis 的性能有所下降。但是这部分性能损耗不是太明显,可以通过增加 Proxy 的数量来弥补性能上的不足。

Codis 的集群配置中心使用 zk 来实现,意味着在部署上增加了 zk 运维的代价,不过大部分互联网企业内部都有 zk 集群,可以使用现有的 zk 集群使用即可。

Codis 的优点

Codis 在设计上相比 Redis Cluster 官方集群方案要简单很多,因为它将分布式的问题交给了第三方 zk/etcd 去负责,自己就省去了复杂的分布式一致性代码的编写维护工作。而 Redis Cluster 的内部实现非常复杂,它为了实现去中心化,混合使用了复杂的 Raft 和 Gossip 协议,还有大量的需要调优的配置参数,当集群出现故障时,维护人员往往不知道从何处着手。

MGET 指令的操作过程

img mget 指令用于批量获取多个 key 的值,这些 key 可能会分布在多个 Redis 实例中。Codis 的策略是将 key 按照所分配的实例打散分组,然后依次对每个实例调用 mget 方法,最后将结果汇总为一个,再返回给客户端。

架构变迁

Codis 作为非官方 Redis 集群方案,近几年来它的结构一直在不断变化,一方面当官方的 Redis 有变化的时候它要实时去跟进,另一方面它作为 Redis Cluster 的竞争方案之一,它还得持续提高自己的竞争力,给自己增加更多的官方集群所没有的便捷功能。

比如 Codis 有个特色的地方在于强大的 Dashboard 功能,能够便捷地对 Redis 集群进行管理。这是 Redis 官方所欠缺的。另外 Codis 还开发了一个 Codis-fe(federation 联邦) 工具,可以同时对多个 Codis 集群进行管理。在大型企业,Codis 集群往往会有几十个,有这样一个便捷的联邦工具可以降低不少运维成本。

Codis 的尴尬

Codis 不是 Redis 官方项目,这意味着它的命运会无比曲折,它总是要被官方 Redis 牵着牛鼻子走。当 Redis 官方提供了什么功能它欠缺时,Codis 就会感到恐惧,害怕自己被市场甩掉,所以必须实时保持跟进。

同时因为 Codis 总是要比 Redis 官方慢一拍,Redis 官方提供的最新功能,Codis 往往要等很久才能同步。比如现在 Redis 已经进入到 4.0 阶段,提供了插件化 Redis-Module 支持,目前 Codis 还没有提供解决方案。

现在 Redis-Cluster 在业界已经逐渐流行起来,Codis 能否持续保持竞争力是个问题,我们看到 Codis 在不断的差异化竞争,竞争的方法就体现在工具上,而不是内核,这个和官方的路线真是相反的,官方对工具无暇顾及,只提供基本的工具,其它完全交给第三方去开发。

Codis 的后台管理

后台管理的界面非常友好,使用了最新的 BootStrap 前端框架。比较酷炫的是可以看到实时的 QPS 波动曲线。

img
img

同时还支持服务器集群管理功能,可以增加分组、增加节点、执行自动均衡等指令,还可以直接查看所有 slot 的状态,每个 slot 被分配到哪个 Redis 实例。

本文由 mdnice 多平台发布

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/78429.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ROS添加节点

1 下载项目源码 (1)这里我使用是哔哩哔哩的博主源码机器人工匠王杰 https://github.com/6-robot/wpr_simulation.git (2)建立工作空间 在主目录下载建立如下文件夹 catkin_ws----       ----src (3)…

express学习笔记4 - 热更新以及express-boom

我们每次改动代码的时候都要重启项目,现在我们给项目添加一个热更新 npm install --save-dev nodemon # or using yarn: yarn add nodemon -D 在package.json添加一行代码 "dev": "nodemon ./bin/www" 重启项目 然后随便做改动&#xff…

多线程(JavaEE初阶系列7)

目录 前言: 1.常见的锁策略 1.1乐观锁和悲观锁 1.2轻量级锁和重量级锁 1.3自旋锁和挂起等待锁 1.4互斥锁与读写锁 1.5可重入锁与不可重入锁 1.6公平锁与非公平锁 2.CAS 2.1什么是CAS 2.2自旋锁的实现 2.3原子类 3.synchronized 3.1synchronized的原理以…

K8S系列文章之 内外网如何互相访问

K8S中网络这块主要考虑 如何访问外部网络以及外部如何访问内部网络 访问外网服务的两种方式 需求 k8s集群内的pod需要访问mysql,由于mysql的性质,不适合部署在k8s集群内,故k8s集群内的应用需要链接mysql时,需要配置链接外网的mysql,本次测试…

记一次 .NET某医疗器械清洗系统 卡死分析

一:背景 1. 讲故事 前段时间协助训练营里的一位朋友分析了一个程序卡死的问题,回过头来看这个案例比较经典,这篇稍微整理一下供后来者少踩坑吧。 二:WinDbg 分析 1. 为什么会卡死 因为是窗体程序,理所当然就是看主…

代理模式——对象的间接访问

1、简介 1.1、概述 由于某些原因,客户端不想或不能直接访问某个对象,此时可以通过一个被称为“代理”的第三者来实现间接访问,该方案对应的设计模式被称为代理模式。 代理模式是一种应用很广泛的结构型设计模式,而且变化很多。…

【我们一起60天准备考研算法面试(大全)-第三十六天 36/60】【最短路】

专注 效率 记忆 预习 笔记 复习 做题 欢迎观看我的博客,如有问题交流,欢迎评论区留言,一定尽快回复!(大家可以去看我的专栏,是所有文章的目录)   文章字体风格: 红色文字表示&#…

SpringCloud Gateway 在微服务架构下的最佳实践

作者:徐靖峰(岛风) 前言 本文整理自云原生技术实践营广州站 Meetup 的分享,其中的经验来自于我们团队开发的阿里云 CSB 2.0 这款产品,其基于开源 SpringCloud Gateway 开发,在完全兼容开源用法的前提下&a…

java中的hashmap和concurrenthashmap解析

hashmap的初始化数组大小为16,如果发生哈希冲突的时候在当前的索引后面采用头插法以链表的形式继续插入节点。 concurrenthashmap的结构图如下所示: 本身不是16个节点吗?这里分为两个长度为4的数组,变成了4*4总共16个节点&#x…

决策树与随机森林

目录 决策树是:Why:How:基本概念决策树生成举例决策树缺点参考 Demo 随机森林1.是:2.Why:3.How:参考 Demo 决策树 是: 1.一种有监督的分类(或预测)算法。 2.利用属性、…

网络安全--原型链污染

目录 1.什么是原型链污染 2.原型链三属性 1)prototype 2)constructor 3)__proto__ 4)原型链三属性之间关系 3.JavaScript原型链继承 1)分析 2)总结 3)运行结果 4.原型链污染简单实验 1)实验一 2&#xff0…

使用hutool工具生成树形结构

假设要构建一个菜单,可以实现智慧库房,菜单的样子如下: 智慧库房|- RFID|- 智慧大屏|- 智能密集架|- 环境管控那这种结构如何保存在数据库中呢?一般是这样的: ​ 每条数据根据parentId相互关联并表示层级关系&#x…

Killing LeetCode [83] 删除排序链表中的重复元素

Description 给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。 Intro Ref Link:https://leetcode.cn/problems/remove-duplicates-from-sorted-list/ Difficulty:Easy Tag&am…

云上 Index:看「简墨」如何为云原生打造全新索引

拓数派首款数据计算引擎 PieCloudDB Database 是一款全新的云原生虚拟数仓。为了提升用户使用体验,提高查询效率,在实现存算分离的同时,PieCloudDB 设计与打造了全新的存储引擎「简墨」等模块,并针对云场景和分析型场景设计了高效…

常见的设计模式(超详细)

文章目录 单例模式饿汉式单例模式懒汉式单例模式双重检索单例模式 工厂模式简单工厂模式工厂(方法)模式抽象工厂模式 原型模式代理模式 单例模式 确保一个类只有一个实例,并且自行实例化并向整个系统提供这个实例。 饿汉式单例模式 饿汉式单…

端口快查表 | 介绍及其作用 | IT人必备技能

1.概述: 端口总数:65535,一般用到的是1~65535,0一般不使用。 0-1023:系统端口,也叫公认端口,这些端口只有系统特许的进程才能使用。 1024~65535为用户端口。 1024-5000:临时端口…

C语言一些有趣的冷门知识

文章目录 概要1.访问数组元素的方法运行结果 2.中括号的特殊用法运行结果 3.大括号的特殊用法运行结果 4.sizeof的用法运行结果 5.渐进运算符运行结果 小结 概要 本文章只是介绍一些有趣的C语言知识,纯属娱乐。这里所有的演示代码我是使用的编译器是Visual Studio …

【Docker】Docker+Zipkin+Elasticsearch+Kibana部署分布式链路追踪

文章目录 1. 组件介绍2. 服务整合2.1. 前提:安装好Elaticsearch和Kibana2.2. 再整合Zipkin 点击跳转:Docker安装MySQL、Redis、RabbitMQ、Elasticsearch、Nacos等常见服务全套(质量有保证,内容详情) 本文主要讨论在Ela…

ChatGPT3.5——AI人工智能是个什么玩意?

ChatGPT3.5——AI人工智能 AI人工智能什么是AI?AI有什么过人之处AI有什么缺点 AI的发展AI的发展史中国是如何发展AI的 AI六大要素感知理解推理学习交互 ChatCPT-3.5GPT-3.5的优势在哪里GPT-3.5的风险GPT-4骗人事件 AI人工智能 AI,就像是一位超级聪明的机…

vue diff 前后缀+最长递增子序列算法

文章目录 查找相同前后缀通过前后缀位置信息新增节点通过前后缀位置信息删除节点 中间部份 diff判断节点是否需要移动删除节点删除未查找到的节点删除多余节点 移动和新增节点最长递增子序列 求解最长递增子序列位置信息 查找相同前后缀 如上图所示,新旧 children 拥…