MySQL到TiDB:Hive Metastore横向扩展之路( 三 )


MySQL到TiDB:Hive Metastore横向扩展之路

文章插图
产生该问题的主要原因为每个 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_IDFROM PARTITIONSINNER JOIN TBLSON PARTITIONS.TBL_ID = TBLS.TBL_IDAND TBLS.TBL_NAME = '${TABLE_NAME}'INNER JOIN DBSON TBLS.DB_ID = DBS.DB_IDAND DBS.NAME = '${DB_NAME}'INNER JOIN PARTITION_KEY_VALS FILTER0ON FILTER0.PART_ID = PARTITIONS.PART_IDAND FILTER0.INTEGER_IDX = ${INDEX1}INNER JOIN PARTITION_KEY_VALS FILTER1ON FILTER1.PART_ID = PARTITIONS.PART_IDAND FILTER1.INTEGER_IDX = ${INDEX2}INNER JOIN PARTITION_KEY_VALS FILTER2ON FILTER2.PART_ID = PARTITIONS.PART_IDAND FILTER2.INTEGER_IDX = ${INDEX3}WHERE FILTER0.PART_KEY_VAL = '${PART_KEY}'AND CASEWHEN FILTER1.PART_KEY_VAL <> '__HIVE_DEFAULT_PARTITION__' THEN CAST(FILTER1.PART_KEY_VAL AS decimal(21, 0))ELSE NULLEND = 10AND 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" = 0INNER JOIN "PARTITION_KEY_VALS" "FILTER1"ON "FILTER1"."PART_ID" = "PARTITIONS"."PART_ID"AND "FILTER1"."INTEGER_IDX" = 1WHERE "FILTER0"."PART_KEY_VAL" = '2021-12-28'AND CASEWHEN "FILTER1"."PART_KEY_VAL" <> '__HIVE_DEFAULT_PARTITION__' THEN CAST("FILTER1"."PART_KEY_VAL" AS decimal(21, 0))ELSE NULLEND = 10;由于对PARTITION_KEY_VALS的PARTITION_KEY_VAL字段添加了索引做查询优化,会导致该类查询生成的执行计划中同样会使用idx_PART_KEY_VAL索引进行数据扫描 , 该执行计划如下:
MySQL到TiDB:Hive Metastore横向扩展之路

文章插图
添加的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数据并返回 。此类执行计划扫描的数据量和需要查询的表的分区总量有关,如果该表只有少数的分区,则查询能够迅速响应 , 但如果查询的表有上百万的分区,则该类执行计划对于该类查询不是最优解 。


推荐阅读