赞
踩
OLTP(on-line transaction processing),联机事务处理,对数据的「增删改」
OLAP(On-Line Analytical Processing),联机分析处理,对数据的「查询」
ROLAP与MOLAP
ROLAP关系型联机分析处理
传统关系型数据库、MPP分布式数据库、基于Hadoop的Spark/Impala
能同时连接明细数据和汇总数据,实时根据用户提出的需求对数据进行计算后返回给用户,所以用户使用相对比较灵活,可以随意选择维度组合来进行实时计算
当计算的数据量达到一定级别或并发数达到一定级别的时候,一定会出现性能问题
MOLAP多维联机分析处理
OLTP所产生的业务数据分散在不同的业务系统中,而OLAP往往需要将不同的业务数据集中到一起进行统一综合的分析,这时候就需要根据业务分析需求做对应的数据清洗后存储在数据仓库中,然后由数据仓库来统一提供OLAP分析。
HTAP是混合 OLTP 和 OLAP 业务同时处理的系统,它打破了事务处理和分析之间的“墙”。它支持更多的信息和“实时业务”的决策
优势:
TiDB 是 PingCAP 公司自主设计、研发的开源分布式关系型数据库,是一款同时支持在线事务处理与在线分析处理 (Hybrid Transactional and Analytical Processing, HTAP) 的融合型分布式数据库产品,具备水平扩容或者缩容、金融级高可用、实时 HTAP、云原生的分布式数据库、兼容 MySQL 5.7 协议和 MySQL 生态等重要特性。目标是为用户提供一站式 OLTP (Online Transactional Processing)、OLAP (Online Analytical Processing)、HTAP 解决方案。TiDB 适合高可用、强一致要求较高、数据规模较大等各种应用场景。
TiDB Server
TiDB Server 负责接收 SQL 请求,处理 SQL 相关的逻辑,并通过 PD 找到存储计算所需数据的 TiKV 地址,与 TiKV 交互获取数据,最终返回结果。 TiDB Server 是无状态的,其本身并不存储数据,只负责计算,可以无限水平扩展,可以通过负载均衡组件(如LVS、HAProxy 或 F5)对外提供统一的接入地址。
PD Server
Placement Driver (简称 PD) 是整个集群的管理模块,其主要工作有三个: 一是存储集群的元信息(某个 Key 存储在哪个 TiKV 节点);二是对 TiKV 集群进行调度和负载均衡(如数据的迁移、Raft group leader 的迁移等);三是分配全局唯一且递增的事务 ID。 PD 是一个集群,需要部署奇数个节点,一般线上推荐至少部署 3 个节点。
TiKV Server
TiKV Server 负责存储数据,从外部看 TiKV 是一个分布式的提供事务的 Key-Value 存储引擎。存储数据的基本单位是 Region,每个 Region 负责存储一个 Key Range (从 StartKey 到 EndKey 的左闭右开区间)的数据,每个 TiKV 节点会负责多个 Region 。TiKV 使用 「Raft」 协议做复制,保持数据的一致性和容灾。副本以 Region 为单位进行管理,不同节点上的多个 Region 构成一个 Raft Group,互为副本。数据在多个 TiKV 之间的负载均衡由 PD 调度,这里也是以 Region 为单位进行调度。
TiDB 的设计目标是 100% 的 OLTP 场景和 80% 的 OLAP 场景,更复杂的 OLAP 分析可以通过 TiSpark 项目来完成。 TiDB 对业务没有任何侵入性,能优雅的替换传统的数据库中间件、数据库分库分表等 Sharding 方案。同时它也让开发运维人员不用关注数据库 Scale 的细节问题,专注于业务开发,极大的提升研发的生产力。
无限水平扩展是 TiDB 的一大特点,这里说的水平扩展包括两方面:计算能力和存储能力。
TiDB Server 负责处理 SQL 请求,随着业务的增长,可以简单的添加 TiDB Server 节点,提高整体的处理能力,提供更高的吞吐。
TiKV 负责存储数据,随着数据量的增长,可以部署更多的 TiKV Server 节点解决数据 Scale 的问题。
PD 会在 TiKV 节点之间以 Region 为单位做调度,将部分数据迁移到新加的节点上。所以在业务的早期,可以只部署少量的服务实例(推荐至少部署 3 个 TiKV, 3 个 PD,2 个 TiDB),随着业务量的增长,按照需求添加 TiKV 或者 TiDB 实例。
高可用是 TiDB 的另一大特点,TiDB/TiKV/PD 这三个组件都能容忍部分实例失效,不影响整个集群的可用性。下面分别说明这三个组件的可用性、单个实例失效后的后果以及如何恢复。
TiDB 是无状态的,推荐至少部署两个实例,前端通过负载均衡组件对外提供服务。当单个实例失效时,会影响正在这个实例上进行的 Session,从应用的角度看,会出现单次请求失败的情况,重新连接后即可继续获得服务。单个实例失效后,可以重启这个实例或者部署一个新的实例。
PD 是一个集群,通过 Raft 协议保持数据的一致性,单个实例失效时,如果这个实例不是 Raft 的 leader,那么服务完全不受影响;如果这个实例是 Raft 的 leader,会重新选出新的 Raft leader,自动恢复服务。PD 在选举的过程中无法对外提供服务,这个时间大约是3秒钟。推荐至少部署三个 PD 实例,单个实例失效后,重启这个实例或者添加新的实例。
TiKV 是一个集群,通过 Raft 协议(raft一致性哈算法以及Raft 为什么是更易理解的分布式一致性算法 )保持数据的一致性(副本数量可配置,默认保存三副本),并通过 PD 做负载均衡调度。
单个节点失效时,会影响这个节点上存储的所有 Region。对于 Region 中的 Leader 结点,会中断服务,等待重新选举;对于 Region 中的 Follower 节点,不会影响服务。当某个 TiKV 节点失效,并且在一段时间内(默认 30 分钟)无法恢复,PD 会将其上的数据迁移到其他的 TiKV 节点上。
TiDB选用的是Key-Value作为存储模型,并且提供有序遍历的方法,在这个巨大的Map中,Key按照Byte舒张压总的原始二进制比特位比较顺序排列,所以我们可以Seek到某一个Key的位置,然后不断调用Next方法以递增的顺序获取比这个Key大的Key-Value。
TiDB选用的是RocksDB作为存储引擎,而不是直接向磁盘上写数据。RocksDB是一个高性能的单机存储引擎(可以认为是一个单机的Key-Value Map),由Facebook的团队在做持续的优化。
TiDB选用Raft协议来做数据复制,保证副本的一致性。
数据的写入是通过Raft的接口写入,然后再通过RocksDB存储到磁盘上。
Raft 是一个一致性协议,提供几个重要的功能:
对于一个KV系统,将数据分散在多台机器上有两种典型方案
TiDB采用的是第二种方式,将整个 Key-Value 空间分成很多段,每一段是一系列连续的 Key,我们将每一段叫做一个 Region,并且我们会尽量保持每个 Region 中保存的数据不超过一定的大小(这个大小可以配置,目前默认是 96mb)。每一个 Region 都可以用 StartKey 到 EndKey 这样一个左闭右开区间来描述。
将数据划分为Region后:
MVCC即多版本控制,当两个Client同时修改一个Key的Value时,如果没有MVCC,就需要对数据进行上锁,在分布式的情况下很可能会带来性能和死锁问题。
TiDB实现MVCC的方式是通过在key后面添加version来实现的。对于同一个Key的多个版本,TiDB把版本号大的放在前面,版本号小的放在后面,当用户通过Key+Version获取Value时,可以直接定位到第一个大于等于这个Key-Version的位置。
TiDB采用 Percolator 作为事务模型,并作了大量的优化。TiKV的事务采用乐观锁,在事务的执行过程中,不会检测写写冲突,只有在提交过程中,才会做冲突检测,冲突双方较早完成提交的会写入成功,另一方会尝试重新执行整个事务。
在写入冲突不严重的情况下,这种模型的性能会很好,但如果存在大量的写入冲突,性能就会变得很差。
对于一个 Table 来说,需要存储的数据包括三部分:
TiDB 面向的首要目标是 OLTP 业务,这类业务需要支持快速地读取、保存、修改、删除一行数据,所以采用行存是比较合适的。
对于 Index,TiDB 不止需要支持 Primary Index,还需要支持 Secondary Index。Index 的作用的辅助查询,提升查询性能,以及保证某些 Constraint。查询的时候有两种模式,一种是点查,比如通过 Primary Key 或者 Unique Key 的等值条件进行查询;另一种是 Range 查询。Index 还分为 Unique Index 和 非 Unique Index,这两种都需要支持。
TiDB 对每个表分配一个「 TableID」,每一个索引都会分配一个 「IndexID」,每一行分配一个 「RowID」(如果表有整数型的 Primary Key,那么会用 Primary Key 的值当做 RowID),其中 TableID 在整个集群内唯一,IndexID/RowID 在表内唯一,这些 ID 都是 int64 类型。
每行数据
Key: tablePrefix{tableID}_recordPrefixSep{rowID}
Value: [col1, col2, col3, col4]
Unique Index
Key: tablePrefix{tableID}_indexPrefixSep{indexID}_indexedColumnsValue
Value: rowID
非 Unique Index:可能有多行数据的 ColumnsValue
是一样的,所以再加上rowID使其唯一
Key: tablePrefix{tableID}_indexPrefixSep{indexID}_indexedColumnsValue_rowID
Value: null
其中 Key 的 Prefix
都是特定的字符串常量,用于在 KV 空间内区分其他数据。
Memcomparable:
保证编码前和编码后的比较关系不变的方案,对于任何类型的值,两个对象编码前的原始类型比较结果,和编码成 byte 数组后的比较结果保持一致。采用这种编码后,一个表的所有 Row 数据就会按照 RowID 的顺序排列在 TiKV 的 Key 空间中,某一个 Index 的数据也会按照 Index 的 ColumnValue 顺序排列在 Key 空间内。
Database/Table 都有元信息,也就是其定义以及各项属性。每个 Database/Table 都被分配了一个唯一的 ID,这个 ID 作为唯一标识,并且在编码为 Key-Value 时,这个 ID 都会编码到 Key 中,再加上 m_
前缀。这样可以构造出一个 Key,Value 中存储的是序列化后的元信息。
将 SQL 查询映射为对 KV 的查询,再通过 KV 接口获取对应的数据,最后执行各种计算。首先我们需要将计算尽量靠近存储节点,以避免大量的 RPC 调用。其次,我们需要将 Filter 也下推到存储节点进行计算,这样只需要返回有效的行,避免无意义的网络传输。最后,我们可以将聚合函数、GroupBy 也下推到存储节点,进行预聚合,每个节点只需要返回一个 Count 值即可,再由 tidb-server 将 Count 值 Sum 起来。
用户的 SQL 请求会直接或者通过 Load Balancer 发送到 tidb-server,tidb-server 会解析 MySQL Protocol Packet,获取请求内容,然后做语法解析、查询计划制定和优化、执行查询计划获取和处理数据。数据全部存储在 TiKV 集群中,所以在这个过程中 tidb-server 需要和 tikv-server 交互,获取数据。最后 tidb-server 需要将查询结果返回给用户。
作为一个分布式高可用存储系统,必须满足的需求,包括四种:
作为一个良好的分布式系统,需要优化的地方,包括:
增加一个 Replica
删除一个 Replica
将 Leader 角色在一个 Raft Group 的不同 Replica 之间 transfer
刚好 Raft 协议能够满足这三种需求,通过 AddReplica、RemoveReplica、TransferLeader 这三个命令,可以支撑上述三种基本操作。
调度依赖于整个集群信息的收集,包括每个 TiKV 节点的状态以及每个 Region 的状态。
每个 TiKV 节点会定期向 PD 汇报节点的整体信息
TiKV 节点(Store)与 PD 之间存在心跳包,一方面 PD 通过心跳包检测每个 Store 是否存活,以及是否有新加入的 Store;另一方面,心跳包中也会携带这个 Store 的状态信息 ,主要包括:
每个 Raft Group 的 Leader 会定期向 PD 汇报信息
每个 Raft Group 的 Leader 和 PD 之间存在心跳包,用于汇报这个 Region 的状态 ,主要包括下面几点信息:
Leader 的位置
Followers 的位置
掉线 Replica 的个数
数据写入/读取的速度
PD 不断的通过这两类心跳消息收集整个集群的信息,再以这些信息作为决策的依据。除此之外,PD 还可以通过管理接口接受额外的信息,用来做更准确的决策。比如当某个 Store 的心跳包中断的时候,PD 并不能判断这个节点是临时失效还是永久失效,只能经过一段时间的等待(默认是 30 分钟),如果一直没有心跳包,就认为是 Store 已经下线,再决定需要将这个 Store 上面的 Region 都调度走。
但是有的时候,是运维人员主动将某台机器下线,这个时候,可以通过 PD 的管理接口通知 PD 该 Store 不可用,PD 就可以马上判断需要将这个 Store 上面的 Region 都调度走。
一个 Region 的 Replica 数量正确
当 PD 通过某个 Region Leader 的心跳包发现这个 Region 的 Replica 数量不满足要求时,需要通过 Add/Remove Replica 操作调整 Replica 数量。出现这种情况的可能原因是:
一个 Raft Group 中的多个 Replica 不在同一个位置
在一般情况下,PD 只会保证多个 Replica 不落在一个节点上,以避免单个节点失效导致多个 Replica 丢失。在实际部署中,还可能出现下面这些需求:
多个节点部署在同一台物理机器上
TiKV 节点分布在多个机架上,希望单个机架掉电时,也能保证系统可用性
TiKV 节点分布在多个 IDC 中,希望单个机房掉电时,也能保证系统可用
这些需求本质上都是某一个节点具备共同的位置属性,构成一个最小的容错单元,我们希望这个单元内部不会存在一个 Region 的多个 Replica。这个时候,可以给节点配置 lables 并且通过在 PD 上配置 location-labels 来指明哪些 lable 是位置标识,需要在 Replica 分配的时候尽量保证不会有一个 Region 的多个 Replica 所在结点有相同的位置标识。
副本在 Store 之间的分布均匀分配
每个副本中存储的数据容量上限是固定的,维持每个节点上面,副本数量的均衡,会使得总体的负载更均衡。
Leader 数量在 Store 之间均匀分配
Raft 协议要读取和写入都通过 Leader 进行,所以计算的负载主要在 Leader 上面,PD 会尽可能将 Leader 在节点间分散开。
访问热点数量在 Store 之间均匀分配
每个 Store 以及 Region Leader 在上报信息时携带了当前访问负载的信息,比如 Key 的读取/写入速度。PD 会检测出访问热点,且将其在节点之间分散开。
各个 Store 的存储空间占用大致相等
每个 Store 启动的时候都会指定一个 Capacity 参数,表明这个 Store 的存储空间上限,PD 在做调度的时候,会考虑节点的存储空间剩余量。
控制调度速度,避免影响在线服务
调度操作需要耗费 CPU、内存、磁盘 IO 以及网络带宽,我们需要避免对线上服务造成太大影响。PD 会对当前正在进行的操作数量进行控制,默认的速度控制是比较保守的,如果希望加快调度(比如已经停服务升级,增加新节点,希望尽快调度),那么可以通过 pd-ctl 手动加快调度速度。
支持手动下线节点
当通过 pd-ctl 手动下线节点后,PD 会在一定的速率控制下,将节点上的数据调度走。当调度完成后,就会将这个节点置为下线状态。
TiDB 支持包括跨行事务,JOIN 及子查询在内的绝大多数 MySQL 的语法。一些 MySQL 语法在 TiDB 中可以解析通过,但是不会做任何后续的处理。
不支持的特性:
参考链接:
https://blog.miuyun.work/archives/18860046
https://www.zhihu.com/question/24110442/answer/851671343
https://blog.csdn.net/weixin_34112030/article/details/92512973
https://zhuanlan.zhihu.com/p/71073707
https://docs.pingcap.com/zh/tidb/stable
如有不对,烦请指出,感谢!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。