tispark原理之下推丨tidb工具分享(代码片段)

TiDB_PingCAP TiDB_PingCAP     2022-12-12     151

关键词:

TiSpark 是 PingCAP 为解决用户复杂 OLAP 需求而推出的产品。它通过 Spark 提供的拓展机制与内置的 TiKV Client Java,在 Spark 之上直连 TiKV 进行读写,具有事务性读取、事务性写入与删除等能力。其中在事务性读取中基于 Spark Extension 实现了下推(详情可见 TiSpark 用户指南)。

为了帮助读者更好地理解、运用 TiSpark,本文将详细介绍 TiSpark 中下推相关的知识,包括 Spark 中的下推含义,实现原理,及其拓展接口、TiSpark 下推策略和下推支持列表。

本文作者:施宇航,PingCAP 研发工程师。

TiSpark 架构图

理解 Spark 的下推

TiSpark 本质是 Spark 的 connector,想要了解 TiSpark 是如何下推的必须先理解 Spark 中的下推。

了解 Spark SQL

首先简单了解一下 Spark SQL 的执行过程,这有助于理解下推原理的介绍。

Spark SQL 的核心是 Catalyst,它会依次进行 SQL 的解析,校验,优化,选择物理计划。最终生成可执行 RDD,交由 Spark Core 执行任务。

在这个过程中,Spark SQL 会被解析为一颗树。树的节点称为 TreeNode,它有多个实现类,可以用来表示逻辑计划树与物理计划树中各种类型的节点。这里不过多展开,我们只需要知道由 TreeNode 组成的一颗树可以表示一条 SQL:如过滤条件会被解析为 Filter 算子节点。

Spark 中的下推

下推是一种经典的 SQL 优化手段,它会尽量将一些算子推向靠近数据源的位置,以减少上层所需处理的数据量,最终达到加快查询速度的目的。常见的下推优化有:谓词下推,聚合下推,映射下推。

在分布式计算引擎 Spark 中,下推的含义如出一辙,但需要注意在 Spark 中其实有两步下推优化:

  1. 逻辑计划优化阶段:会尽量将算子推向靠近数据源的方向,但不会推向数据源

  2. 物理计划生成阶段:将算子推到数据源,Spark 可能不会再处理该算子

举个例子,考虑如下 SQL:

select * from A join B on A.id = B.id where A.a>10 and B.b<100;

上文提到 SQL 会被解析为一颗逻辑计划树

  • filter 表示 where 条件
  • join 表示 join 操作
  • scan_a 与 scan_b 表示从数据源 A,B 表拉取数据

第一步:在逻辑计划下推优化后,过滤条件会被下推到更靠近数据源的位置,这样 join 所需处理的数据就会更少

对应的 SQL 可如下表示:

select * from (select * from A where A.a>10) a join (select * from B where B.b<100) b on a.id = b.id 

第二步:在物理计划生成时,过滤条件还可能被彻底下推到数据源。也就是说 Spark 无需处理 Filter 了,数据源返回就已完成过滤。

Spark 下推原理

该小节代码基于 Spark 3.2

逻辑计划下推优化

Spark 首先会在逻辑计划优化时进行下推优化。

在 Catalyst 的逻辑计划优化阶段,会应用各种优化规则,其中就包括了下推优化的规则。这里就对应了上文所说的逻辑计划层的下推优化。以谓词下推优化 PushDownPredicates 为例

object PushDownPredicates extends Rule[LogicalPlan] with PredicateHelper 
  def apply(plan: LogicalPlan): LogicalPlan = plan transform 
    CombineFilters.applyLocally
      .orElse(PushPredicateThroughNonJoin.applyLocally)
      .orElse(PushPredicateThroughJoin.applyLocally)
  

CombineFilters 用于合并过滤条件。PushPredicateThroughNonJoin 和 PushPredicateThroughJoin 则用于分别处理不包含 join 和包含 join 时的谓词下推。由于这部分下推不是本文重点,我们不再赘述其具体实现,感兴趣的同学可以直接参考 Spark 源码

物理计划下推数据源

在完成逻辑计划阶段的下推优化后,Spark 会基于下推结果,在生成物理计划时再进行下推数据源的优化。TiSpark 主要涉及此时的下推,我们重点阐述这部分的原理。

