AeroSpike踩坑手记1:Architecture of a Real Time Operational DBMS论文导读

又开了一个新的坑,笔者工作之后维护着一个 NoSQL 数据库。而笔者维护的数据库正是基于社区版本的 Aerospike打造而来。所以这个踩坑系列的文章属于工作总结型的内容,会将使用开发 Aerospike 的各种问题进行总结梳理,希望能够给予大家启发和帮助。第一篇开山之文,就先从Aerospike 公司在16年数据库顶会 VLDB的一篇论文 《Aerospike: Architecture of a Real Time Operational DBMS》展开,来高屋建瓴的审视一下 Aeropike 的设计思路,来看看如何Aerospike这款分布式数据库有什么亮点值得我们学习借鉴的,由于论文发布在2016年,笔者完成这篇文章时Aerospike的版本已经发布到4.5了,很多最新的实现与老论文已经有些不同了,这点希望大家理解。准备好,老司机发车了~~

1.AeroSpike 的定位与场景

从论文的题目出发,这篇文章的核心在于实时操作数据库的架构,在论文引言之中对Aerospike的定位是一个高性能分布式数据库,用于处理实时的交互式在线服务。所以说,大多数使用Aerospike的场景是实时决策系统,它们有海量的数据规模,并且有严格的SLA要求,同时是百万级别的 QPS,具有ms的查询时延。显然,这样的场景使用传统的 RDMS 是不现实的,在论文之中,提到 Aerospike 的一个典型的应用场景,广告推荐系统,我们来一起看看它们是如何契合的:

众所周知,广告推荐系统这样的应用场景需要极高的吞吐量、低延迟和稳定的可用性。同时,广告推荐系统具有随时间增加其数据使用量以提高其推荐的质量的趋势,即,在固定时间量中可访问的数据越多,推荐就越精确。下图展示了一个广告推荐系统是如何结合 Aerospike来提供推荐服务的:

显然,这就是笔者之前的文章之中聊到的典型的Lambda架构,笔者当时正是以广告推荐系统进行举例的。所以在这里笔者就不展开再聊Aerospike在其中充当的实时流存储的角色了,感兴趣的朋友可以看这里

2.Aerospike的总体架构

除了广告推荐系统之外,论文的原文还介绍了许多关于Aerospike的适用场景,有兴趣的可以通过原文深入了解。接下来我们直奔主题,来看看Aerospike的总体架构

由上图所示,Aerospike核心分为三个层次:

  • 客户端层
  • 分布式层
  • 数据层

所以接下来我们来一一解构,Aerospike的各个层次。

2.1 分布式层

与Cassandra类似的是,Aerospike也采用了P2P的架构,也就是说,集群之中不存在的中心节点,每个节点都是对等的结构。而分布式层聚焦在两点之上:

  • 节点分布
  • 数据分布

2.1.1 节点分布

节点需要处理节点成员关系,并对Aerospike集群当前成员达成共识。比如:网络故障和节点加入或离开。

节点分布所关心的点在于:

  • 集群中的所有节点到达当前集群成员的单一一致视图。
  • 自动检测新节点的加入与离开。
  • 检测网络故障并且能够容忍网络的不稳定性。
  • 尽量缩短集群成员变化的时间。
2.1.1.1 集群视图

每个Aerospike节点都会自动分配一个唯一的节点标识符,它是其MAC地址和监听端口唯一确定的。集群整体视图由一个元组定义:<cluster_key,succession_list>

  • cluster_key是随机生成的8字节值标识一个唯一的集群视图
  • succession_list 是一个集合,标识了所有属于集群的Aerospike节点

cluster_key标识当前集群成员身份状态,并在每次集群视图更改时更改。 它使得Aerospike节点用于区分两个不同的集群视图。对集群视图的更改都对集群的性能有着有着显著影响,这意味着需要快速检测节点加入/离开,并且随后需要存在有效的一致性机制来处理对集群视图的更改。

2.1.1.2 节点检测

节点的加入或离开是通过不同节点之间定期交换的心跳消息来检测的。集群中的每个节点都维护一个邻接列表,该列表是最近向节点发送心跳消息的节点列表。如果在配置的超时间隔内,由于没有收到对应的心跳消息,从邻近列表中删除对应的节点。

而节点检测机制需要保证:

  • 避免由于零星和短暂的网络故障而将节点误删除出集群。
  • 防止不稳定节点频繁加入和离开集群。
