美文网首页
ETCD——基础原理

ETCD——基础原理

作者: DevilRoshan | 来源:发表于2020-11-07 19:07 被阅读0次

    "A distributed, reliable key-value store for the most critical data of a distributed system."

    一个分布式、可靠 key-value 存储分布式系统。

    应用场景

    • 配置管理
    • 服务注册发现
    • 选主
    • 应用调度
    • 分布式队列
    • 分布式锁

    架构

    ETCD.png
    • biltdb:Bolt是一个纯粹Key/Value模型的程序。该项目的目标是为不需要完整数据库服务器(如Postgres或MySQL)的项目提供一个简单,快速,可靠的数据库;
    • Wal:预写式日志,etcd用于持久化存储的日志格式;
    • snapshot:etcd防止WAL文件过多而设置的快照,存储etcd数据状态;
    • Raft:etcd所采用的保证分布式系统强一致性的算法。

    一个ETCD集群一般由3个或者5个节点组成,两个quorum一定存在交集,则
    quorum=(n+1)/2
    即:3个节点容忍1个节点故障,5个节点容忍2个节点故障,以此类推。

    etcd功能.png

    主要APIs

    • Put(key, value) / Delete(key): 写入 / 删除数据
    • Get(key) / Get(keyFrom, keyEnd): 查询操纵 / 范围查询
    • Watch(key / keyPrefix): 根据key来watch / 根据前缀Watch(实际应用场景一般使用这种)
    • Transactions(if / then / else ops) . Commit(): 事务操作
    • Lease: Grant / Revoke / KeepAlive: Lease接口

    etcd数据版本号机制

    • term:Leader任期,Leader切换时,全局单调递增, 64bits
    • revision:数据版本,数据发生变更,全局单调递增,64bits
    • KeyValue:
      • create_revision
      • mod_revision
      • version

    etcd MVCC和stream watch

    Put(key, value1) rev = 5
    Put(key, value2) rev = 6
    Get(key) --> value2
    Get(key, rev=5) --> value1
    ...
    

    默认为最新版本,可以指定版本号

    watcher := Watch(key, rev)
    for {
        event := watcher.Recv()
        handle(event)
        ...
    }
    

    指定旧版本,可以拿到从旧版本到当前的所有的数据版本更新

    mvcc.png

    首先,所有的数据都保存在B+树(灰色),当我们指定了版本信息之后,会直接到灰色B+树中去获取相关的数据;同时,还有另外一个B+树,它维护了key和revions的映射关系,查询key的数据时候,会根据key查询到revision,再通过revision查询到相应的key。

    • 一个数据多个版本;
    • 通过定期的Compaction来清理历史数据。(见附)

    etcd mini-transactions

    Txn.If(
        Compare(Value(key1),">", "bar"),
        Compare(Version(key1),"=", 2),
        ...
    ).Then(
        Put(key2, valueX)
        Delete(key3)
        ...
    ).Else(
        Put(key2, valueY)
        ...
    ).Commit()
    
    • 当key1的值大于“bar”,且key1的版本等于2的时候,将key2的值设为valueX,并删除key3;
    • 否则,将key2的值设为valueY;
    • 提交。

    etcd Lease 的概念和用法

    • lease = CreateLease(10s)
    • Put(key1, value1, lease)
    • Put(key2, value2, lease)
    • ......
    • lease.KeepAlive():续约。
    • lease.Revoke():清理。

    租约,检测一个节点是否存活。key1和key2绑定到lease租约上。

    将多个key绑定到同一个lease对象之上,大幅提高etcd性能。

    如何保证一致性?

    etcd 使用 raft 协议来维护集群内各个节点状态的一致性。简单说,etcd 集群是一个分布式系统,由多个节点相互通信构成整体对外服务,每个节点都存储了完整的数据,并且通过 Raft 协议保证每个节点维护的数据是一致的。

    每个 etcd 节点都维护了一个状态机,并且,任意时刻至多存在一个有效的主节点。主节点处理所有来自客户端写操作,通过 Raft 协议保证写操作对状态机的改动会可靠的同步到其他节点。

    数据模型

    etcd 的设计目标是用来存放非频繁更新的数据,提供可靠的 Watch插件,它暴露了键值对的历史版本,以支持低成本的快照、监控历史事件。这些设计目标要求它使用一个持久化的、多版本的、支持并发的数据数据模型。

    当 etcd 键值对的新版本保存后,先前的版本依然存在。从效果上来说,键值对是不可变的,etcd 不会对其进行 in-place 的更新操作,而总是生成一个新的数据结构。为了防止历史版本无限增加,etcd 的存储支持压缩(Compact)以及删除老旧版本。

    逻辑视图

    从逻辑角度看,etcd 的存储是一个扁平的二进制键空间,键空间有一个针对键(字节字符串)的词典序索引,因此范围查询的成本较低。

    键空间维护了多个修订版本(Revisions),每一个原子变动操作(一个事务可由多个子操作组成)都会产生一个新的修订版本。在集群的整个生命周期中,修订版都是单调递增的。修订版同样支持索引,因此基于修订版的范围扫描也是高效的。压缩操作需要指定一个修订版本号,小于它的修订版会被移除。

    一个键的一次生命周期(从创建到删除)叫做 “代 (Generation)”,每个键可以有多个代。创建一个键时会增加键的版本(version),如果在当前修订版中键不存在则版本设置为1。删除一个键会创建一个墓碑(Tombstone),将版本设置为0,结束当前代。每次对键的值进行修改都会增加其版本号 — 在同一代中版本号是单调递增的。

    当压缩时,任何在压缩修订版之前结束的代,都会被移除。值在修订版之前的修改记录(仅仅保留最后一个)都会被移除。

    物理视图

    etcd 将数据存放在一个持久化的 B+ 树中,处于效率的考虑,每个修订版仅仅存储相对前一个修订版的数据状态变化(Delta)。单个修订版中可能包含了 B+ 树中的多个键。

    键值对的键,是三元组(major,sub,type)

    • major:存储键值的修订版;
    • sub:用于区分相同修订版中的不同键;
    • type:用于特殊值的可选后缀,例如 t 表示值包含墓碑。

    键值对的值,包含从上一个修订版的 Delta。B+ 树 —— 键的词法字节序排列,基于修订版的范围扫描速度快,可以方便的从一个修改版到另外一个的值变更情况查找。

    etcd 同时在内存中维护了一个 B 树索引,用于加速针对键的范围扫描。索引的键是物理存储的键面向用户的映射,索引的值则是指向 B+ 树修该点的指针。

    典型使用场景

    元数据存储——Kubernetes

    • 元数据高可用,无单点故障;
    • 系统无状态,故障修复方案简单;
    • 系统可水平扩展,提高性能及容量;
    • 简化架构实现,降低系统工程复杂性。

    Service Discovery(Name Service)

    • 资源注册;
    • 存活性检测;
    • API Gateway无状态,可水平扩展;
    • 支持上万个进程的规模。

    Distributed Coordination: Leader Election

    • 分部署系统设计模式(多个master选举一个leader对外服务)
      1. 选主;
      2. 注册IP;
      3. 获取主节点地址。
    • 分布式系统并发控制
      • 分布式信号量;
      • 自动踢出故障节点;
      • 存储进程执行状态。

    1.--auto-compaction-retention

    由于ETCD数据存储多版本数据,随着写入的主键增加历史版本需要定时清理,默认的历史数据是不会清理的,数据达到2G就不能写入,必须要清理压缩历史数据才能继续写入;

    所以根据业务需求,在上生产环境之前就提前确定,历史数据多长时间压缩一次;例如生产环境现在升级后是默认一小时压缩一次数据。这样可以极大的保证集群稳定,减少内存和磁盘占用。

    2.--max-request-bytes

    etcd Raft消息最大字节数,ETCD默认该值为1.5M; 但是很多业务场景发现同步数据的时候1.5M完全没法满足要求,所以提前确定初始值很重要; 由于1.5M导致我们线上的业务无法写入元数据的问题,

    例如升级之后把该值修改为默认32M,但是官方推荐的是10M,可以根据业务情况自己调整。

    3.--quota-backend-bytes

    ETCDdb数据大小,默认是2G,当数据达到2G的时候就不允许写入,必须对历史数据进行压缩才能继续写入;参加1里面说的,启动的时候就应该提前确定大小,官方推荐是8G。

    /usr/bin/etcd --auto-compaction-retention '1' --max-request-bytes '33554432' --quota-backend-bytes '8589934592'
    

    相关文章

      网友评论

          本文标题:ETCD——基础原理

          本文链接:https://www.haomeiwen.com/subject/npjwvktx.html