下推接口

在 Spark 中,提供了 DataSource API 接口用于拓展数据源,其中包含了下推接口用于指定需要下推到数据源的算子。以 Spark 3.2.1 的谓词下推为例,其接口如下:

@Evolving
public interface SupportsPushDownFilters extends ScanBuilder 
  Filter[] pushFilters(Filter[] filters);
  Filter[] pushedFilters();

  • Filter[] pushFilters(Filter[] filters):入参是从 Catalyst expression 解析来的所有过滤条件,它是经过了第一步逻辑计划优化之后的结果。出参是 Spark 无法下推到数据源的过滤条件,被称为 postScanFilters
  • Filter[] pushedFilters():出参是能下推到数据源的过滤条件,被称为 pushedFilters

其中 postScanFilters 和 pushedFilters 中允许有相同的 filter,此时数据源和 Spark 都会进行过滤操作。在 Spark 中 parquet row group filter 就是有相同 filter 的一个例子

下推原理

那么当我们实现该接口,Spark 又是如何运作的呢?我们可以简单将其归纳为两步:

  1. 第一步:根据此接口,保留无法下推到数据源的 Filter
  2. 第二步:根据此接口,最终生成物理计划时,在获取数据源数据的 Scan 算子中处理下推部分的 Filter。这一步中,由于不同的数据源会有不同处理,当我们自定义拓展数据源时,一般由我们自己实现。

先来看第一步,第一步发生在 catalyst 的优化阶段,由 V2ScanRelationPushDown 完成。

其简化过的核心代码如下:

object V2ScanRelationPushDown extends Rule[LogicalPlan] with PredicateHelper 
  import DataSourceV2Implicits._

  def apply(plan: LogicalPlan): LogicalPlan = 
    applyColumnPruning(pushDownAggregates(pushDownFilters(createScanBuilder(plan))))
  

  private def pushDownFilters(plan: LogicalPlan) = plan.transform 
    case Filter(condition, sHolder: ScanBuilderHolder) =>
      val (pushedFilters, postScanFiltersWithoutSubquery) = PushDownUtils.pushFilters(
        sHolder.builder, normalizedFiltersWithoutSubquery)
     
      val filterCondition = postScanFilters.reduceLeftOption(And)
      filterCondition.map(Filter(_, sHolder)).getOrElse(sHolder)
  

在 apply 方法中会应用各种下推,其中包含谓词下推 pushDownFilters。

而在 pushDownFilters 中会依赖 PushDownUtils 分别获取到 pushedFilters 与 postScanFilters。最终返回的逻辑计划中只会包含 postScanFilters。

PushDownUtils 简化过的核心代码如下:

object PushDownUtils extends PredicateHelper 
  def pushFilters(
      scanBuilder: ScanBuilder,
      filters: Seq[Expression]): (Seq[sources.Filter], Seq[Expression]) = 
    scanBuilder match 
      case r: SupportsPushDownFilters =>
        val postScanFilters = r.pushFilters(translatedFilters.toArray).map  filter =>
          DataSourceStrategy.rebuildExpressionFromFilter(filter, translatedFilterToExpr)
        
        (r.pushedFilters(), (untranslatableExprs ++ postScanFilters).toSeq)

      case _ => (Nil, filters)
    
  

pushFilters 方法负责处理谓词下推,其实现很简单:匹配上 SupportsPushDownFilters 接口,然后根据我们的具体实现获取到 pushedFilters 与 postScanFilters。如果没有实现,则 pushedFilters 为空表示无需下推。

第二步数据源不同其实现也不同,我们以 JDBC 数据源为例。

首先是 JDBCScanBuilder 的 build 方法

override def build(): Scan = 
  val resolver = session.sessionState.conf.resolver
  val timeZoneId = session.sessionState.conf.sessionLocalTimeZone
  val parts = JDBCRelation.columnPartition(schema, resolver, timeZoneId, jdbcOptions)
  JDBCScan(JDBCRelation(schema, parts, jdbcOptions)(session), finalSchema, pushedFilter,
    pushedAggregateList, pushedGroupByCols)

