引言:
现如今,不仅仅是互联网会产生海量数据,传统行业,银行,证券,甚至政企单位数据产生的速度和数量也都在急速攀升,这些企业在有大量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中
ALTER TABLE x SET TiFLASH REPLICA n
从而将需要进行OLAP业务的表在TiFlash中存放一份或者多份副本,TiKV和TiFlash在服务级别、也可以在 服务器级别进行隔离。
这样做会虽然会引入额外的存储成本空间, 却可以满足资源隔离,提升稳定性,
同时采用额外增加副本的形式,将要进行OLAP分析的表以列存的形式进行存储,这样OLAP的业务,就可以享受到列存的优势。而OLTP的业务,继续使用行存。
2、基于CBO 、RBO 分发请求
在OLAP和OLTP之间其实并没有一个明确的界限,一个sql查询执行多久算OLTP,多久算OLAP要看实际情况来看,比如在一些点查去一整列全部数据的的情形下,通过tikv反而能够更快的得到结果,同时提供更高的qps。
TiDB 优化器支持基于CBO估算查询语句时是使用TiKV还是TiFlash,还是同时选择
比如有sql
select T.*, S.a from T join S on T.b=S.b where T.a between 1 and 100
当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的设计逻辑,第一数据稳定正确性,第二易用性,第三才考虑性能。