etcd是一个高可用的分布式键值(key-value)数据库。etcd内部采用raft协议作为一致性算法,etcd基于Go语言实现。
etcd是一个服务发现系统,具备以下的特点:
简单:安装配置简单,而且提供了HTTP API进行交互,使用也很简单
安全:支持SSL证书验证
快速:根据官方提供的benchmark数据,单实例支持每秒2k+读操作
可靠:采用raft算法,实现分布式系统数据的可用性和一致性
etcd应用场景
用于服务发现,服务发现(ServiceDiscovery)要解决的是分布式系统中最常见的问题之一,即在同一个分布式集群中的进程或服务如何才能找到对方并建立连接。
要解决服务发现的问题,需要具备下面三种必备属性。
一个强一致性、高可用的服务存储目录。
基于Ralf算法的etcd天生就是这样一个强一致性、高可用的服务存储目录。
一种注册服务和健康服务健康状况的机制。
用户可以在etcd中注册服务,并且对注册的服务配置key TTL,定时保持服务的心跳以达到监控健康状态的效果。
一种查找和连接服务的机制。
通过在etcd指定的主题下注册的服务业能在对应的主题下查找到。为了确保连接,我们可以在每个服务机器上都部署一个proxy模式的etcd,这样就可以确保访问etcd集群的服务都能够互相连接。
k8s的etcd的原理分析
k8s集群使用etcd作为它的数据后端,etcd是一种无状态的分布式数据存储集群. 数据以key-value的形式存储在其中. 今天同事针对etcd集群的运作原理做了一个讲座,总结一下.
A. etcd 数据的组织形式
etcd的API分为两种, 分别用export ETCDCTL_API=3和export ETCDCTL_API=2来区分. 两种API的调用接口不同, 其数据组织形式也不同. API_2下,其key和value都存储在内存中.
而API_3下,key存储在内存中,value存储在硬盘中. 显然, API_3更有优势,因为key是相较于value来说要短小的多. 这里我们讨论的是更为常用的API_3下的数据组织.
在etcd中,key以B树的形式存储在内存中, value以B+树的形式存储在硬盘中. 为什么要以B/B+树的形式来存储呢? 这涉及到一个所有的数据系统都要面对的问题, 如何花更少的时间
将数据从硬盘中读取出来. 众所周知, 计算机的存储体系里, cache> 内存>>> 磁盘, 也就是说对于etcd来说,访问一个数据最大的时间消耗在磁盘访问. 那么就要想方设法降低访问磁盘的
次数. 这个时候B/B+树的优势就体现出来了. 下面详细分析一下.
B/B+树模型的源头是AVL(二叉平衡树). 对于AVL来说, 它每一个节点只存储一个数据, 因此对于一个很庞大的AVL树来说, 访问一个数据的时间复杂度是log2 n. 这里n是这棵AVL树存储的数据总数. 假设有一个数据总量为1023的AVL, 访问某个数据最坏的情况下需要访问10个节点. 由于AVL树的节点之间不像数元素在内存中连续存储, 这10次节点访问操作
很有可能包含多次磁盘访问. 因此拖慢了访问速度. 而对于B/B+树来说, 设计者将每一个节点的大小设置为内存一个分页的大小(一般是4kb), 而内存的一个分页的大小又等同于磁盘一个数据块的大小.因此, B/B+树相对于AVL来说的优势在于,它在硬盘中读取数据时, 单位是4kb的数据块而不是单个数据. 这样, 它将数据块读取到内存中后再进一步查找,从而大大减少了磁盘I/O的次数.
关于B/B+树在数据库系统应用中更为详细的介绍网上有很多相关资料.不再赘述.至于B树和B+树的区别,B+树只在叶子节点中存储data, 在非叶子节点中只存储search_key, B树在非叶子节点中存储的就是真正的数据.
B. etcd中如何存储一个key-value
了解了B/B+树的概念后, 我们分析一下etcd如何将数据存储到硬盘中. 首先,etcd中有个概念叫revision, 这个revision可以理解为是一个全局变量. 用户每次执行一个操作, 例如插入一个key-pair, 这个revision就会自增1, 可以理解为这个revision就是一个全局的ID,表示已经执行了多少次操作, 每一次操作都有唯一的revision来识别. 对于内存中的B树来说, 它在进行查找时所使用的search-key是etcd key, 节点中存储的就是revision信息.
而硬盘中存储的B+树的search-key就是revision值, 其节点中存储的是etcd key和etcd value. 通过这样的组织结构, etcd做到了保存每一个key 的每一个历史记录.
至此,我们可以梳理一下etcd查找关键字,例如"spe",的过程, 首先etcd根据"spe"去内存中遍历B树, 找到这个key所对应的revision, 这里revision是一组数字,包含了"spe"的每一次修改. 从这一组revision中找到最大的那一个,如果用户指定了某个revision的话, 那么就取出用户指定的那个. 然后拿着revision去硬盘中查找B+树, 依次将节点读入内存进行查找.直至到达叶子节点,并且最终找到想要的值.