该方法会以 pushedFilter 为参数返回 Scan 的实现类 JDBCScan

case class JDBCScan(
    relation: JDBCRelation,
    prunedSchema: StructType,
    pushedFilters: Array[Filter],
    pushedAggregateColumn: Array[String] = Array(),
    groupByColumns: Option[Array[String]]) extends V1Scan 
  override def toV1TableScan[T <: BaseRelation with TableScan](context: SQLContext): T = 
    new BaseRelation with TableScan 
      override def buildScan(): RDD[Row] = 
        val columnList = if (groupByColumns.isEmpty) 
          prunedSchema.map(_.name).toArray
         else 
          pushedAggregateColumn
        
        relation.buildScan(columnList, prunedSchema, pushedFilters, groupByColumns)
      
    .asInstanceOf[T]
  

JDBCScan 会调用 relation.buildScan,该方法最终会返回 JDBCRDD

private[jdbc] class JDBCRDD(
    sc: SparkContext,
    getConnection: () => Connection,
    schema: StructType,
    columns: Array[String],
    filters: Array[Filter],
    partitions: Array[Partition],
    url: String,
    options: JDBCOptions,
    groupByColumns: Option[Array[String]])
  extends RDD[InternalRow](sc, Nil) 

  private val filterWhereClause: String =
    filters
      .flatMap(JDBCRDD.compileFilter(_, JdbcDialects.get(url)))
      .map(p => s"($p)").mkString(" AND ")
      

在 JDBCRDD 中,Filter 会在 filterWhereClause 中被解析为 SQL 的 Where 条件,最终以完整 SQL 的形式请求兼容 MySQL 协议的数据源。

至此,JDBC 数据源实现了对下推 Filter 的处理。那么在 Spark 中,是如何衔接上 JDBCRDD 的呢?

这就发生 catalyst planner 阶段中的 DataSourceV2Strategy 策略中。其简化核心代码如下:

class DataSourceV2Strategy(session: SparkSession) extends Strategy with PredicateHelper 

  override def apply(plan: LogicalPlan): Seq[SparkPlan] = plan match 
    case PhysicalOperation(project, filters,
        DataSourceV2ScanRelation(_, V1ScanWrapper(scan, pushed, aggregate), output)) =>
      val v1Relation = scan.toV1TableScan[BaseRelation with TableScan](session.sqlContext)
      val rdd = v1Relation.buildScan()
      val unsafeRowRDD = DataSourceStrategy.toCatalystRDD(v1Relation, output, rdd)
      val dsScan = RowDataSourceScanExec(
        output,
        output.toStructType,
        Set.empty,
        pushed.toSet,
        aggregate,
        unsafeRowRDD,
        v1Relation,
        tableIdentifier = None)
      withProjectAndFilter(project, filters, dsScan, needsUnsafeConversion = false)

    case PhysicalOperation(project, filters, DataSourceV2ScanRelation(_, scan: LocalScan, output))

    case PhysicalOperation(project, filters, relation: DataSourceV2ScanRelation)

    case PhysicalOperation(p, f, r: StreamingDataSourceV2Relation)
 
    case PhysicalOperation(p, f, r: StreamingDataSourceV2Relation)

在该策略中,Spark 会匹配 PhysicalOperation 算子,由于有多种 Scan ,因此这里会有多种匹配。由于 JDBC 数据源实现的是 V1Scan,这里就会进入到第一个匹配。

在该匹配中会调用 toV1TableScan 获取到 JDBCRDD,并在 RowDataSourceScanExec 中执行获取数据的操作。

最后返回的 withProjectAndFilter 只是将 RowDataSourceScanExec 拼接到整个物理计划中返回。

基于此,Spark 完成了数据源的下推,并提供了一套封装好的接口用于实现外部数据源的下推。

理解 TiSpark 的下推

注意:TiSpark 的下推是指下推到数据源,TiSpark 不会对逻辑计划中的下推优化有任何改动。下文涉及到的下推都是指下推到数据源。

TiSpark 下推策略

那么对 TiSpark 来说,一个算子是否要下推?我们从两方面讨论

是否需要下推

