etcd 在超大规模数据场景下的性能优化

概述

etcd是一个开源的分布式的kv存储系统, 最近刚被cncf列为沙箱孵化项目。etcd的应用场景很广,很多地方都用到了它,例如kubernetes就用它作为集群内部存储元信息的账本。本篇文章首先介绍我们优化的背景,为什么我们要进行优化, 之后介绍etcd内部存储系统的工作方式,之后介绍本次具体的实现方式及最后的优化效果。

优化背景

由于阿里巴巴内部集群规模大,所以对etcd的数据存储容量有特殊需求,之前的etcd支持的存储大小无法满足要求, 因此我们开发了基于etcd proxy的解决方案,将数据转储到了tair中(可类比redis))。这种方案虽然解决了数据存储容量的问题,但是弊端也是比较明显的,由于proxy需要将数据进行搬移,因此操作的延时比原生存储大了很多。除此之外,由于多了tair这个组件,运维和管理成本较高。因此我们就想到底是什么原因限制了etcd的存储容量,我们是否可以通过技术手段优化解决呢?

提出了如上问题后我们首先进行了压力测试不停地像etcd中注入数据,当etcd存储数据量超过40GB后,经过一次compact(compact是etcd将不需要的历史版本数据删除的操作)后发现put操作的延时激增,很多操作还出现了超时。监控发现boltdb内部spill操作(具体定义见下文)耗时显著增加(从一般的1ms左右激增到了8s)。之后经过反复多次压测都是如此,每次发生compact后,就像世界发生了停止,所有etcd读写操作延时比正常值高了几百倍,根本无法使用。

etcd内部存储工作原理


etcd存储层可以看成由两部分组成,一层在内存中的基于btree的索引层,一层基于boltdb的磁盘存储层。这里我们重点介绍底层boltdb层,因为和本次优化相关,其他可参考上文。

etcd中使用boltdb作为最底层持久化kv数据库,boltdb的介绍如下:

Bolt was originally a port of LMDB so it is architecturally similar. 
Both use a B+tree, have ACID semantics with fully serializable transactions, and support lock-free MVCC using a single writer and multiple readers.
Bolt is a relatively small code base (<3KLOC) for an embedded, serializable, transactional key/value database so it can be a good starting point for people interested in how databases work。

如上介绍,它短小精悍,可以内嵌到其他软件内部,作为数据库使用,例如etcd就内嵌了boltdb作为内部存储k/v数据的引擎。
boltdb的内部使用B+ tree作为存储数据的数据结构,叶子节点存放具体的真实存储键值。它将所有数据存放在单个文件中,使用mmap将其映射到内存,进行读取,对数据的修改利用write写入文件。数据存放的基本单位是一个page, 大小默认为4K. 当发生数据删除时,boltdb不直接将删掉的磁盘空间还给系统,而是内部将他先暂时保存,构成一个已经释放的page池,供后续使用,这个所谓的池在boltdb内叫freelist。例子如下:

红色的page 43, 45, 46, 50 页面正在被使用,而page 42, 44, 47, 48, 49, 51 是空闲的,可供后续使用。

如下etcd监控图当etcd数据量在50GB左右时,spill 操作延时激增到了8s

问题分析

由于发生了用户数据的写入, 因此内部B+ tree结构会频繁发生调整(如再平衡,分裂合并树的节点)。spill操作是boltdb内部将用户写入数据commit到磁盘的关键一步, 它发生在树结构调整后。它释放不用的page到freelist, 从freelist索取空闲page存储数据。

通过对spill操作进行更深入细致的调查,我们发现了性能瓶颈所在, spill操作中如下代码耗时最多:

