开源分布式数据库 TiDB 架构以及 HTAP 的实现
作者: 凌云 Cloud 原文来源:https://tidb.net/blog/c11cf89a
引言:
现如今,不仅仅是互联网会产生海量数据,传统行业,银行,证券,甚至政企单位数据产生的速度和数量也都在急速攀升,这些企业在有大量 OLTP 业务的同时也会存在一些 OLAP 业务。甚至希望可以实时或者准实时消费这些数据。
Gartner 在 2014 提出了 HTAP(Hybrid Transactional and Analytical Processing)的概念,简单来说就是一套系统,既可以承载 Online Transactional Processing 业务 又可以承载 Online Analytical Processing 业务。但是,OLTP 业务,天然适合行存,OLAP 业务,天然适合列存。分析型查询经常会占用大量的 CPU 、内存 、和 IO 资源,网络带宽,必然会抢占 OLTP 的资源,如何可以做到一套系统能够应对不同场景同时又能保证的 HTAP 呢?
TiDB 作为 PingCAP 开源的关系型分布式 NewSQL 数据库,灵感来自 Google Spanner|F1 ,是一个 Key-Value 数据库,具备分布式、强一致性的多副本、水平伸缩的同时,也称自己也是一款 HTAP 的数据库,本文旨在介绍 TiDB 的架构同时,探讨 TiDB HTAP 的实现。
TiDB 架构:
TiDB
TiDB 属于集群的计算节点,参考了开源 F1 的实现,是无状态的 SQL 层,所以可以任意节点故障,兼容 MySQL 协议,负责接收客户端连接、sql 请求,也负责 SQL 语法语意解析,执行计划的解析和优化, 最终生成分布式执行计划将请求传递给存储节点。TiDB 节点类似于 RAC 的计算节点,每个节点可以进行数据读取的同时也可以进行数据写入。
PD
PD 属于集群的调度节点,集成了 etcd,但有自己的 leader,属于整个 TiDB 集群的元信息管理模块,因为可以自动 failover,不会出现单点故障, PD 在存储元数据的同时,也会根据每个 TiKV 节点上报的 region 信息、对 region 进行 split | merge,从而保证每个 region 大小相近,根据上报的数据分布、访问状态,信息,每个 TiKV 上的 region 进行调度和空间和负载的相对均衡。
TiKV
TiKV 属于集群的存储节点,存储引擎使用了 RocksDB,以 Key-Value 的形式存储数据, 比如要存放 a-z 的数据,可以按照[a,f) 为一个 region,[f-h) 为一个 region 以此类推, Region 的副本之间通过 Raft 协议来保持一致。 同时通过 key+version 让一个 key 拥有多个版本的方式来支持 MVCC。
TiFlash
TiFlash 属于集群的特殊存储节点,借助了 ClickHouse 实现,TiFlash 中的数据以列式的形式进行存储,可以自定义配置需要同步的表和副本数,由 PD 进行有选择的同步。
HTAP 实现:
为什么行存适合 OLTP
1、锁级别更低
OLTP 需要支持事务,要依赖 MVCC+LOCK, 目前主流的关系型数据库,锁的最小粒度都是行级锁, 如果事务 A 更新一条记录 id=1 的一个字段, 事务 B 想更新这一条记录,就必须要等到 A 事务提交或者回滚结束,才能进行更新。如果此时是行级锁,上锁的范围只有表 A id=1 的记录(姑且这么认为,不去发散 MDL 读锁,next-key lock、间隙锁等)。但如果事务 A 此时是列级锁,那会将锁的范围会扩大。在一个 OLTP 的系统中是不能忍受的。
2、修改或者插入 IO 调度更少
采用行存每条记录的列一般都处于一个 block 或者 page 中,进行 IO 调度时需要的次数更少
为什么列存适合 OLAP
1、压缩
相同数据类型的列存放在一起后,可以有更高的压缩比,节约存储空间的同时,一次 IO 也可以取到更多的数据。同时数据的压缩可以减少内存 buffer 的使用和磁盘的 IO 的开销,虽然会增加一定的 CPU 开销,不过增加的 CPU 开销一般都可以被内存和磁盘 IO 的减少所抵消。
2、减少不必要的数据访问
查询操作时,只会访问到需要访问的列。而不像行存一样,会将整行的数据全部取出,除非行存发生索引覆盖。
3、索引
列存,可以做到全列全索引,而不会额外增加太多的索引维护和空间存储负担,行存的 OLTP 表中,如果传入的查询条件没有索引,则会全表扫描,如果每一列都创建索引,需要巨大的维护代价。
而涉及到 cardinality 较小的列,OLAP 可以不用考虑事务,做成位图索引。 但是比如在 Oracle 中,如果使用位图索引,会有巨大的锁维护代价。
也可以通过倒排索引建立值到行的关系。
TiDB 如何实现实现 HTAP:
1、资源隔离,行存列存分离存放
OLAP 的请求,一般都会占用大量的 CPU 内存 和 IO 资源, 而 OLTP 却要求超高的并发、快速的响应、实时的数据。如果不能做到资源隔离,OLAP 必然会影响 OLTP。
TiDB 通过将 TiKV 数据有选择的同步到 TiFlash 中
从而将需要进行 OLAP 业务的表在 TiFlash 中存放一份或者多份副本,TiKV 和 TiFlash 在服务级别、也可以在 服务器级别进行隔离。
这样做会虽然会引入额外的存储成本空间, 却可以满足资源隔离,提升稳定性,
同时采用额外增加副本的形式,将要进行 OLAP 分析的表以列存的形式进行存储,这样 OLAP 的业务,就可以享受到列存的优势。而 OLTP 的业务,继续使用行存。
2、基于 CBO 、RBO 分发请求
在 OLAP 和 OLTP 之间其实并没有一个明确的界限,一个 sql 查询执行多久算 OLTP, 多久算 OLAP 要看实际情况来看,比如在一些点查去一整列全部数据的的情形下,通过 tikv 反而能够更快的得到结果,同时提供更高的 qps。
TiDB 优化器支持基于 CBO 估算查询语句时是使用 TiKV 还是 TiFlash,还是同时选择
比如有 sql
当 T 和 S 都在行 a 上存在索引,并且也都复制到了 TiFlash,最好的方法,是通过行存来访问表 T,通过列存来访问表 S。 因为 1、表 T 查询的是全部的列,同时 T 存在范围扫描,走行存开销会更低 2、表 S 走列存,只需要扫描 2 列即可。
可以通过 hint、会话级别、实例级别配置引擎隔离参数。
同时新版 TiDB 可以通过 CBO RBO 判断或者控制,是否通过 MPP 进行 data shuffle。
3、数据一致性
通过 MultiRaft 管理每个数据分片,TiFlash 作为 Raft 的 learner 角色,不用参与投票只需要被动的接受在不会影响 Raft 副本之间同步的情况下,保证了 TiFlash 和 TiKV 数据的最终一致。 通过额外存一份列存数据,采用时间换空间的形式,达到了行存服务 OLTP ,列存服务 OLAP 的目的。TiFlash 的数据副本在接受请求时,会向 Leader 副本发起校对,当进度确保至少所包含读取请求时间戳所覆盖的数据之后才响应读取,但在极端压力下,可能会出现 leaner 副本跟不上主副本的情况。
最后:
TiDB 的 HTAP,通过 Snapshot Isolation 保证 MVCC, raft 协议,把 TiFlash 节点以 Raft 的 leaner 形式额外维护一份列存数据副本,通过 hint 或者 CBO 的形式,将请求分布到 TiKV 或者 TiFlash 节点来实现。
由数据库内部来额外维护一份列存数据。对于使用者来说,可以不必额外维护同步链路,并且可以有选择的对数据进行同步、并不用担心数据最终一致性问题。 可以对用户暴露一个入口,实现 HTAP,在易用性方面做得很好。性能方面暂时还不得而知。
不过可以参考 TiDB 的设计逻辑,第一数据稳定正确性,第二易用性,第三才考虑性能。
参考 http://www.vldb.org/pvldb/vol13/p3072-huang.pdf
版权声明: 本文为 InfoQ 作者【TiDB 社区干货传送门】的原创文章。
原文链接:【http://xie.infoq.cn/article/ad975947164ecac3e97347299】。文章转载请联系作者。
评论