一般来说,下推可以获得更好的速度与性能,但这在大数据量的 OLAP 场景中不是绝对的:假设你的 Spark 资源非常充裕,而作为 TiSpark 数据源的 TiKV 压力较大,如果还进行下推特别是消耗资源较大的聚合下推,反而会使得整体执行速度降低,转而将压力放到资源充分的 Spark 则是更合理的选择。因此对于一些资源消耗大的算子,我们应该由实际情况决定是否下推。对于此 TiSpark 提供了一些可配置的参数

  • spark.tispark.plan.allow_agg_pushdown:用于指定是否允许下推聚合操作,当 TiKV 压力过大时可以选择配置此参数
  • spark.tispark.plan.unsupported_pushdown_exprs:提供更精细的配置,用户可以通过配置禁止相应表达式被下推。当你不想下推或是你使用的是非常老的 TiKV 版本(某些表达式无法下推),那么可以选择配置此参数。

能否被下推

算子不总是能下推,可能某些表达式,类型就会无法下推。这里的限制来源于两个方面:Spark 本身不支持,TiKV 不支持。两者的并集即是最终无法下推的。对于此 TiSpark 会自动基于 Spark 与 TiKV 的能力决定是否下推,无需用户选择。

TiSpark 下推原理

上文讲到 Spark 提供了 DataSource API 用于拓展数据源实现下推。但实际上它并不能满足 TiSpark 的下推需求,它存在如下问题:

  • DataSource API V1 时期下推接口设计糟糕:假设原来有 limitscan,prunedscan,limitprunedscan 三种下推接口。那么当要支持新的下推算子 filter 时,就需要新增 filterscan,filterlimitscan,filterprunedscan,filterlimitprunedscan 四个接口。
  • 下推能力受限:即使是在 Spark 3.0 中的 DataSource API V2,也只支持谓词下推与列裁剪下推。在最近的 Spark 3.2/3.3 中才陆续支持了聚合下推,Limit 下推等能力
  • 下推策略不灵活:有些下推是要综合考虑各种算子的。比如 Avg 实际就是 Sum/Count,只要这两个算子可以下推理论上我们也可以下推 Avg,但 DataSource API 却无法支持

回顾 Spark 下推的实现原理。其实就是在 Spark 逻辑计划中摘除相应算子,然后在执行物理计划时应用到数据源中。其问题在于 DataSource API 的框架使得下推能力被大大限制。那么如果我们不使用 DataSource API,同时使用类似修改物理计划的方式去进行数据源下推不就可以了吗?

TiSpark 的确是这么做的。在 Spark 2.2 时支持了 catalyst extension 了,它能以拓展点的方式在 catalyst 的各阶段插入自定义规则或策略。TiSpark 就是基于 catalyst extension 实现了一套下推策略。

数据源下推需要修改物理计划,从逻辑计划转换为物理计划发生在 planner 阶段。相应的 TiSpark 就需要去实现该阶段的拓展点,其接口如下:

def injectPlannerStrategy(builder: StrategyBuilder): Unit = 
  plannerStrategyBuilders += builder

以下代码基于 Spark 3.2.1 TiSpark 3.0.1

TiSpark 对该拓展点的实现如下:

e.injectPlannerStrategy(new TiStrategyFactory(getOrCreateTiContext))

class TiStrategyFactory(getOrCreateTiContext: SparkSession => TiContext)
    extends (SparkSession => Strategy) 
  override def apply(sparkSession: SparkSession): Strategy = 
    TiExtensions.validateCatalog(sparkSession)
    ReflectionUtil.newTiStrategy(getOrCreateTiContext, sparkSession)
  

其中 ReflectionUtil 可以利用反射,基于 Spark 版本返回对应的 TiStrategy。这是为了解决多版本 Spark 之间的源代码不兼容问题,我们重点来看 TiStrategy 的实现:

case class TiStrategy(getOrCreateTiContext: SparkSession => TiContext)(sparkSession: SparkSession)
    extends Strategy with Logging 

  override def apply(plan: LogicalPlan): Seq[SparkPlan] = 
    plan
      .collectFirst 
        case DataSourceV2ScanRelation(DataSourceV2Relation(table: TiDBTable, _, _, _, _), _, _) =>
          doPlan(table, plan)
      
      .toSeq
      .flatten
  

