zoukankan      html  css  js  c++  java
  • TiDB集群体系结构

    TiDB集群体系结构
    作者:周万春
    微信:lovemysql3306
    
    
    
    1、CAP分布式
    CAP 理论是分布式系统的一个基础理论,它描述了任何一个分布式系统最多只能满足以下三个特性中的两个:
        一致性(Consistency)
        可用性(Availability)
        分区容错性(Partition tolerance
    
    
    
    2、TiDB整体架构
    TiDB 有以下的一些优势:
        纯分布式架构,拥有良好的扩展性,支持弹性的扩缩容
        支持 SQL,对外暴露 MySQL 的网络协议,并兼容大多数 MySQL 的语法,在大多数场景下可以直接替换 MySQL
        默认支持高可用,在少数副本失效的情况下,数据库本身能够自动进行数据修复和故障转移,对业务透明
        支持 ACID 事务,对于一些有强一致需求的场景友好,例如:银行转账
        具有丰富的工具链生态,覆盖数据迁移、同步、备份等多种场景
    
    
    三大模块:每个模块都是分布式的架构。
        1、计算层(SQL)------------》TiDB
        2、分布式存储(K-V键值对)----》PD
        3、元信息系统、分布式调度-----》TiKV + TiFlash
    
    
    TiDB
        1、无状态、不存储数据
        2、接受客户端连接
        3、执行 SQL 解析和优化
        4、生成分布式执行计划
        5、数据读取请求转发给底层的存储层 TiKV
    
    
    TiKV + TiFlash
        TiKV 分布式 KV 存储(默认分布式存储引擎)。
             支持弹性的扩容和缩容。
             默认3个多副本。
             支持高可用和自动故障转移。
        TiFlash 把数据以列式的形式进行存储(是为了分析型的场景加速)。
    
    
    PD
        整个 TiDB 集群的元信息存储。
        存储每个 TiKV 节点实时的数据分布情况和集群的整体拓扑结构。
        为分布式事务分配事务 ID。
        支持高可用。
        TiKV 节点实时上报的数据分布状态。
        PD 下发数据调度命令给具体的 TiKV 节点。
    
    
    
    3、TiKV说存储
    本地存储(RocksDB)
        TiKV 的 KV 存储模型和 SQL 中的 Table 无关!
        数据保存在 RocksDB 中,再由 RocksDB 将数据落盘。
        一个TiKV里面有两个 RocksDB,一个用于存储数据,一个用于存储 Raft。
    
    
    TiKV 利用 Raft 来做数据复制,每个数据变更都会落地为一条 Raft 日志,通过 Raft 的日志复制功能,将数据安全可靠地同步到复制组的每一个节点中。
    Raft 是一个一致性协议。
    Raft 负责:
        Leader(主副本)选举
        成员变更(如添加副本、删除副本、转移 Leader 等操作)
        日志复制(通过 Raft,将数据复制到其他 TiKV 节点)
    
    数据写入:
        数据的写入是通过 Raft 这一层的接口写入,而不是直接写 RocksDB。
        数据写入 ---》Raft ---》RocksDB ---》磁盘
    
    
    Region
        对于一个 KV 系统,将数据分散在多台机器上有两种比较典型的方案:
            Hash:按照 Key 做 Hash,根据 Hash 值选择对应的存储节点。
            Range:按照 Key 分 Range,某一段连续的 Key 都保存在一个存储节点上。(TiKV选择此方案)
        连续的 key-value 划分为一个 Region,默认大小 96 M。
        TiKV:
            Region1:
                key1-value1
                key2-value2
                key3-value3
            Region2:
                key4-value4
                key5-value5
                key6-value6
        以 Region 为单位做数据的分散和复制:
            以 Region 为单位,将数据分散在集群中所有节点上,并且保证每个节点上 Region 数量尽可能相同。(此均匀分布是 PD 干的活)
            以 Region 为单位做 Raft 的数据复制和成员管理。
        TiKV 是以 Region 为单位做数据的复制,也就是一个 Region 的数据会保存多个副本,TiKV 将每一个副本叫做一个 Replica。
        Replica 之间是通过 Raft 来保持数据的一致,一个 Region 的多个 Replica 会保存在不同的节点上,构成一个 Raft Group。
        其中一个 Replica 会作为这个 Group 的 Leader,其他的 Replica 作为 Follower。
        所有的读和写都是通过 Leader 进行,读操作在 Leader 上即可完成,而写操作再由 Leader 复制给 Follower。 
    
    
    TiKV 的 MVCC 实现是通过在 Key 后面添加版本号来实现。
        Key1_Version3 -> Value
        Key1_Version2 -> Value
        Key1_Version1 -> Value
        ......
        Key2_Version4 -> Value
        Key2_Version3 -> Value
        Key2_Version2 -> Value
        Key2_Version1 -> Value
        ......
        KeyN_Version2 -> Value
        KeyN_Version1 -> Value
        ......
        通过 RocksDB 的 SeekPrefix(Key_Version) API,定位到第一个大于等于这个 Key_Version 的位置。
    
    
    分布式事务ACID
        TiKV 的事务采用的是 Google 在 BigTable 中使用的事务模型:Percolator。
        在 TiKV 层的事务 API 的语义类似下面的伪代码:
            tx = tikv.Begin()
                tx.Set(Key1, Value1)
                tx.Set(Key2, Value2)
                tx.Set(Key3, Value3)
            tx.Commit()
    
    
    
    4、TiDB谈计算
    表数据与 Key-Value 的映射关系
        TiDB 会为每个表分配一个表 ID,用 TableID 表示。表 ID 是一个整数,在整个集群内唯一。
        TiDB 会为表中每行数据分配一个行 ID,用 RowID 表示。行 ID 也是一个整数,在表内唯一。
            如果某个表有整数型的主键,TiDB 会使用主键的值当做这一行数据的行 ID。
        每行数据构成的 (Key, Value) 键值对:
            Key:   tablePrefix{TableID}_recordPrefixSep{RowID}
            Value: [col1, col2, col3, col4]
    
    
    索引数据和 Key-Value 的映射关系
        TiDB 为表中每个索引分配了一个索引 ID,用 IndexID 表示。
        对于主键和唯一索引,我们需要根据键值快速定位到对应的 RowID,因此,按照如下规则编码成 (Key, Value) 键值对:
            Key:   tablePrefix{tableID}_indexPrefixSep{indexID}_indexedColumnsValue
            Value: RowID
        对于不需要满足唯一性约束的普通二级索引,一个键值可能对应多行,我们需要根据键值范围查询对应的 RowID。 因此,按照如下规则编码成 (Key, Value) 键值对:
            Key:   tablePrefix{TableID}_indexPrefixSep{IndexID}_indexedColumnsValue_{RowID}
            Value: null
        一个表内所有的行都有相同的 Key 前缀,一个索引的所有数据也都有相同的前缀。
    
    
    元信息管理
        元信息也是以 Key-value 存储在了 TiKV 中。
        每个 Database/Table 都被分配了一个唯一的 ID。这个 ID 会编码到 Key 中,再加上 m_ 前缀。这样可以构造出一个 Key,Value 中存储的是序列化后的元信息。
        当前所有表结构信息的最新版本号,也会构成 Key-value 键值对,存储在 pd-server 内置的 etcd 中。
        其Key 为"/tidb/ddl/global_schema_version",Value 是类型为 int64 的版本号值。
        有一个后台线程在不断的检查 etcd 中存储的表结构信息的版本号是否发生变化,并且保证在一定时间内一定能够获取版本的变化。
    
    
    TiDB 的 SQL层,即 tidb-server
        负责将 SQL 翻译成 Key-Value 操作,将其转发给共用的分布式 Key-Value 存储层 TiKV,然后组装 TiKV 返回的结果,最终将查询结果返回给客户端。
        将 SQL 查询映射为对 KV 的查询,再通过 KV 接口获取对应的数据,最后执行各种计算。
        这一层的节点都是无状态的,节点本身并不存储数据,节点之间完全对等。
    
    
    
    5、PD讲调度
    作为一个分布式高可用存储系统,必须满足的需求,包括四种:
        副本数量不能多也不能少
        副本需要分布在不同的机器上
        新加节点后,可以将其他节点上的副本迁移过来
        节点下线后,需要将该节点的数据迁移走
    
    
    作为一个良好的分布式系统,需要优化的地方,包括:
        维持整个集群的 Leader 分布均匀
        维持每个节点的储存容量均匀
        维持访问热点分布均匀
        控制 Balance 的速度,避免影响在线服务
        管理节点状态,包括手动上线/下线节点,以及自动下线失效节点
    
    
    信息收集
        调度依赖于整个集群信息的收集,简单来说,我们需要知道每个 TiKV 节点的状态以及每个 Region 的状态。TiKV 集群会向 PD 汇报两类消息:
        每个 TiKV 节点会定期向 PD 汇报节点的整体信息。
        TiKV 节点(Store)与 PD 之间存在心跳包,一方面 PD 通过心跳包检测每个 Store 是否存活,以及是否有新加入的 Store;另一方面,心跳包中也会携带这个 Store 的状态信息:
            总磁盘容量
            可用磁盘容量
            承载的 Region 数量
            数据写入速度
            发送/接受的 Snapshot 数量(Replica 之间可能会通过 Snapshot 同步数据)
            是否过载
            标签信息(标签是具备层级关系的一系列 Tag)
        每个 Raft Group 的 Leader 会定期向 PD 汇报信息。
        每个 Raft Group 的 Leader 和 PD 之间存在心跳包,用于汇报这个 Region 的状态,主要包括下面几点信息:
            Leader 的位置
            Followers 的位置
            掉线 Replica 的个数
            数据写入/读取的速度
    
    PD 不断的通过这两类心跳消息收集整个集群的信息,再以这些信息作为决策的依据。
    默认是 30 分钟,如果一直没有心跳包,就认为是 Store 已经下线。
    再决定需要将这个 Store 上面的 Region 都调度走。但是有的时候,是运维人员主动将某台机器下线,这个时候,可以通过 PD 的管理接口通知 PD 该 Store 不可用,PD 就可以马上判断需要将这个 Store 上面的 Region 都调度走。
    
    调度的策略
        一个 Region 的 Replica 数量正确
            当 PD 通过某个 Region Leader 的心跳包发现这个 Region 的 Replica 数量不满足要求时,需要通过 Add/Remove Replica 操作调整 Replica 数量。出现这种情况的可能原因是:
            某个节点掉线,上面的数据全部丢失,导致一些 Region 的 Replica 数量不足
            某个掉线节点又恢复服务,自动接入集群,这样之前已经补足了 Replica 的 Region 的 Replica 数量多过,需要删除某个 Replica
            管理员调整了副本策略,修改了 max-replicas 的配置
    
        一个 Raft Group 中的多个 Replica 不在同一个位置
        副本在 Store 之间的分布均匀分配
        访问热点数量在 Store 之间均匀分配
        各个 Store 的存储空间占用大致相等
        控制调度速度,避免影响在线服务
            如果希望加快调度(比如已经停服务升级,增加新节点,希望尽快调度),那么可以通过 pd-ctl 手动加快调度速度。
        支持手动下线节点
            当通过 pd-ctl 手动下线节点后,PD 会在一定的速率控制下,将节点上的数据调度走。当调度完成后,就会将这个节点置为下线状态。
    
    
    调度的实现
        PD 不断的通过 Store 或者 Leader 的心跳包收集信息,获得整个集群的详细数据,并且根据这些信息以及调度策略生成调度操作序列,
        每次收到 Region Leader 发来的心跳包时,PD 都会检查是否有对这个 Region 待进行的操作,通过心跳包的回复消息,
        将需要进行的操作返回给 Region Leader,并在后面的心跳包中监测执行结果。
        注意这里的操作只是给 Region Leader 的建议,并不保证一定能得到执行,
        具体是否会执行以及什么时候执行,由 Region Leader 自己根据当前自身状态来定。
  • 相关阅读:
    Python中常见的文件对象内建函数
    数字整除
    Bag标签之校验
    开源硬件交流@上海新车间
    POJ 题目2774 Long Long Message(后缀数组,求最长公共子串长度)
    Linux以下的两种文件锁
    敏捷自己主动化单元測试 (从前台 JavaScript 至后台 Java)
    浅谈关于collection接口及相关容器类(一)
    上传文件 | 下载文件
    几年前再用exjts4,如今extjs5发布了,技术更新快,每次给人惊喜
  • 原文地址:https://www.cnblogs.com/zhouwanchun/p/14078412.html
Copyright © 2011-2022 走看看