第十二期技术夜校分享嘉宾是DBA大咖——Xiaoyu
他拥有10年+互联网数据库运维经验、在游戏、电商、OTA行业从事过DBA运维工作、在大规模数据库自动化、平台化方面有较资深的落地经验。
市场上有很多数据库产品,如Oracle、MySQL、SQLServer、NoSQL、NewSQL等,那么目前数据库圈最火的分布式关系型数据库之一TiDB你了解吗?相信很多同学以前听说过TiDB,也知道是一款国人研发的数据库,但你知道TiDB到底是如何实现的?它跟其他数据库产品相比,它的核心优势是什么?
此次夜校分享,xiaoyu向大家介绍了数据库发展史、TiDB 设计、架构及生态及TiDB在得物的应用。
2008年以前
2008 年以前应用最为广泛的是单机关系型数据库(SQL),能很好的解决复杂的数据运算及表间处理,多用于银行、电信等传统行业复杂业务逻辑场景中,以 Oracle 为代表。此类数据库挑战在于成本高,随着数据量增加,只能通过购买更贵更好的服务器;无法线性扩容,海量数据下处理能力大幅下降。
2008年至2013年
2008年至2013年,随着搜索/社交的发展,数据量爆发增长,传统数据库高成本,无法线性扩容问题日益突显;分布式及分布式非关系型(NoSQL)开始快速发展,如 MongoDB,HBase。但此类数据库的局限在于无法处理交易类数据及复杂业务逻辑的特性,限制其在非互联网领域的发展。
2013年以后
2013年以来,有个新的概念为分布式关系型数据库(NewSQL),它是兼具NoSQL扩展性又不丧失传统关系型数据库ACID特性的分布式数据库。随着互联网向银行、电信、电力等方向的渗透,传统行业数据量迅速提升,需要同时满足低成本、线性扩容及能够处理交易类事务的新型数据库,大数据的存储刚需不可避免。NewSQL的挑战在于,它是基于 Google Spanner/F1 论文,未开源它的代码及技术细节,是基础软件最前沿的领域之一,技术门槛最高。NewSQL 代表产品有Spanner/F1(未开源)、CockroachDB(开源)和TiDB(开源)。
与传统的单机数据库相比,TiDB 具有以下优势:
-
纯分布式架构,拥有良好的扩展性,支持弹性的扩缩容
-
支持 SQL,对外暴露 MySQL 的网络协议,并兼容大多数 MySQL 的语法,在大多数场景下可以直接替换 MySQL
-
默认支持高可用,在少数副本失效的情况下,数据库本身能够自动进行数据修复和故障转移,对业务透明
-
支持 ACID 事务,对于一些有强一致需求的场景友好,例如:银行转账
-
具有丰富的工具链生态,覆盖数据迁移、同步、备份等多种场景
图|TiDB整体架构
TiDB Server
SQL 层,对外暴露 MySQL 协议的连接 endpoint,负责接受客户端的连接,执行 SQL 解析和优化,最终生成分布式执行计划。TiDB 层本身是无状态的,实践中可以启动多个 TiDB 实例,通过负载均衡组件(如 LVS、HAProxy 或 F5)对外提供统一的接入地址,客户端的连接可以均匀地分摊在多个 TiDB 实例上以达到负载均衡的效果。TiDB Server 本身并不存储数据,只是解析 SQL,将实际的数据读取请求转发给底层的存储节点 TiKV(或 TiFlash)。
PD Server
整个 TiDB 集群的元信息管理模块,负责存储每个 TiKV 节点实时的数据分布情况和集群的整体拓扑结构,提供 TiDB Dashboard 管控界面,并为分布式事务分配事务 ID。PD 不仅存储元信息,同时还会根据 TiKV 节点实时上报的数据分布状态,下发数据调度命令给具体的 TiKV 节点,可以说是整个集群的“大脑”。此外,PD 本身也是由至少 3 个节点构成,拥有高可用的能力。建议部署奇数个 PD 节点。
TiKV Server
负责存储数据,从外部看 TiKV 是一个分布式的提供事务的 Key-Value 存储引擎。存储数据的基本单位是 Region,每个 Region 负责存储一个 Key Range(从 StartKey 到 EndKey 的左闭右开区间)的数据,每个 TiKV 节点会负责多个 Region。TiKV 的 API 在 KV 键值对层面提供对分布式事务的原生支持,默认提供了 SI (Snapshot Isolation) 的隔离级别,这也是 TiDB 在 SQL 层面支持分布式事务的核心。TiDB 的 SQL 层做完 SQL 解析后,会将 SQL 的执行计划转换为对 TiKV API 的实际调用。所以,数据都存储在 TiKV 中。另外,TiKV 中的数据都会自动维护多副本(默认为三副本),天然支持高可用和自动故障转移。
TiKV如何完成自动扩容?步骤如下:
1、比如当前的架构是4台 TiKV 节点,假设其中 Node1 的负载较高,我们要把 Region1 迁移出到新节点。
2、此时我们加入一台 Node5用于扩容集群
3、此时 PD 将Region1 的 leader 迁移至 Node2
4、在新的 Node5上新增一个 Region1的副本,复制完成后会将 Leader 角色迁移至 Node5。
5、然后将 Node1上的 Region1 删除掉,本次扩容就结束了。
TiKV - 异地多数据中心
两地三中心架构,即生产数据中心、同城灾备中心、异地灾备中心的高可用容灾方案。在这种模式下,两个城市的三个数据中心互联互通,如果一个数据中心发生故障或灾难,其他数据中心可以正常运行并对关键业务或全部业务实现接管。相比同城多中心方案,两地三中心具有跨城级高可用能力,可以应对城市级自然灾害。
TiDB 分布式数据库通过 Raft 算法原生支持两地三中心架构的建设,并保证数据库集群数据的一致性和高可用性。而且因同城数据中心网络延迟相对较小,可以把业务流量同时派发到同城两个数据中心,并通过控制 Region Leader 和 PD Leader 分布实现同城数据中心共同负载业务流量的设计。
该架构具备高可用能力,同时通过 PD 调度限制了 Region Leader 尽量只出现在同城的两个数据中心,这相比于三数据中心,即 Region Leader 分布不受限制的方案有以下优缺点:
优点:
-
Region Leader 都在同城低延迟机房,数据写入速度更优;
-
两中心可同时对外提供服务,资源利用率更高;
-
可保证任一数据中心失效后,服务可用并且不发生数据丢失。
缺点:
-
因为数据一致性是基于 Raft 算法实现,当同城两个数据中心同时失效时,因为异地灾备中心只剩下一份副本,不满足 Raft 算法大多数副本存活的要求。最终将导致集群暂时不可用,需要从一副本恢复集群,只会丢失少部分还没同步的热数据。这种情况出现的概率是比较小的;
-
由于使用到了网络专线,导致该架构下网络设施成本较高;
-
两地三中心需设置 5 副本,数据冗余度增加,增加空间成本。
SQL 计算层
关系模型到KV的映射
TiDB 自动将 SQL 结构映射为 KV 结构。简单来说,TiDB 执行了以下操作:
一行数据映射为一个 KV,Key 以 TableID 构造前缀,以行 ID 为后缀
一条索引映射为一个 KV,Key 以 TableID+IndexID 构造前缀,以索引值构造后缀
可以看到,对于一个表中的数据或者索引,会具有相同的前缀,这样在 TiKV 的 Key 空间内,这些 Key-Value 会在相邻的位置。那么当写入量很大,并且集中在一个表上面时,就会造成写入的热点,特别是连续写入的数据中某些索引值也是连续的(比如 update time 这种按时间递增的字段),会在很少的几个 Region 上形成写入热点,成为整个系统的瓶颈。同样,如果所有的数据读取操作也都集中在很小的一个范围内 (比如在连续的几万或者十几万行数据上),那么可能造成数据的访问热点。
二级索引
TiDB 支持完整的二级索引,并且是全局索引,很多查询可以通过索引来优化。如果利用好二级索引,对业务非常重要,很多 MySQL 上的经验在 TiDB 这里依然适用,不过 TiDB 还有一些自己的特点,需要注意,这一节主要讨论在 TiDB 上使用二级索引的一些注意事项。
二级索引是否越多越好?
二级索引能加速查询,但是要注意新增一个索引是有副作用的,上一节介绍了索引的存储模型,那么每增加一个索引,在插入一条数据的时候,就要新增一个 Key-Value,所以索引越多,写入越慢,并且空间占用越大。另外过多的索引也会影响优化器运行时间,并且不合适的索引会误导优化器。所以索引并不是越多越好。
对哪些列建索引比较合适?
上文提到,索引很重要但不是越多越好,因此需要根据具体的业务特点创建合适的索引。原则上需要对查询中需要用到的列创建索引,目的是提高性能。下面几种情况适合创建索引:
区分度比较大的列,通过索引能显著地减少过滤后的行数
有多个查询条件时,可以选择组合索引,注意需要把等值条件的列放在组合索引的前面。
这里举一个例子,假设常用的查询是 select * from t where c1 = 10 and c2 = 100 and c3 > 10, 那么可以考虑建立组合索引 Index cidx (c1, c2, c3),这样可以用查询条件构造出一个索引前缀进行 Scan。
通过索引查询和直接扫描 Table 的区别
TiDB 实现了全局索引,所以索引和 Table 中的数据并不一定在一个数据分片上,通过索引查询的时候,需要先扫描索引,得到对应的行 ID,然后通过行 ID 去取数据,所以可能会涉及到两次网络请求,会有一定的性能开销。
如果查询涉及到大量的行,那么扫描索引是并发进行,只要第一批结果已经返回,就可以开始去取 Table 的数据,所以这里是一个并行 + Pipeline 的模式,虽然有两次访问的开销,但是延迟并不会很大。
以下情况不会涉及到两次访问的问题:
索引中的列已经满足了查询需求 。比如 Table t 上面的列 c 有索引,查询是 select c from t where c > 10;,这个时候,只需要访问索引,就可以拿到所需要的全部数据。这种情况称之为覆盖索引 (Covering Index)。所以如果很关注查询性能,可以将部分不需要过滤但是需要在查询结果中返回的列放入索引中,构造成组合索引,比如这个例子: select c1, c2 from t where c1 > 10;,要优化这个查询可以创建组合索引 Index c12 (c1, c2)。
表的 Primary Key 是整数类型 。在这种情况下,TiDB 会将 Primary Key 的值当做行 ID,所以如果查询条件是在 PK 上面,那么可以直接构造出行 ID 的范围,直接扫描 Table 数据,获取结果。
分布式事务
前面提到了数据如何存储,分布式事TiDB选的2PC,角色为协调者和事务参与者,在请求阶段需要做协调和决策,所有节点同意后进入表决阶段。
TiDB采用的2PC是Percolator 的分布式事务模型,是去中心化的事务模型。TiDB是如何实现的呢?
第一阶段,Prewrite:事务涉及改动的 keys 选一个作为 primary key, 其他为 secondary keys;并行 prewrite 向所有 keys确认。若成功所有 key 会将与 primary key 形成锁定;强一致,任何一个分片在事务执行中故障,重试后事务会失败。
第二阶段,Commit:首先 commit primary key ,若此步成功,标致着整个事务提交成功;异步并行 commit secondary keys。通过查询primary key的状态决定是否提交;查询语句会根据 primary key 的状态,返回不同version的值。
数据库类的基础软件,周边生态工具是非常重要的一个环节。拿 MySQL 来说,单从性能、稳定性方面来说,不是一款非常优秀的产品,但是为什么会这么受欢迎呢?根本原因就是生态,基于 Binlog 的一系列大数据生态、极大的人才库、商业化服务公司(Percona等)、各式各样的周边运维工具。基于这些原因,让你完全可以忽略之前提到的性能之类的问题,通过生态工具去弥补。TiDB 的思路也是类似,在一开始就选择了互联网行业使用广泛的MySQL 协议。有个数据库就得考虑以下几个问题:如何将数据库导入、如何备份恢复、如何监控、如何将数据导出、如何兼容以前 MySQL 下游的大数据体系。
下图中包括了 TiDB 的架构和主要生态工具。
DM
TiDB Data Migration (DM) 是一体化的数据迁移任务管理平台,支持从 MySQL 或 MariaDB 到 TiDB 的全量数据迁移和增量数据复制。使用 DM 工具有利于简化错误处理流程,降低运维成本。
BR
Backup & Restore(以下简称 BR)是 TiDB 分布式备份恢复的命令行工具,用于对 TiDB 集群进行数据备份和恢复。相比 dumpling 和 mydumper,BR 更适合大数据量的场景。
TiDB Binlog & TiCDC
TiDB Binlog 是一个用于收集 TiDB 的 binlog,并提供准实时备份和同步功能的工具。可以讲变更记录写下游的 MySQL、TiDB、kafka。不过由于 TiDB Binlog 维护较为麻烦,所以新推出了 TiCDC 来解决这个问题。TiCDC 是一款通过拉取 TiKV 变更日志实现的 TiDB 增量数据同步工具,具有将数据还原到与上游任意 TSO 一致状态的能力,同时提供开放数据协议 (TiCDC Open Protocol),支持其他系统订阅数据变更。
TiDB 监控
TiDB 使用开源时序数据库 Prometheus 作为监控和性能指标信息存储方案,使用 Grafana 作为可视化组件进行展示。Alertmanager 作为告警发送模块。
HTAP数据库(Hybrid Transaction and Analytical Process,混合事务和分析处理)。2014年Gartner的一份报告中使用混合事务分析处理(HTAP)一词描述新型的应用程序框架,以打破OLTP和OLAP之间的隔阂,既可以应用于事务型数据库场景,亦可以应用于分析型数据库场景。实现实时业务决策。这种架构具有显而易见的优势:不但避免了繁琐且昂贵的ETL操作,而且可以更快地对最新数据进行分析。
1、技术要点
-
底层数据要么只有一份,要么可快速复制,并且同时满足高并发的实时更新。
-
要满足海量数据的容量问题,在存储、计算都具有很好的线性扩展能力。
-
具备标准的SQL,并支持诸如二级索引、分区、列式存储、向量化计算等技术。
2、重点技术
行存储(Row-based):对于传统的关系型数据库,比如OracleDB、MySQL、SQL Server、TiDB 中的 TiKV等,一般都是采用行存储(Row-based)行。在基于行式存储的数据库中,数据是按照行数据为基础逻辑存储单元进行存储的,一行中的数据在存储介质中以连续存储形式存在。这个类型比较适合 OLTP 类的场景。
列式存储(Column-based)是相对于行式存储来说的,Hbase、Greenplum、Clickhouse等分布式数据库均采用列式存储。在基于列式存储的数据库中,数据是按照列为基础逻辑存储单元进行存储的,一列中的数据在存储介质中以连续存储形式存在。
TiFlash 是 TiDB HTAP 形态的关键组件,它是 TiKV 的列存扩展,在提供了良好的隔离性的同时,也兼顾了强一致性。列存副本通过 Raft Learner 协议异步复制,但是在读取的时候通过 Raft 校对索引配合 MVCC 的方式获得 Snapshot Isolation 的一致性隔离级别。这个架构很好地解决了 HTAP 场景的隔离性以及列存同步的问题。TiFlash 提供列式存储,且拥有借助 ClickHouse 高效实现的协处理器层。除此以外,它与 TiKV 非常类似,依赖同样的 Multi-Raft 体系,以 Region 为单位进行数据复制和分散。TiFlash 以低消耗不阻塞 TiKV 写入的方式,实时复制 TiKV 集群中的数据,并同时提供与 TiKV 一样的一致性读取,且可以保证读取到最新的数据。TiFlash 中的 Region 副本与 TiKV 中完全对应,且会跟随 TiKV 中的 Leader 副本同时进行分裂与合并。TiFlash 可以兼容 TiDB 与 TiSpark,用户可以选择使用不同的计算引擎。
TiFlash 核心特性如下:
异步复制
TiFlash 中的副本以特殊角色 (Raft Learner) 进行异步的数据复制。这表示当 TiFlash 节点宕机或者网络高延迟等状况发生时,TiKV 的业务仍然能确保正常进行。这套复制机制也继承了 TiKV 体系的自动负载均衡和高可用:并不用依赖附加的复制管道,而是直接以多对多方式接收 TiKV 的数据传输;且只要 TiKV 中数据不丢失,就可以随时恢复 TiFlash 的副本。
一致性
TiFlash 提供与 TiKV 一样的快照隔离支持,且保证读取数据最新(确保之前写入的数据能被读取)。这个一致性是通过对数据进行复制进度校验做到的。每次收到读取请求,TiFlash 中的 Region 副本会向 Leader 副本发起进度校对(一个非常轻的 RPC 请求),只有当进度确保至少所包含读取请求时间戳所覆盖的数据之后才响应读取。
智能选择
TiDB 可以自动选择使用 TiFlash 列存或者 TiKV 行存,甚至在同一查询内混合使用提供最佳查询速度。这个选择机制与 TiDB 选取不同索引提供查询类似:根据统计信息判断读取代价并作出合理选择。
计算加速
TiFlash 对 TiDB 的计算加速分为两部分:列存本身的读取效率提升以及为 TiDB 分担计算。其中分担计算的原理和 TiKV 的协处理器一致:TiDB 会将可以由存储层分担的计算下推。能否下推取决于 TiFlash 是否可以支持相关下推。具体介绍请参阅“TiFlash 支持的计算下推”一节。
总体来说架构是比较好的,但是实践过程中对业务的 SQL 理解比较到位才能够真正用好 TiFLash,目前的版本对复杂 join、多个大表 join 支持还不够好,期待下个版本的 MPP 特性加持。
得物在今年初的时候就开始尝试引入 TiDB 了,截止目前线上在运行的有个4个集群,共60个节点,总数据量27T。其中有两个核心功能接入了 TiDB。对于数据库选型,我们一定是要结合业务场景去选型,比如数据量比较少,增速不快(半年500G),我们完全可以通过单机数据库MySQL去支撑;比如有热点访问的需求,那么分布式数据库一定是不合适的,也应该选单机数据库 MySQL,因为针对单机升配成本低,且没有分布式的网络开销;比如有低延迟(10ms)要求的业务,那么选分布式数据库也是不合适的。
说了这么多不合适用的场景,那么什么场景合适TiDB呢?比如需要有较高吞吐量、要求线性扩容性能和容量、数据增量比较快的业务。
由于本次分享时间有限,Xiaoyu无法深入展开去讲基于各个场景如何使用分布式关系新数据库。希望下次有机会可以邀请Xiaoyu以场景为主题,再做一次分享。