该方法会去尝试匹配 TiDBTable ,如果出现 TiDBTable 那么就表示数据源为 TiDB,就会执行 doPlan 方法应用我们自定义的策略。反之,我们不做任何操作避免影响其他数据源的执行计划。再来深入到 doPlan 方法中(简化代码):

private def doPlan(source: TiDBTable, plan: LogicalPlan): Seq[SparkPlan] =
  plan match 
    case PhysicalOperation(
          projectList,
          filters,
          DataSourceV2ScanRelation(
            DataSourceV2Relation(source: TiDBTable, _, _, _, _),
            _,
            _)) =>
      pruneFilterProject(projectList, filters, source, newTiDAGRequest()) :: Nil
      
    case TiAggregation(
          groupingExpressions,
          aggregateExpressions,
          resultExpressions,
          TiAggregationProjectionV2(filters, _, `source`, projects))
        if isValidAggregates(groupingExpressions, aggregateExpressions, filters, source) =>
    case _ => Nil
  

该策略其实在模仿 Spark 下推原理章节中提到的 DataSourceV2Strategy 策略。

在该下推策略中,我们基于模式匹配,识别出能够进行下推的 Spark 算子,分别执行各种下推逻辑。以谓词下推进行举例:谓词下推会匹配 PhysicalOperation ,并执行 pruneFilterProject 方法:

private def pruneFilterProject(
    projectList: Seq[NamedExpression],
    filterPredicates: Seq[Expression],
    source: TiDBTable,
    dagRequest: TiDAGRequest): SparkPlan = 

  val (pushdownFilters: Seq[Expression], residualFilters: Seq[Expression]) =
    filterPredicates.partition((expression: Expression) =>
      TiExprUtils.isSupportedFilter(expression, source, blocklist))

  val residualFilter: Option[Expression] =
    residualFilters.reduceLeftOption(catalyst.expressions.And)

  filterToDAGRequest(tiColumns, pushdownFilters, source, dagRequest)

  val scan = toCoprocessorRDD(source, projectSeq, dagRequest)
  residualFilter.fold(scan)(FilterExec(_, scan))

  • 首先 TiExprUtils.isSupportedFilter 方法会将 filter 表达式 转换为 TiKV 表达式,然后根据具体的表达式与类型判断是否可以下推。可以下推的被放在 pushdownFilters 中,不可以下推的被放在 residualFilter 中。
  • 然后 filterToDAGRequest 方法会基于可下推的 filter 构建请求 TiKV 的参数。最终由 toCoprocessorRDD 方法返回一个可获取源数据的物理计划。
  • 最后该物理计划会由 FilterExec 包装执行,同时还需要在其上层应用无法下推的 residualFilter 算子。

这样就完成了 Filter 的下推,类似的 TiSpark 还支持了常见的聚合下推,limit 下推,order by 下推。

TiSpark 下推参照表

TiSpark 目前能够支持谓词下推,聚合下推,limit 下推,order by 下推,但有些类型无法下推,其支持情况如下表:

目前 2022/07/11:

  • min/max(time) 下推有错误的结果,min/max(set) 下推可能导致 TiKV panic
  • 谓词下推不支持对 NULL 进行过滤

在 TiSpark 中,可以通过 explain 判断是否被下推,举个例子:

  1. 创建 TiDB 表
CREATE TABLE `test`.`t`  (
  `id` int(11) NOT NULL,
  PRIMARY KEY (`id`)
);
  1. 执行查询语句
spark.sql("select avg(id) from test.t where id > 10").explain
  1. 查看执行计划,重点关注 TiKV CoprocessorRDD

    • RangeFilter: [[id@LONG GREATER_THAN 10]]:表明 id>10 被下推
    • Aggregates: Sum(id@LONG), Count(id@LONG) : 表明 Sum 和 Count 被下推,他们会最终被处理为 Avg
