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

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

阿里系统软件技术 2019-05-27 09:13:17 本文共5419个字,预计阅读需要14分钟。

http://www.itpub.net/2019/05/27/1958/

不明觉厉

作者 | 阿里云智能事业部高级开发工程师 陈星宇(宇慕)

划重点

  • etcd 优化背景
  • 问题分析
  • 优化方案展示
  • 实际优化效果

本文被收录在 5 月 9 日 cncf.io 官方 blog 中,链接:https://www.cncf.io/blog/2019/05/09/performance-optimization-of-etcd-in-web-scale-data-scenario/

概述

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 操作中如下代码耗时最多:

 1// arrayAllocate returns the starting page id of a contiguous list of pages of a given size. 2// If a contiguous block cannot be found then 0 is returned. 3func (f *freelist) arrayAllocate(txid txid, n int) pgid { 4         ... 5    var initial, previd pgid 6    for i, id := range f.ids { 7        if id <= 1 { 8            panic(fmt.Sprintf("invalid page allocation: %d", id)) 9        }1011        // Reset initial page if this is not contiguous.12        if previd == 0 || id-previd != 1 {13            initial = id14        }1516        // If we found a contiguous block then remove it and return it.17        if (id-initial)+1 == pgid(n) {18            if (i + 1) == n {19                f.ids = f.ids[i+1:]20            } else {21                copy(f.ids[i-n+1:], f.ids[i+1:]) # 复制22                f.ids = f.ids[:len(f.ids)-n]23            }2425            ...26            return initial27        }2829        previd = id30    }31    return 032}

之前 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 数据结构。

1type freelist struct {2  ...3    freemaps       map[uint64]pidSet           // key is the size of continuous pages(span), value is a set which contains the starting pgids of same size4    forwardMap     map[pgid]uint64             // key is start pgid, value is its span size5    backwardMap    map[pgid]uint64             // key is end pgid, value is its span size6    ...7}

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

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

 1// mergeWithExistingSpan merges pid to the existing free spans, try to merge it backward and forward 2func (f *freelist) mergeWithExistingSpan(pid pgid) { 3    prev := pid - 1 4    next := pid + 1 5 6    preSize, mergeWithPrev := f.backwardMap[prev] 7    nextSize, mergeWithNext := f.forwardMap[next] 8    newStart := pid 9    newSize := uint64(1)1011    if mergeWithPrev {12        //merge with previous span13        start := prev + 1 - pgid(preSize)14        f.delSpan(start, preSize)1516        newStart -= pgid(preSize)17        newSize += preSize18    }1920    if mergeWithNext {21        // merge with next span22        f.delSpan(next, nextSize)23        newSize += nextSize24    }2526    f.addSpan(newStart, newSize)27}

新的算法借鉴了内存管理中的 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 时间

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

总结

这次优化将  boltdb中 freelist 分配的内部算法由 O(n) 降为 O(1), 释放部分从 O(nlgn) 降为 O(1), 解决了在超大数据规模下 etcd 内部存储的性能问题,使 etcd 存储 100GB 数据时的读写操作也像存储 2GB 一样流畅。并且这次的新算法完全向后兼容,无需做数据迁移或是数据格式变化即可使用新技术带来的福利!

目前该优化经过 2 个多月的反复测试, 上线使用效果稳定,并且已经贡献到了开源社区(https://github.com/etcd-io/bbolt/pull/141),在新版本的 boltdb 和 etcd 中,供更多人使用。

本文由 阿里系统软件技术 发布在 ITPUB,转载此文请保持文章完整性,并请附上文章来源(ITPUB)及本页链接。
原文链接:http://www.itpub.net/2019/05/27/1958/

原文地址:https://www.cnblogs.com/jinanxiaolaohu/p/11130257.html

时间: 2024-08-11 23:32:35

[转帖]etcd 在超大规模数据场景下的性能优化的相关文章

百度技术沙龙 - 大数据场景下主题检索应用

第48期百度技术沙龙上的<大数据场景下主题检索应用>讲座介绍了很多训练大规模主题模型的技术细节.讲座回来后,我粗略整理了下讲座上涉及的主题模型和训练大规模模型相关的资料和文献. 1. 主题模型的发展历史 a. 布尔模型 Boolean model b. 向量空间模型 VSM (Vector space model) c. 潜在语义索引 LSI (Latent semantics index) - 首先作为一种降维技术, 对doc-word矩阵进行SVD分解 d. 概率潜在语义分析pLSA e.

调度、模型、同步与任务——阿里云大数据数仓建设性能优化方案

摘要:对于阿里云大数据数仓建设性能优化而言,主要可以从调度优化.模型优化.同步优化以及任务优化这四个方面着手.其实,对于性能优化而言,最终还是会归结到"资源"之上,所以资源是否足够,分配是否合理也是我们在进行性能优化时必须考虑的关键所在. 本文将主要围绕以下四个方面进行介绍:调度优化.模型优化.同步优化以及任务优化.对于调度优化而言,将分享任务调度如何进行优化,以及如何看到调度的瓶颈点,以及在初步进行建设和使用数据仓库的任务之后,对于任务如何进行调整来满足业务的时间要求.对于模型优化而

大数据分页实现与性能优化

摘要:Web 应用程序中经常使用数据分页技术,该技术是提高海量数据访问性能的主要手段.实现web数据分页有多种方案,本文通过实际项目的测试,对多种数据分页方案深入分析和比较,找到了一种更优的数据分页方案Row_number()二分法.它依靠二分思想,将整个待查询记录分为2部分,使扫描的记录量减少一半,进而还通过对数据表及查询条件进行优化,实现了存储过程的优化.根据Row_number()函数的特性,该方案不依赖于主键或者数字字段,大大提高了它在实际项目中的应用,使大数据的分页效率得到了更显著的提

Oracle在Linux下的性能优化

Oracle数据库内存参数的优化 Ø       与oracle相关的系统内核参数 Ø       SGA.PGA参数设置   Oracle下磁盘存储性能优化 Ø       文件系统的选择(ext2/ext3.xfs.ocfs2) Ø       Oracle ASM存储  1.优化oracle性能参数之前要了解的情况 1)物理内存有多大 2)操作系统估计要使用多大内存 3)数据库是使用文件系统还是裸设备 4)有多少并发连接 5)应用是OLTP类型还是OLAP类型 2.oracle数据库内存参