// arrayAllocate returns the starting page id of a contiguous list of pages of a given size.
// If a contiguous block cannot be found then 0 is returned.
func (f *freelist) arrayAllocate(txid txid, n int) pgid {...var initial, previd pgidfor i, id := range f.ids {if id <= 1 {panic(fmt.Sprintf("invalid page allocation: %d", id))}// Reset initial page if this is not contiguous.if previd == 0 || id-previd != 1 {initial = id}// If we found a contiguous block then remove it and return it.if (id-initial)+1 == pgid(n) {if (i + 1) == n {f.ids = f.ids[i+1:]} else {copy(f.ids[i-n+1:], f.ids[i+1:]) # 复制f.ids = f.ids[:len(f.ids)-n]}...return initial}previd = id}return 0
}

之前etcd内部内部工作原理讲到boltdb将之前释放空闲的页面存储为freelist供之后使用,如上代码就是freelist内部page再分配的函数,他尝试分配连续的n个page页面供使用,返回起始页page id。 代码中f.ids是一个数组,他记录了内部空闲的page的id。例如之前上图页面里f.ids=[42,44,47,48,49,51]

当请求n个连续页面时,这种方法通过线性扫描的方式进行查找。当遇到内部存在大量碎片时,例如freelist内部存在的页面大多是小的页面,比如大小为1或者2,但是当需要一个size为4的页面时候,这个算法会花很长时间去查找,另外查找后还需调用copy移动数组的元素,当数组元素很多,即内部存储了大量数据时,这个操作是非常慢的。

优化方案

由上面的分析, 我们知道线性扫描查找空页面的方法确实比较naive, 在大数据量场景下很慢。前yahoo的chief scientist Udi Manber曾说过在yahoo内最重要的三大算法是 hashing, hashing and hashing!(From algorithm design manual)

因此我们的优化方案中将相同大小的连续页面用set组织起来,然后在用hash算法做不同页面大小的映射。如下面新版freelist结构体中的freemaps数据结构。

type freelist struct {...freemaps       map[uint64]pidSet           // key is the size of continuous pages(span), value is a set which contains the starting pgids of same sizeforwardMap     map[pgid]uint64             // key is start pgid, value is its span sizebackwardMap    map[pgid]uint64             // key is end pgid, value is its span size...
}

除此之外,当页面被释放,我们需要尽可能的去合并成一个大的连续页面,之前的算法这里也比较简单,是个是耗时的操作O(nlgn).我们通过hash算法,新增了另外两个数据结构forwardMapbackwardMap, 他们的具体含义如下面注释所说。

当一个页面被释放时,他通过查询backwardMap尝试与前面的页面合并,通过查询forwardMap尝试与后面的页面合并。具体算法见下面mergeWithExistingSpan函数。


// mergeWithExistingSpan merges pid to the existing free spans, try to merge it backward and forward
func (f *freelist) mergeWithExistingSpan(pid pgid) {prev := pid - 1next := pid + 1preSize, mergeWithPrev := f.backwardMap[prev]nextSize, mergeWithNext := f.forwardMap[next]newStart := pidnewSize := uint64(1)if mergeWithPrev {//merge with previous spanstart := prev + 1 - pgid(preSize)f.delSpan(start, preSize)newStart -= pgid(preSize)newSize += preSize}if mergeWithNext {// merge with next spanf.delSpan(next, nextSize)newSize += nextSize}f.addSpan(newStart, newSize)
}

新的算法借鉴了内存管理中的segregated freelist的算法,它也使用在tcmalloc中。它将page分配时间复杂度由O(n)降为O(1), 释放从O(nlgn)降为O(1),优化效果非常明显。

实际优化效果

以下测试为了排除网络等其他原因,就测试一台etcd节点集群,唯一的不同就是新旧算法不同, 还对老的tair作为后端存储的方案进行了对比测试. 模拟测试为接近真实场景,模拟100个客户端同时向etcd put 1百万的kv对,kv内容随机,控制最高5000qps,总计大约20~30GB数据。测试工具是基于官方代码的benchmark工具,各种情况下客户端延时如下

旧的算法时间

有一些超时没有完成测试,

新的segregated hashmap

etcd over tail 时间

方案完成耗时性能提升倍数
新的hashmap算法210s1x
旧array算法4974s24x
etcd over tair17058x

在数据量更大的场景下,并发度更高的情况下新算法提升倍数会更多。

总结

这次优化将boltdb中freelist分配的内部算法由O(n)降为O(1), 释放部分从O(nlgn)降为O(1), 解决了在超大数据规模下etcd内部存储的性能问题,使etcd存储100GB数据时的读写操作也像存储2GB一样流畅。并且这次的新算法完全向后兼容,无需做数据迁移或是数据格式变化即可使用新技术带来的福利!
目前该优化经过2个多月的反复测试, 上线使用效果稳定,并且已经贡献到了开源社区link,在新版本的boltdb和etcd中,供更多人使用。


原文链接
本文为云栖社区原创内容,未经允许不得转载。

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

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

相关文章

时间复杂度的表示、分析、计算方法……一文带你看懂时间复杂度!

作者 | OverRedMaple责编 | Carol来源 | CSDN 博客封图 | CSDN付费下载于东方 IC如果你还在发愁究竟怎么计算时间复杂度和空间复杂度&#xff0c;那你是来对地方了&#xff01;名词解释&#xff1a;在计算机科学中&#xff0c;时间复杂性&#xff0c;又称时间复杂度&#xff0c…

ThreadPoolExecutor中的keepAliveTime详解

文章目录一、keepAliveTime的概念二、keepAliveTime的设置方法2.1. 通过构造函数设置2.2. 通过setKeepAliveTime方法动态设置三、线程是如何根据keepAliveTime进行销毁的阅读这篇文章&#xff0c;你将会知道&#xff1a; keepAliveTime的概念。 keepAliveTime是如何设置的。 线…

OPPO数据中台之基石:基于Flink SQL构建实数据仓库

本文整理自 2019 年 4 月 13 日在深圳举行的 Flink Meetup 会议&#xff0c;分享嘉宾张俊&#xff0c;目前担任 OPPO 大数据平台研发负责人&#xff0c;也是 Apache Flink contributor。本文主要内容如下&#xff1a; OPPO 实时数仓的演进思路&#xff1b;基于 Flink SQL 的扩…

如何实现7*24小时灵活发布?阿里技术团队这么做

研发效能分为两块&#xff0c;一是用技术的更新来提升效率&#xff1b;二是提高整个技术生态中的协同效率&#xff0c;激发技术活力。阿里巴巴技术团队在此基础上要实现的终极目标是打造7*24小时灵活发布的通道&#xff0c;以及提供更快的业务代码迭代能力。今天&#xff0c;阿…

不看就亏系列!这里有完整的 Hadoop 集群搭建教程,和最易懂的 Hadoop 概念!| 附代码...

作者 | chen_01_c责编 | Carol来源 | CSDN 博客封图 | CSDN付费下载于视觉中国hadoop介绍Hadoop 是 Lucene 创始人 Doug Cutting&#xff0c;根据 Google 的相关内容山寨出来的分布式文件系统和对海量数据进行分析计算的基础框架系统&#xff0c;其中包含 MapReduce 程序&#…

数据科学家是个性感的工作?我信你个鬼!

数据科学家40%是个吸尘器&#xff0c;40%是个清洁工&#xff0c;剩下20%是个算命的。作者 | Jingles译者 | 香槟超新星&#xff0c;责编 | 夕颜出品 | CSDN&#xff08;ID:CSDNnews&#xff09;根据《哈佛商业评论》的说法&#xff0c;数据科学家是21世纪最性感的工作。在现在这…

深入搜索引擎原理

之前几段工作经历都与搜索有关&#xff0c;现在也有业务在用搜索&#xff0c;对搜索引擎做一个原理性的分享&#xff0c;包括搜索的一系列核心数据结构和算法&#xff0c;尽量覆盖搜索引擎的核心原理&#xff0c;但不涉及数据挖掘、NLP等。文章有点长&#xff0c;多多指点~~ 一…

印度版的“大众点评”如何将 Food Feed 业务从 Redis 迁移到 Cassandra

Zomato 是一家食品订购、外卖及餐馆发现平台&#xff0c;被称为印度版的“大众点评”。目前&#xff0c;该公司的业务覆盖全球24个国家&#xff08;主要是印度&#xff0c;东南亚和中东市场&#xff09;。本文将介绍该公司的 Food Feed 业务是如何从 Redis 迁移到 Cassandra 的…

利用Packer自定义镜像创建容器集群

阿里云容器服务Kubernetes集群支持CentOS操作系统&#xff0c;在绝大多数情况下可以满足客户的要求。但是有些客户由于业务系统对操作系统依赖比较高&#xff0c;希望定制化一些操作系统参数&#xff0c;则可以用自定义镜像来创建Kubernetes集群。 创建自定义操作系统镜像有两…

“远程”、“协作”风靡之际,你对TA知晓多少?

作者|晶少 转载|CSDN博客 2.4亿人在线使用文档协作无延宕…… 6万名武汉中小学生实力打造“远程课堂”活学高效…… 疫情以来&#xff0c;“远程”、“协作”持续大热&#xff0c;此局毋庸置疑。 根据QuestMobile最新发布的《2020中国移动互联网“战役”专题报告》显示&am…

蚂蚁金服OceanBase性价比是传统数据库的十倍

200名数据库领域从业三年以上的会员投票和专业的评委评选&#xff0c;在如此严苛的条件之下&#xff0c;蚂蚁金服金融级分布式关系数据库OceanBase 2.0依然获得了专家评审团的一致青睐&#xff0c;荣获2019中国数据库技术大会的“年度最佳创新产品”奖。 蚂蚁金服资深总监韩鸿源…

战疫内外,京东智联云如此“一鸣惊人”!

作者|晶少 转载|CSDN博客 鼠年春节&#xff0c;一场疫情突如其来地打破了人们平静的生活&#xff1b;但在滨州&#xff0c;一款名为“疫情助手”的上线软件却为滨州市民的疫情生活带来“雪中送炭”的丝丝温情&#xff0c;细微知著中人们深深感受到了京东诠释而来的“ABCDE”技…

亿级消息系统的核心存储:Tablestore发布Timeline 2.0模型

背景 互联网快速发展的今天&#xff0c;社交类应用、消息类功能大行其道&#xff0c;占据了大量网络流量。大至钉钉、微信、微博、知乎&#xff0c;小至各类App的推送通知&#xff0c;消息类功能几乎成为所有应用的标配。根据场景特点&#xff0c;我们可以将消息类场景归纳成三…

SLS机器学习最佳实战:日志聚类+异常告警

0.文章系列链接 SLS机器学习介绍&#xff08;01&#xff09;&#xff1a;时序统计建模SLS机器学习介绍&#xff08;02&#xff09;&#xff1a;时序聚类建模SLS机器学习介绍&#xff08;03&#xff09;&#xff1a;时序异常检测建模SLS机器学习介绍&#xff08;04&#xff09;…

大数据成长之路:谈谈那些必须学习的Linux基础知识

作者| Roy瑞士责编| Carol封图| CSDN│下载于视觉中国这里主要介绍学习大数据过程中用到的Linux基础知识&#xff0c;现在主攻的方向是大数据开发&#xff0c;欢迎大家共同交流。环境推荐安装VMware虚拟机并安装CentOS操作系统&#xff0c;具体资源的下载和安装可以查到&#x…

数据可用不可见!揭秘蚂蚁区块链摩斯安全计算平台

“数据安全”与“隐私泄漏”制约数字经济长期发展 在新的商业智能时代&#xff0c;已形成广泛的共识&#xff1a;数据是最基础的生产资料&#xff0c;各个行业与企业对于数据的利用也步入成熟期。可见的未来&#xff0c;数据利用的深度和广度将进一步升级&#xff0c;进入跨机…

如何与亦敌亦友的 null 说拜拜?大神原来是这么做的!

作者| 沉默王二责编| Carol封图| CSDN│下载于视觉中国从 10 年前我开始写第一行 Java 代码至今&#xff0c;一直觉得 null 在 Java 中是一个最特殊的存在&#xff0c;它既是好朋友&#xff0c;可以把不需要的变量置为 null 从而释放内存&#xff0c;提高性能&#xff1b;它又是…

K8s中Pod健康检查源代码分析

了解k8s中的Liveness和Readiness Liveness: 表明是否容器正在运行。如果liveness探测为fail&#xff0c;则kubelet会kill掉容器&#xff0c;并且会触发restart设置的策略。默认不设置的情况下&#xff0c;该状态为success. Readiness: 表明容器是否可以接受服务请求。如果re…

CSE:阿里在线应用如何演进成Serverless架构

Cloud Service Engine&#xff0c;简称CSE&#xff0c;是中间件部门研发的面向通用Serverless计算的中间件产品&#xff0c;目标是具备AWS Lambda的各种优势&#xff0c;同时可以解决AWS Lambda的关键技术缺陷。 AWS Lambda如果用于核心业务&#xff0c;可能会有以下缺陷&…

郫都区计算机学校,成都郫县好升学的计算机学校有哪些

【郫县好一、成都郫县希望1.成都郫县希望职业学校/招生代码&#xff1a;512632.成都郫县希望职业学校/学校简介&#xff1a;成都郫县希望职业学校学校是由郫都区教育局批准成立的&#xff0c;由希望集团投资创办的一所全日制、专业化的民办中等职业学校, 由郫都区教育局主管。学…