*(2) HashAggregate(keys=[], functions=[specialsum(specialsum(id#252L, DecimalType(38,0), null)#258, DecimalType(38,0), null), specialsum(count(id#252L)#259L, LongType, 0)])
+- Exchange SinglePartition, true, [id=#38]
   +- *(1) HashAggregate(keys=[], functions=[partial_specialsum(specialsum(id#252L, DecimalType(38,0), null)#258, DecimalType(38,0), null), partial_specialsum(count(id#252L)#259L, LongType, 0)])
      +- *(1) ColumnarToRow
         +- TiKV CoprocessorRDD[table: t] IndexReader, Columns: id@LONG:  IndexRangeScan(Index:primary(id)):  RangeFilter: [[id@LONG GREATER_THAN 10]], Range: [([t\\200\\000\\000\\000\\000\\000\\000o_i\\200\\000\\000\\000\\000\\000\\000\\001\\003\\200\\000\\000\\000\\000\\000\\000\\v], [t\\200\\000\\000\\000\\000\\000\\000o_i\\200\\000\\000\\000\\000\\000\\000\\001\\372])] , Aggregates: Sum(id@LONG), Count(id@LONG) , startTs: 434873744501506049

展望

TiSpark 通过劫持执行计划的方式具有如下优点:

  • 不会被 DataSource API 限制下推能力;
  • 我们只添加了足够薄的一层,使得完全不影响 Spark SQL 的执行计划以及其他数据源的执行流程。

但同时,他也带来了一定问题:

  • 代码复杂度增加;
  • 插入自定义策略的时要时刻注意不影响 Spark 原有逻辑;
  • 需要深入 Spark Catalyst 触碰到一些正在发展中的接口,这意味着不稳定。

随着 Spark DataSource API 的发展,其下推能力也在不断完善:Spark 3.2 支持了聚合下推,Spark 3.3 支持了 Limit 下推并增强了整体下推的能力。希望在不远的将来,TiSpark 能将下推逻辑部分甚至全部切换到 DataSource API。以减少对执行计划的侵入并提高代码的可读性。

使用tispark读取tiflash(代码片段)

TiSpark目前提供类似TiDB中engine隔离的方式读取TiFlash,方式是通过配置参数 spark.tispark.isolation_read_engines。参数值默认为 tikv,tiflash,表示根据CBO自动选择从TiFlash或从TiKV读取数据。如果将该参数值设置成 tiflash,表示强... 查看详情

使用tispark读取tiflash(代码片段)

TiSpark目前提供类似TiDB中engine隔离的方式读取TiFlash,方式是通过配置参数 spark.tispark.isolation_read_engines。参数值默认为 tikv,tiflash,表示根据CBO自动选择从TiFlash或从TiKV读取数据。如果将该参数值设置成 tiflash,表示强... 查看详情

dumpling导出表内并发优化丨tidb工具分享

...程师SIG组:MigrateSIGCommunity,主要涵盖TiDB数据处理工具,包含TiDB数据备份/导入导出,TiDB数据变更捕获,其他数据库数据迁移至TiDB等前言Dumpling是由Go语言编写的用于对数据库进行数据导出的工具。目前支 查看详情

基于tispark的海量数据批量处理技术

...介绍:杨哲轩,PingCAP资深解决方案架构师。熟悉TiSpark的人都知道,TiSpark是Spark的一个插件,它其实就是给予了Spark能够去访问TiDB底层分布式存储引擎TiKV或者TiFlash的能力。之前我们一直在解决读的问题,写问... 查看详情

大数据大数据组件tidb原理+实战篇(代码片段)

文章目录1.TiDB引入1.1.数据库技术发展简史1.2.从MySQL到TiDB1.3.TiDB概述1.4.数据库种类简介2.TiDB架构特性2.1.TiDB整体架构2.2.TiDB核心特性2.3.存储和计算能力3.TiDB安装部署3.1.TiDB-Local单机版3.2.TiDB-Docker集群版4.TiDB实践案例4.1.TiDB-SQL操作4.2... 查看详情

dm分库分表ddl“乐观协调”模式介绍丨tidb工具分享(代码片段)

...中同步延迟比较敏感的场景。悲观协调和乐观协调的对比原理DMworker的 查看详情

备份的“算子下推”:br简介丨tidb工具分享(代码片段)

BR选择了在TransactionKV层面进行扫描来实现备份。这样,备份的核心便是分布在多个TiKV节点上的MVCCScan:简单,粗暴,但是有效,它生来就继承了TiKV的诸多优势:分布式、利于横向拓展、灵活(可以备份... 查看详情

tidb6.0实战分享丨冷热存储分离解决方案(代码片段)

结论先行TiDB6.0正式提供了数据放置框架(PlacementRulesinSQL)功能,用户通过SQL配置数据在TiKV集群中的放置位置,可以对数据进行直接的管理,满足不同的业务场景需要。如:1.冷热分离存储,降低存储... 查看详情

tidb在ebay丨亿优百倍:商品数据服务缓存与代码优化

“亿优百倍”是eBay智能营销团队推出的系列文章,分享了在营销商品数据服务系统的架构、设计、代码方面的一些理解和研究。在上期的亿优百倍|商品数据服务TiDB性能优化(点击阅读)里,我们分享了对TiDB进行调优的方法,以提... 查看详情

dm分库分表ddl“悲观协调”模式介绍丨tidb工具分享

...过程中,这个复杂度转移到了数据迁移流程里。TiDBDM工具为用户提供了分库分表合并迁移功能,在数据迁移的过程中,支持将分表DML事件合并迁移,并一定程度支持上游分表进行DDL变更。本文以及后续文章主要介... 查看详情

tidb数据库压力测试工具安装(代码片段)

使用sysbench指定连接的时候不需要写上mysql-socket参数 如果自己使用sysbench来测试mysql性能的话,建议使用源码编译比较好,版本为0.4版本。步骤是:1、yuminstallm4autoconfautomakelibtool2、./configure--prefix=/usr/local/sysbench--with-mysql-include... 查看详情

tidb数据库使用syncer工具同步实时数据(代码片段)

mysql>selectcampaign_id,count(id)fromcreative_outputgroupbycampaign_id;602843rowsinset(4min44.23sec)mysql>selectis_cr_own,count(id)fromcreative_outputgroupbyis_cr_own;597684rowsinset(44.55sec)&n 查看详情

技术分享|tidb对大事务的简单拆分

作者:杨涛涛资深数据库专家,专研MySQL十余年。擅长MySQL、PostgreSQL、MongoDB等开源数据库相关的备份恢复、SQL调优、监控运维、高可用架构设计等。目前任职于爱可生,为各大运营商及银行金融企业提供MySQL相关技术支持、MySQL... 查看详情

tidb查询优化及调优系列tidb查询计划简介(代码片段)

...过一些具体的案例,向大家介绍TiDB查询及优化相关的原理和应用,在上一篇文章中我们简要介绍了TiDB查询优化器的优化流程。查询计划(executionplan)展现了数据库执行SQL语句的具体步骤,例如通过索引还是... 查看详情

tidb-使用tidblightning迁移mysql数据至tidb中(代码片段)

...ning介绍TiDBLightning是一个将全量数据高速导入到TiDB集群的工具,目前支持Mydumper或CSV输出格式的数据源。你可以在以下两种场景下使用Lightning:迅速导入大量新数据。备份恢复所有数据。TiDBLightning主要包含两个部分:tidb-li... 查看详情

tidb-使用br工具进行数据热备份与恢复(代码片段)

一、BR工具BR全称为Backup&Restore,是TiDB分布式备份恢复的命令行工具,用于对TiDB集群进行数据备份和恢复。BR只支持在TiDBv3.1及以上版本使用。在前面的章节中,我们介绍了dumpling将数据导出的方式,也可以作为... 查看详情

猿创征文|国产数据库实战之tidb数据库快速入门(代码片段)

...IP地址3.TiDB集群介绍二、快速部署本地测试集群1.安装TiUP工具2.声明全局环境变量3.快速部署TiDB集群三、连接TiDB数据库1.新开一个session以访问TiDB数据库2.通过Mysql客户端连接TiDB数据库四、清理TiDB集群环境五、TiDB数据库的用户管... 查看详情

gbase哪个证书要花钱

...系型数据库的能力。PCTA需要学习并熟练掌握TiDB架构原理、安装部署、周边工具等基础知识。PCTP ( PingCAPCertifiedTiDBProfessional )是PingCAP公司认证 TiDB 数据库专家的缩写 。PCTP要求具备管理大型分布式关... 查看详情