辅助心跳

在阻塞的网络中,有可能任意丢失某些数据包。因此,除了常规的心跳消息之外,节点还使用了定期交换的其他消息作为备选的辅助心跳机制。例如,副本写可以用作心跳消息的辅助。这确保了,只要节点之间的主要或次要心跳通信是完整的,仅主心跳信息的丢失不会引起集群视图的变更。

健康检测

集群中的每个节点可以通过计算平均消息丢失来评估其每个节点的健康评分,健康评分是通过:每个节点接收的预期消息数量与每个节点接收的实际消息数量的加权平均值计算而成的。

设t为心跳消息的发送间隔,w为心跳信息的发送频率,r为在这个窗口时间中丢失的心跳消息的数量,α是一个比例因子,la(prev)之前的健康因子。la(new)为更新之后的健康因子,所以它的计算方式如下图所示:

健康因子在所有节点标准差两倍的节点是异常值,并且被认为是不健康的。如果不健康的节点是集群的成员,则将其从集群中删除。如果不是成员,则直到其平均消息丢失在可容忍的限度内才能加入集群。在实践中,α被设置为0.95,节点的历史表现比赋予了更多的权重。窗口时间一般设置为1秒。

2.1.1.3 视图更改

对邻近列表的更改就会产生新集群视图,这需要一次Paxos一致性算法。邻接链表之中节点标识符最高的节点充当Paxos提议者,如果建议被接受,节点就开始重新分配数据。

Aerospike实现了最小化集群由于单一故障事件而更改视图的次数。例如,有故障的网络交换机可能使集群成员的子集不可到达。一旦恢复了网络,就需要将这些节点添加到集群中。如果每个丢失或加入的节点都需要触发创建新的集群视图,这种代价是很高的。所以Aerospike仅在固定的集群更改间隔(间隔本身的时间是可配置的)开始时做出集群视图的调整。这里的想法是避免如心跳子系统检测到的那样对节点到达和离开事件反应太快,而是用一个集群视图更改来处理一批节点加入或删除的事件。这避免了由重复的集群视图更改和数据分布导致的大量潜在开销。集群更改间隔等于节点超时值的两倍,确保在单个间隔中明确检测到由于单个网络故障而失败的所有节点。

2.2 数据分布

Aerospike使用RipeMD160算法将record的key散列为160字节的digest,digest被划分为4096个分区。分区是Aerospike中最小的数据分布单元,根据key 的digest为记录分配分区。即使key的分布是倾斜的,在digest空间中分布也是均匀的,它有助于避免在数据访问期间创建热点,这有助于系统的容错。

一个好的数据分布需要满足下列条件:

  • 存储负载均匀地分布在集群中,
  • 具有较好的扩展性
  • 节点出现变化时,数据的重新平衡是非破坏性的

数据分配算法为每个分区生成一个副本列表。副本列表中的第一个节点是该分区的主节点,其余的节点是副本。在默认情况下,所有读/写都通过副本的主节点。Aerospike支持任意数量的副本,(通常设置为两副本,笔者在实际使用中也是两副本)。 Aerospike 采取的是一致性哈希的分片分配的方式,当节点出现失效或宕机的情况时。这个节点可以从副本列表中删除,而后续节点的左移。如下图所示,如果该节点需要承载了数据的副本,则需要将此分区中的记录复制到新节点。一旦原始节点返回并再次成为集群的一部分,它将简单地重新获得其在分区复制列表中的位置。向集群中添加一个全新的节点将具有将此节点插入各个分区副本列表中的某个位置的效果。因此,将导致每个分区的后续节点的右移,而新节点左侧的分配不受影响。

上面的讨论给出了算法就能确保副本的最低迁移成本。但是当一个节点被删除并重新加入集群时,它需要和其他副本进行同步。当一个全新的节点加入一个拥有大量现有数据的集群,所以新的节点需要获得对应分区中所有记录的全新副本,并且还能够处理新的读写操作。接下来我们来看看副本同步的机制:

2.2.1 数据迁移

将record从一个节点移动到另一个节点的过程称为迁移。在每次集群视图改变之后,就需要进行数据迁移。每个分区的主副本为对应的分区分配唯一的分区版本,这个版本号会被复制到各个副本中。在集群视图更改之后,节点之间交换分区的分区版本和数据。

