共计 8171 个字符,预计需要花费 21 分钟才能阅读完成。
作者:vivo 互联网大数据团队 – Wang Zhiwen
本文介绍了 vivo 在大数据元数据服务横向扩大路线上的摸索历程,由理论面临的问题登程,对以后支流的横向扩大计划进行了调研及比照测试,通过多方面比照数据择优抉择 TiDB 计划。其次分享了整个扩大计划流程、施行遇到的问题及解决方案,对于在大数据元数据性能上面临同样窘境的开发者本篇文章具备十分高的参考借鉴价值。
一、背景
大数据元数据服务 Hive Metastore Service(以下简称 HMS),存储着数据仓库中所依赖的所有元数据并提供相应的查问服务,使得计算引擎(Hive、Spark、Presto)能在海量数据中精确拜访到须要拜访的具体数据,其在离线数仓的稳固构建上扮演着无足轻重的角色。vivo 离线数仓的 Hadoop 集群基于 CDH 5.14.4 版本构建,HMS 的版本抉择追随 CDH 大版本,以后应用版本为 1.1.0-cdh5.14.4。
vivo 在 HMS 底层存储架构未降级前应用的是 MySQL 存储引擎,但随着 vivo 业务倒退,数据爆炸式增长,存储的元数据也相应的增长到亿级别(PARTITION_PARAMS:8.1 亿、PARTITION_KEY_VALS:3.5 亿、PARTITIONS:1.4 亿),在如此大量的数据基数下,咱们团队常常面临机器资源的性能瓶颈,往往用户多并发的去查问某些大分区表(50w+ 分区),机器资源的使用率就会被打满,从而导致元数据查问超时,重大时甚至整个 HMS 集群不可用,此时复原伎俩只能临时停服所有 HMS 节点,直到 MySQL 机器负载降下来后在逐渐复原服务。为此,针对以后 MySQL 计划存在的重大性能瓶颈,HMS 急需一套欠缺的横向扩大计划来解决以后当务之急。
二、横向扩大技术计划选型
为解决 HMS 的性能问题,咱们团队对 HMS 横向扩大计划做了大量的调研工作,总体下来业内在 HMS 的横向扩大思路上次要分为对 MySQL 进行拆库扩大或用高性能的分布式引擎代替 MySQL。在第一种思路上做的比拟成熟的计划有 Hotels.com 公司开源的 Waggle Dance,实现了一个跨集群的 Hive Metastore 代理网关,他容许用户同时拜访多个集群的数据,这些集群能够部署在不同的平台上,特地是云平台。第二种思路以后支流的做法是用分布式存储引擎 TiDB 替换传统的 MySQL 引擎,在 Hive 社区中有不少公司对 hive 2.x 接入 TiDB 做了大量的测试并利用到生产中(详情点击)。
2.1 Waggle Dance
Waggle-dance 向用户提供对立的入口,将来自 Metastore 客户端的申请路由到底层对应的 Metastore 服务,同时向用户暗藏了底层的 Metastore 散布,从而在逻辑层面整合了多个 Metastore 的 Hive 库表信息。Waggle-dance 实现了 Metastore 的 Thrift API,客户端无需改变,对用户来说,Waggle-dance 就是一个 Metastore。其整体架构如下:
Waggle Dance 架构
从 Waggle-dance 的架构中最突出的个性是其采纳了多个不同的 MySQL 实例分担了原单 MySQL 实例的压力,除此之外其还有如下劣势:
- 用户侧能够沿用 Metastore 客户端的用法,配置多台 Waggle-dance 的连贯,在以后 Waggle-dance 连贯服务不可用的时候切换到其余的 Waggle-dance 服务上。
- Waggle-dance 只需几秒即可启动,加上其无状态服务的个性,使得 Waggle-dance 具备高效的动静伸缩性,能够在业务高峰期疾速上线新的服务节点扩散压力,在低峰期下线局部服务节点开释资源。
- Waggle-dance 作为一个网关服务,除了路由性能外,还反对后续的定制化开发和差异化部署,平台可依据须要增加诸如鉴权、防火墙过滤等性能。
2.2 TiDB
TiDB 是 PingCAP 公司自主设计、研发的开源分布式关系型数据库,是一款同时反对在线事务处理与在线剖析解决 (Hybrid Transactional and Analytical Processing, HTAP) 的交融型分布式数据库产品,具备程度扩容或者缩容、金融级高可用、实时 HTAP、云原生的分布式数据库、兼容 MySQL 5.7 协定和 MySQL 生态等重要个性。在 TiDB 4.x 版本中,其性能及稳定性较与之前版本失去了很大的晋升并满足 HMS 的元数据查问性能需求。故咱们对 TiDB 也做了相应的调研及测试。联合 HMS 及大数据生态,采纳 TiDB 作为元数据存储整体的部署架构如下:
HMS on TiDB 架构
因为 TiDB 自身具备程度扩大能力,扩大后能均分查问压力,该个性就是咱们解决 HMS 查问性能瓶颈的大杀器。除此外该架构还有如下劣势:
- 用户无需任何改变;HMS 侧面没有任何改变,只是其依赖的底层存储发生变化。
- 不毁坏数据的完整性,无需将数据拆分多个实例来分担压力,对 HMS 来说其就是一个残缺、独立的数据库。
- 除引入 TiDB 作为存储引擎外,不须要额定的其余服务撑持整个架构的运行。
2.3 TiDB 和 Waggle Dance 比照
后面内容对 Waggle-dance 计划和 TiDB 计划做了简略的介绍及劣势总结,以下列举了这两个计划在多个维度的比照:
通过上述多个维度的比照,TiDB 计划在性能体现、程度扩大、运维复杂度及机器老本上都优于 waggle-dance 计划,故咱们线上抉择了前者进行上线利用。
三、TiDB 上线计划
抉择 TiDB 引擎代替原 MySQL 存储引擎,因为 TiDB 与 MySQL 之间不能做双主架构,在切换过程中 HMS 服务须齐全停服后并重新启动切换至 TiDB,为保障切换过程顺利及前面若有重大问题产生能及时回滚,在切换前做了如下数据同步架构以保障切换前 MySQL 与 TiDB 数据统一以及切换后仍有 MySQL 兜底。
TiDB&MySQL 上线前后数据同步架构
在上述架构中,切换前惟一可写入的数据源只有源数据库主库,其余所有 TiDB、MySQL 节点都为只读状态,当且仅当所有 HMS 节点停服后,MySQL 源数据库从库及 TiDB 源数据库主库的数据同步最大工夫戳与源数据库主库统一时,TiDB 源数据库主库才凋谢可写入权限,并在批改 HMS 底层存储连贯串后逐个拉起 HMS 服务。
在上述架构实现后,即可开始具体的切换流程,切换整体流程如下:
HMS 切换底层存储流程
其中在保障源 MySQL 与 TiDB 数据失常同步前,须要对 TiDB 做以下配置:
- tidb_skip_isolation_level_check 须要配置为 1,否则启动 HMS 存在 MetaException 异样。
- tidb_txn_mode 需配置为 pessimistic,晋升事务一致性强度。
- 事务大小限度设置为 3G,可依据本人业务理论状况进行调整。
- 连贯限度设置为最大 3000,可依据本人业务理论状况进行调整。
此外在开启 sentry 服务状态下,需确认 sentry 元数据中 NOTIFICATION_ID 的值是否落后于 HMS 元数据库中 NOTIFICATION_SEQUENCE 表中的 NEXT_EVENT_ID 值,若落后需将后者替换为前者的值,否则可能会产生建表或创立分区超时异样。
以下为 TiDB 计划在在不同维度上的体现:
- 在对 HQL 的兼容性上 TiDB 计划齐全兼容线上所有引擎对元数据的查问,不存在语法兼容问题,对 HQL 语法兼容度达 100%
- 在性能体现上查问类接口均匀耗时优于 MySQL,性能整体晋升 15%;建表耗时升高了 80%,且反对更高的并发,TiDB 性能体现不差于 MySQL
- 在机器资源应用状况上整体磁盘使用率在 10% 以下;在没有热点数据拜访的状况下,CPU 均匀使用率在 12%;CPU.WAIT.IO 平均值在 0.025% 以下; 集群不存在资源应用瓶颈。
- 在可扩展性上 TiDB 反对一键程度扩缩容,且外部实现查问平衡算法,在数据达到平衡的状况下各节点可平摊查问压力。
- 在容灾性上 TiDB Binlog 技术可稳固撑持 TiDB 与 MySQL 及 TiDB 之间的数据同步,实现残缺的数据备份及可回退抉择。
- 在服务高可用性上 TiDB 可抉择 LVS 或 HaProxy 等服务实现负载平衡及故障转移。
以下为上线后 HMS 次要 API 接口调用耗时状况统计:
四、问题及解决方案
4.1 在模仿 TiDB 回滚至 MySQL 过程中呈现主键抵触问题
在 TiDB 数据增长 3 倍后,切换回 MySQL 呈现主键反复异样,具体日志内容如下:
主键抵触异样日志
产生该问题的次要起因为每个 TiDB 节点在调配主键 ID 时,都申请一段 ID 作为缓存,用完之后再去取下一段,而不是每次调配都向存储节点申请。这意味着,TiDB 的 AUTO_INCREMENT 自增值在单节点上能保障枯燥递增,但在多个节点下则可能会存在激烈跳跃。因而,在多节点下,TiDB 的 AUTO_INCREMENT 自增值从全局来看,并非相对枯燥递增的,也即并非相对有序的,从而导致 Metastore 库里的 SEQUENCE_TABLE 表记录的值不是对应表的最大值。
造成主键抵触的次要起因是 SEQUENCE_TABLE 表记录的值不为元数据中理论的最大值,若存在该状况在切换回 MySQL 后就有可能生成已存在的主键导致初见抵触异样,此时只需将 SEQUENCE_TABLE 里的记录值设置以后理论表中的最大值即可。
4.2 PARTITION_KEY_VALS 的索引取舍
在应用 MySQL 引擎中,咱们收集了局部慢查问日志,该类查问次要是查问分区表的分区,相似如下 SQL:
# 以下查问为查问三级分区表模板,且每级分区都有过去条件
SELECT PARTITIONS.PART_ID
FROM PARTITIONS
INNER JOIN TBLS
ON PARTITIONS.TBL_ID = TBLS.TBL_ID
AND TBLS.TBL_NAME = '${TABLE_NAME}'
INNER JOIN DBS
ON TBLS.DB_ID = DBS.DB_ID
AND DBS.NAME = '${DB_NAME}'
INNER JOIN PARTITION_KEY_VALS FILTER0
ON FILTER0.PART_ID = PARTITIONS.PART_ID
AND FILTER0.INTEGER_IDX = ${INDEX1}
INNER JOIN PARTITION_KEY_VALS FILTER1
ON FILTER1.PART_ID = PARTITIONS.PART_ID
AND FILTER1.INTEGER_IDX = ${INDEX2}
INNER JOIN PARTITION_KEY_VALS FILTER2
ON FILTER2.PART_ID = PARTITIONS.PART_ID
AND FILTER2.INTEGER_IDX = ${INDEX3}
WHERE FILTER0.PART_KEY_VAL = '${PART_KEY}'
AND CASE
WHEN FILTER1.PART_KEY_VAL <> '__HIVE_DEFAULT_PARTITION__' THEN CAST(FILTER1.PART_KEY_VAL AS decimal(21, 0))
ELSE NULL
END = 10
AND FILTER2.PART_KEY_VAL = '068';
在测试中通过管制并发重放该类型的 SQL,随着并发的减少,各个 API 的均匀耗时也会增长,且重放的 SQL 查问耗时随着并发的减少查问均匀耗时达到 100s 以上,尽管 TiDB 及 HMS 在压测期间没有呈现任何异样,但显然这种查问效率会让用户很难承受。DBA 剖析该查问没有抉择适合的索引导致查问走了全表扫描,倡议对 PARTITION_KEY_VALS 的 PARTITION_KEY_VAL 字段增加了额定的索引以减速查问,最终该类型的查问失去了极大的优化,即便加大并发到 100 的状况下均匀耗时在 500ms 内,对此咱们曾尝试对 PARTITION_KEY_VALS 增加上述索引操作。
但在线上理论的查问中,那些没有产生慢查问的分区查问操作其实都是按天分区的进行一级分区查问的,其 SQL 相似如下:
SELECT "PARTITIONS"."PART_ID"
FROM "PARTITIONS"
INNER JOIN "TBLS"
ON "PARTITIONS"."TBL_ID" = "TBLS"."TBL_ID"
AND "TBLS"."TBL_NAME" = 'tb1'
INNER JOIN "DBS"
ON "TBLS"."DB_ID" = "DBS"."DB_ID"
AND "DBS"."NAME" = 'db1'
INNER JOIN "PARTITION_KEY_VALS" "FILTER0"
ON "FILTER0"."PART_ID" = "PARTITIONS"."PART_ID"
AND "FILTER0"."INTEGER_IDX" = 0
INNER JOIN "PARTITION_KEY_VALS" "FILTER1"
ON "FILTER1"."PART_ID" = "PARTITIONS"."PART_ID"
AND "FILTER1"."INTEGER_IDX" = 1
WHERE "FILTER0"."PART_KEY_VAL" = '2021-12-28'
AND CASE
WHEN "FILTER1"."PART_KEY_VAL" <> '__HIVE_DEFAULT_PARTITION__' THEN CAST("FILTER1"."PART_KEY_VAL" AS decimal(21, 0))
ELSE NULL
END = 10;
因为对 PARTITION_KEY_VALS 的 PARTITION_KEY_VAL 字段增加了索引做查问优化,会导致该类查问生成的执行打算中同样会应用 idx_PART_KEY_VAL 索引进行数据扫描,该执行打算如下:
走 idx_PART_KEY_VAL 索引执行打算
增加的 idx_PART_KEY_VAL 索引在该字段的具备雷同值的数据较少时,应用该索引能检索较少的数据晋升查问效率。在 hive 中的表一级分区根本是按天进行分区的,据统计每天天分区的增量为 26w 左右,如果应用 idx_PART_KEY_VAL 索引,按这个数值计算,查问条件为 day>=2021-12-21 and day<2021-12-26 的查问须要检索将近 160w 条数据,这显然不是一个很好的执行打算。
若执行打算不走 idx_PART_KEY_VAL 索引,TiDB 可通过 dbs、tbls 检索出所有关联 partition 数据,在依据 part_id 和过滤条件扫描 PARTITION_KEY_VALS 数据并返回。此类执行打算扫描的数据量和须要查问的表的分区总量无关,如果该表只有多数的分区,则查问可能迅速响应,但如果查问的表有上百万的分区,则该类执行打算对于该类查问不是最优解。
不走 idx_PART_KEY_VAL 索引执行打算
针对不同执行打算的个性,整顿了以下比照点:
在理论生产中元数据根本都是按天分区为主,每天增长大略有 26w 左右,且范畴查问的应用场景较多,应用 idx_PART_KEY_VAL 索引查问的执行打算不太适宜线上场景,故该索引需不适宜增加到线上环境。
4.3 TiDB 内存突增导致宕机问题
在刚上线 TiDB 服务初期,曾数次面临 TiDB 内存溢出的问题,每次呈现的工夫都随机不确定,呈现的时候内存突增简直在一瞬间,若期间 TiDB 的内存抗住了突增量,突增局部内存开释在很长时间都不会失去开释,最终对 HMS 服务稳定性带来抖动。
TiDB 内存突增状况
通过和 TiDB 开发、DBA 联结剖析下,确认 TiDB 内存飙高的起因为用户在应用 Dashboard 功能分析慢查问引起;在剖析慢查问过程中,TiDB 须要加载本地所有的 slow-query 日志到内存,如果这些日志过大,则会造成 TiDB 内存突增,此外,如果在剖析期间,用户点击了勾销按钮,则有可能会造成 TiDB 的内存透露。针对该问题制订如下解决方案:
- 应用大内存机器替换原小内存机器,防止剖析慢查问时内存不够
- 调大慢查问阈值为 3s,缩小日志产生
- 定时 mv 慢查问日志到备份目录
4.4 locate 函数查问不走索引导致 TiKV 负异常
在 HMS 中存在局部通过 JDO 的形式去获取分区的查问,该类查问的过滤条件中用 locate 函数过滤 PART_NAME 数据,在 TiDB 中通过函数作用在字段中是不会触发索引查问的,所以在该类查问会加载对应表的所有数据到 TiDB 端计算过滤,TiKV 则需一直扫描全表并传输数据到 TiDB 段,从而导致 TiKV 负载异样。
locate 函数导致全表扫描
然而上述的查问条件能够通过 like 形式去实现,通过应用 like 语法,查问能够胜利应用到 PARTITIONS 表的 UNIQUEPARTITION 索引过滤,进而在 TiKV 端进行索引过滤升高负载。
like 语法走索引过滤
通过实现将 locate 函数查问转换为 like 语法查问,无效升高了 TiKV 端的负载状况。在 HMS 端实现变更后,TiKV 的 CPU 使用率升高了将近一倍,因为在 KV 端进行索引过滤,相应的 io 使用率有所回升,但网络传输则有显著的降落,由均匀 1G 升高到 200M 左右。
变更前后 TiKV 的负载状况
除 TiKV 负载有显著的升高,TiDB 的整体性能也失去显著的晋升,各项操作耗时呈量级升高。以下整顿了 TiDB 增删改查的天均匀耗时状况:
TiDB P999 天均匀耗时统计
4.5 get_all_functions 优化
随着 hive udf 的一直增长,HMS 的 get_all_functions api 均匀耗时增长的也越来越久,均匀在 40-90s,而该 api 在 hive shell 中首次执行查问操作时会被调用注册所有的 udf,过长的耗时会影响用户对 hive 引擎的应用体验,例如执行简略的 show database 须要期待一分钟甚至更久能力返回后果。
原 get_all_functions api 均匀耗时
导致该 api 耗时重大的次要起因是 HMS 通过 JDO 形式获取所有的 Function,在获取所有的 udf 时后盾会遍历每条 func 去关联 DBS、FUNC_RU 两个表,获取性能极低。而应用 directSQL 的形式去获取所有 udf 数据,响应耗时都在 1 秒以内实现,性能晋升相当显著。以下为 directSQL 的 SQL 实现逻辑:
select FUNCS.FUNC_NAME,
DBS.NAME,
FUNCS.CLASS_NAME,
FUNCS.OWNER_NAME,
FUNCS.OWNER_TYPE,
FUNCS.CREATE_TIME,
FUNCS.FUNC_TYPE,
FUNC_RU.RESOURCE_URI,
FUNC_RU.RESOURCE_TYPE
from FUNCS
left join FUNC_RU on FUNCS.FUNC_ID = FUNC_RU.FUNC_ID
left join DBS on FUNCS.DB_ID = DBS.DB_ID
五、总结
咱们从 2021 年 7 月份开始对 TiDB 进行调研,在经验数个月的测试于同年 11 月末将 MySQL 引擎切换到 TiDB。因为后期测试次要集中在兼容性和性能测试上,疏忽了 TiDB 本身可能潜在的问题,在上线初期经验了数次因慢查问日志将 TiDB 内存打爆的状况,在这特别感谢咱们的 DBA 团队、平台经营团队及 TiDB 官网团队帮忙剖析、解决问题,得以防止该问题的再次发生;与此同时,因为以后 HMS 应用的版本较低,加上大数据的组件在一直的降级演进,咱们也须要去兼容降级带来的变动,如 HDFS 降级到 3.x 后对 EC 文件读取的反对,SPARK 获取分区防止全表扫描革新等;此外因为 TiDB 的 latin 字符集反对中文字符的写入,该个性会导致用户误写入谬误的中文分区,对于此类型数据无奈通过现有 API 进行删除,还须要在应用层去禁止该类型谬误分区写入,防止无用数据累积。
经验了一年多的理论生产环境测验,TiDB 内存整体应用在 10% 以内,TiKV CPU 应用安稳,应用峰值均在 30 核内,暂不存在零碎瓶颈;HMS 服务的稳定性整体可控,要害 API 性能指标满足业务的理论需要,为业务的增长提供牢靠反对。在将来三年内,咱们将放弃该架构去撑持整个大数据平台组件的稳固运行,期间咱们也将继续关注行业内的变动,排汇更多优良教训利用到咱们的生产环境中来,包含但不限于对性能更好的高版本 TiDB 尝试,HMS 的性能优化案例。