解析世界杯超大规模直播场景下的码率控制

摘要: 这一次的世界杯,与以往世界杯最大的区别在于,有很多互联网用户观看直播,而不是在电视上.在互联网观看直播,互联网的网络条件不一样,观众会看不同码率的视频.所以主要分享下阿里云在直播中怎么做码率控制. 在本月的重庆云栖大会飞天技术汇专场中,阿里云高级算法专家黄海宇分享了题为<超大规模直播码率控制>的议题,从生产的链路角度来说世界杯怎么让观众看到更加清晰的视频. 这一次的世界杯,与以往世界杯最大的区别在于,有很多互联网用户观看直播,而不是在电视上.在互联网观看直播,互联网的网络条件不一样,观

互联网场景下闪存优化测试和应用

原创 2016-07-11 杨尚刚 Docker 闪存在这几年存储领域发展非常快,应用也越来越广泛,如何能更好的使用闪存,本次分享讲一些闪存相关的优化和应用. 闪存应用场景 数据库 NoSQL 分布式存储 CDN 公有云存储 综合上面几种场景看,闪存主要适合有比较高的随机IO需求和带宽需求的场景.场景选择上,也是要发挥闪存的长处.目前上面业务中 未来几年发展比较快的会是在公有云存储这一部分.下图就是某厂商云盘对比,可以看到闪存的价格已经很接近机械硬盘了,而单从每IOPS成本看,性价比会更高. 闪

Linux 下网络性能优化方法简析

概述 对于网络的行为,可以简单划分为 3 条路径:1) 发送路径,2) 转发路径,3) 接收路径,而网络性能的优化则可基于这 3 条路径来考虑.由于数据包的转发一般是具备路由功能的设备所关注,在本文中没有叙述,读者如果有兴趣,可以自行学习(在 Linux 内核中,分别使用了基于哈希的路由查找和基于动态 Trie 的路由查找算法).本文集中于发送路径和接收路径上的优化方法分析,其中的 NAPI 本质上是接收路径上的优化,但因为它在 Linux 的内核出现时间较早,而它也是后续出现的各种优化方法的基

TI C6000 数据存储处理与性能优化

存储器之于CPU好比仓库之于车间.车间加工过程中的原材料.半成品.成品等均需入出仓库,生产效率再快,如果仓库周转不善,也必然造成生产阻塞.如同仓库需要合理地规划管理一般,数据存储也需要恰当的处理技巧来提升CPU的运算性能. 本文基于TI C6000系列DSP,介绍了与运算性能优化有关的存储器知识.针对具体的数据存储问题,给出相应的代码优化策略,并将容易混淆的概念集中讨论.  名词说明   EMIF: External Memory Interface PMC: Program Memory Co

面试官:怎么设计大文件、大数据场景下的传输加密方案?

某年某月某一天,冷冽寒风中,姚小毛走进了某家公司,开始了新一轮的面试. 一阵寒暄后. 面试官:"你好,看你的项目经验中有做过数据加密的工作,你是使用什么加密算法加解密的?" 姚小毛:"嗯,我是采用的 非对称加密 + 对称加密 的混合加密算法." 面试官:"为什么要用混合加密的方式?" 姚小毛:"非对称加密跟对称加密都各有优缺点. 非对称安全性好点,由发送方跟接收方分别持有公钥.私钥. 但是缺点是在做大数据量的加密传输时,传输速度会比较慢