2.2.1.1 增量迁移

Aerospike使用增量迁移的方式优化迁移的速度。如果在能够在分区版本上建立总顺序,那么数据迁移的过程将更加有效。例如,如果节点1上的分区版本的值小于节点2上的相同分区版本的值,则节点1上的分区版本可能被丢弃。但是,通过分区版本号的排序是有问题的,因为网络分区引起的集群分裂会引起分区版本的冲突。

所以当两个版本冲突时,节点需要协商实际记录中的差异,并通过只对应于两个分区版本之间的差异的数据发送。在某些情况下,可以根据分区版本顺序完全避免迁移。在其他情况下,如滚动升级,可以传递增量的数据,而不是迁移整个分区。

  • 迁移流程中的读写

    如果分区正在进行迁移时,如果此时对应的分区有读写,主副本会读取所有的分区版本,协调出一个最终胜出的版本用于读或写事务。(按照笔者对文章的理解,这个流程会涉及多个副本,是一个耗时的操作

  • 没有数据的主副本

    新添加到正在运行的集群的空节点成为了主副本,并且没有对应分区的数据,没有任何数据的分区的副本被标记为处于DESYNC状态。Aerospike会指定一个最多记录的分区版本作为这个分区的代理主副本。所有的读操作都会指向代理主副本。(此时写还是在主副本上如果客户端可以容忍读取旧版本的记录,则可以减少协调胜出版本的损耗。此代理主副本的工作会持续到对应分区的迁移完成。

  • 迁移顺序
    • 小分区优先

      让分区版本中记录最少的分区开始迁移。这种策略可以快速减少特定分区的不同副本的数量。随着迁移的完成,延迟会改善,需要进行协调副本版本会减少对应的节点进行的通信。

    • 热分区优先

      根据分区的 qps 的大小确认分区迁移的顺序。这种策略的目标与小分区优先的逻辑是一致的。

2.2.2 快速重启

节点重新启动是很常见的场景,比如:服务升级,宕机重启等。Aerospike的索引是内存中的而没有存储在持久设备上。在节点重新启动时,需要通过扫描持久设备上的记录来重新构建索引。(这个过程巨慢无比,笔者目前维护的大集群,单机存储数据量达1T,单次启动需要30分钟之久

为了避免在每次重新启动时重新构建索引,Aerospike的利用了共享内存来实现快速重启。(目前开源的版本是不支持这个功能的,笔者所在的团队通过二次开发实现了对应的功能。但是机器一旦重启之后,也必须重建索引,所以有机器频繁重启的,可以考虑一些对应索引进行落盘

2.3 客户端层

2.3.1 服务发现

在Aerospike中,每个节点维护着一个邻接列表标识着全局的节点分布情况。客户端从一个种子节点,发现整个集群的节点。

每个客户端进程都将集群分区映射的信息存储在共享内存之中。为了保持信息最新,客户端进程定期通过AeroSpike节点,来检查集群是否有任何变动。它通过根据服务器的最新版本检查本地存储的版本来实现这一点。对于单机的多个客户端,AeroSpike将数据存储在共享内存之中,并且用跨进程的互斥代码来实现集群信息的共享。

####2.3.2 连接管理

对于每个集群节点,在初始化时,客户端需为节点创建一个内存结构,并存储其分区映射,并且为节点维护连接。一旦出现节点和客户端的网络问题,这种频繁的内存调整容易产生性能问题。所以Aerospike客户端实现以下策略:

####2.3.2.1 健康计数

为了避免由于偶尔的网络故障导致上文的问题。当客户端连接集群节点操作发生问题时,会对集群节点进行故障计数。当故障计数超过特定阈值时,客户端才会删除集群节点。对集群节点的成功操作可以将故障计数重置为0。

####2.3.2.2 节点咨询

网络的故障通常很难复杂。在某些极端情况下,集群节点可以彼此感知,但是客户端不能直接感知到集群节点X。在这些情况下,客户端连接集群之中所有可见节点,并咨询集群之中的所有节点在其邻接列表中是否包含X。如果没有包含,则客户端将等待一个阈值时间,永久移除X节点。

3 跨数据中心同步

3.1.1 失效接管

在正常状态下(即,当没有故障时),每个节点只将节点上主副本的数据传送到远程集群。只在节点出现故障时才使用从副本。如果一个节点出现失效,所有其他节点能够检测到,并代表失效的节点接管工作。

3.1.2 数据传输优化

当发生写操作时,主副本在日志之中记录。进行数据传输时,首先读取一批日志,如果同一个记录有多个更新,选取一批之中最近的更新记录。一旦选取了记录,将其与实际记录比较。如果日志文件上的记录小于实际的记录,则跳过该记录。对于但是跳过记录的次数有一个上限,因为如果记录不断更新,那么可能永远不会推送记录。当系统中存在频繁更新记录的热键时,这些优化提供了巨大的好处。

4 存储落地

4.1 存储管理

Aerospike的存储层是一个混合模型,其中索引存储在内存中(不持久),数据可以选择存储在持久存储(SSD)或内存之中。而随机的读写SSD容易产生写放大。(笔者之前的文章也同样聊过这个问题,可以参考这里)为了避免在SSD的单个块上产生不均匀的磨损,Aerospike采取了批量写的方式。当更新记录时,从SSD读取旧记录,并将更新后的副本写入缓冲区。当缓冲区在充满时刷新到SSD上。

读取单元RBLOCKS的大小是128字节。而WBLOCK的大小,可配置,通常为1MB。这样的写入优化了磁盘寿命。Aerospike通过Hash函数在多个设备上切分数据来操作多个设备。这允许并行访问多个设备,同时避免任何热点。

4.2 Defragmentation垃圾清理

Aerospike通过运行后台碎片整理进程来回收空间。每个设备对应的块都存在填充因子。块的填充因子写入在块中。系统启动时,存储系统载入块中的填充因子,并在每次写入时保持更新。当块的填充因子低于阈值时,块成为碎片整理的候选者,然后排队等待碎片整理。

块进行碎片整理时,将读取有效记录并将其移动到新的写入缓冲区,当写入缓冲区已满时,将其刷新到磁盘。为了避免混合新写和旧写,Aerospike维护两个不同的写缓冲队列,一个用于普通客户端写,另一个用于碎片整理。

设置一个较高的阈值(通常为50%)会导致设备不断的刷写。而较低的设置会降低磁盘的利用率。所以基于可立即被写入可用磁盘空间,调整碎片整理速率以确保有效的空间利用。

4.3 性能与调优

4.3.1 Post Write Queue

Aerospike没有维护LRU缓存,而是维护的post write queue。这是最近写入的数据缓存,这个缓存不需要额外的内存空间。post write queue提高了缓存命中率,并减少了存储设备上的I/O负载。

5.小结

关于论文之中对Aerospike的设计笔者已经夹带私货的阐述清晰了。而关于单机优化和Aerospike性能测试,笔者就不再赘述了,感兴趣的可以回到论文之中继续一探究竟。对于论文之中的细节想要进一步的了解,可以继续关注笔者后续关于Aerospike的拆坑手记~~~

原文地址:https://www.cnblogs.com/happenlee/p/10294102.html

时间: 2024-11-07 16:02:32

AeroSpike踩坑手记1:Architecture of a Real Time Operational DBMS论文导读的相关文章

基于Python技术栈的算法落地踩坑

背景介绍 在一些业务场景,我们需要把离线训练好的模型以微服务部署线上,如果是简单的使用sklearn pipeline,可以保存为XML格式的pmml供Java调用,在配置为4 core,8G内存的docker环境可以提供8K左右的高并发,并且这种docker可以快速大规模部署到PaaS云平台,优势相当明显,实际情况是算法人员会基于Python自定义lambda处理数据,而自定义的lambda是很难保存到pmml中的,并且很多公司的算法团队也是要求基于Python技术栈是 落地的. 踩坑过程 算

阿里云磁盘扩容踩坑总结

公司半年前上线一个新的项目,采购了一批阿里云主机,磁盘组成是40G系统盘+100G的数据盘,数据库采用MariaDB Galera Cluster集群部署,由于业务数据量快速增长,导致磁盘存储空间剩余量很少,急需要扩容,先总结整个项目规划中埋下的坑: 1.没有DBA对数据库的容量规划,而前期的运维人员采购时选用100G的SSD云盘: 2.数据库默认使用共享表空间,缺点是删除数据后不释放空间,当数据快速增长后,我们采取了先删除临时表数据的方式来尽量避免暴力扩容,争取在春节期间稳定,删除部分数据后,

Zabbix 踩坑之旅——zabbix触发重启tomcat

一.实验需求 公司tomcat服务经常自动崩溃,导致业务中断,暂时用zabbix对其执行监控,在tomcat崩溃时能够先自动启动,保证业务尽快恢复正常. 二.准备环境 系统环境:CentOS 6.5 IP地址: zabbix-server: 192.168.239.128 zabbix-agent: 192.168.239.130 zabbix的服务端和客户端的安装此处都以rpm包安装,配置略过.agent端上安装好tomcat. 三.开启踩坑之旅--agent端 ① 修改zabbix-agen

Android开发在路上:少去踩坑,多走捷径【转】

作者:gzjay,腾讯MIG无线产品部 高级工程师 最近一朋友提了几个Android问题让我帮忙写个小分享,我觉得对新人还是挺有帮助的,所以有了这个小分享. 1.目前, Android APP开发完成后,通常需要在哪些机型上进行测试? 2.目前, 开发Android APP时,需要考虑的分辨率有哪些? 这两个问题可以合起来回答的. http://developer.android.com/about/dashboards/index.html 源自Google Play的数据,每月都会进行upd

ELK之ES2.4.1双实例平滑升级至5.2.1踩坑并supervisor管理记

ES老集群用的2.4.1版本,跑的比较好就一直没动,最近看资料ES5.X已经稳定,并且性能有较大提升,心里就发痒了,但由于业务要保持高可以用的属性,就得想一个平滑升级的方案,最后想到了多实例过度的办法,5.X版本网上介绍配置变化较大,也做好了踩坑准备,确定好要升级后,立刻动手. 一.对应升级改造方案 使用端口9220和9330 安装并配置好新的ES5.2.1实例-->关掉logstash并将ES2.4.1实例堆栈调小重启(kafka保留3个小时日志所以不会丢失)-->启动ES5.2.1并将lo

vue+ vue-router + webpack 踩坑之旅

说是踩坑之旅 其实是最近在思考一些问题 然后想实现方案的时候,就慢慢的查到这些方案   老司机可以忽略下面的内容了 1)起因  考虑到数据分离的问题  因为server是express搭的   自然少不了res.render("xx",data)    这句话的意思就是去查找相应的模板文件然后在用数据去渲染在将渲染好的页面去返回给浏览器,给浏览器去解析,渲染模板其实就是做的替换字符串+拼接字符串的活  各种的模板引擎也有各个优化的点(比如可以将对应的模板编译的函数保存在内存中,然后在通

AI相关 TensorFlow -卷积神经网络 踩坑日记之一

上次写完粗浅的BP算法 介绍 本来应该继续把 卷积神经网络算法写一下的 但是最近一直在踩 TensorFlow的坑.所以就先跳过算法介绍直接来应用场景,原谅我吧. TensorFlow 介绍 TF是google开源出来的人工智能库,由python语言写的 官网地址:http://www.tensorflow.org/   请用科学上网访问 中文地址:http://www.tensorfly.cn/ 当然还有其他AI库,不过大多数都是由python 写的 .net 的AI库叫 Accord.net

之后要接触更多代码管理的知识——2015踩坑有感

前言 学习是没有止境的,管理代码的能力也永远需要提高. 前几个月还觉得R语言,业务上要用的都学得七七八八了呢,这几个月在自家部门吭哧吭哧搞报表自动化时,各个坑一踩一个准,才明白写代码,懂得一点语言特性固然重要,弄一套科学地管理代码的方法,却是势在必行. 因此在这里总结一下这几个月来我踩过的种种坑,以及之后在查阅种种大神的经验,以及学软件工程这门课时看到的一些比较妥当的方法,算是这几个月的一个总结.2016年的时候,真的要多学学如何科学地管理代码,科学开发 请注意,因为我属于跨专业半路出家写代码,

运维小白部署网站踩坑全过程

作为一名一个星期之前还是个运维小白的我,经过一番摸爬滚打终于把我的小网站部署上去了 一.服务器环境 OK,一开始我得到的是一台centos的服务器,没有装apache/nginx.mysql和php环境,一开始我是使用lnmp一键安装包,结果发现php好多扩展和依赖缺失,重装了php后才解决了问题,mysql也遇到一些问题,所以建议大家在装环境时除非一键lamp/lnmp安装包经历过实践的考验,不然还是老老实实地一个一个组件安装,php.mysql.nginx. lnmp/lamp安装成功的标志