学习RadonDB源码(四)

1. 继续一下昨天的话题

昨天我学习了查询优化器的部分内容,其实主要是起了一个提纲挈领的作用,真的内容我还没有学习。那么今天首先歪个楼,写一点Go语言相关的东西,毕竟我的目的是在学习RadonDB源码的同时学习Go语言。

plans := planner.NewPlanTree()
    switch node.(type) {
    case *sqlparser.DDL:
        node := planner.NewDDLPlan(log, database, query, node.(*sqlparser.DDL), router)
        plans.Add(node)
    case *sqlparser.Insert:
        node := planner.NewInsertPlan(log, database, query, node.(*sqlparser.Insert), router)
        plans.Add(node)
    case *sqlparser.Delete:
        node := planner.NewDeletePlan(log, database, query, node.(*sqlparser.Delete), router)
        plans.Add(node)
    case *sqlparser.Update:
        node := planner.NewUpdatePlan(log, database, query, node.(*sqlparser.Update), router)
        plans.Add(node)
    case *sqlparser.Select:
        nod := node.(*sqlparser.Select)
        selectNode := planner.NewSelectPlan(log, database, query, nod, router)
        plans.Add(selectNode)
    case *sqlparser.Checksum:
        node := planner.NewOthersPlan(log, database, query, node, router)
        plans.Add(node)
    default:
        return nil, errors.Errorf("optimizer.unsupported.query.type[%+v]", node)

首先呢,代码新建了一个PlanTree指针类型的plans。然后呢,根据不同的node类型判断分支,每一个分支里都会新建一个不同类型的Plan,然后把这个Plan放入到plans里。

看到这里我有点恍惚了,这有点像多态啊。我们来看看NewPlanTree都干了什么:

type Plan interface {
    Build() error
    Type() PlanType
    JSON() string
    Size() int
    Children() *PlanTree
}

// PlanTree is a container for all plans
type PlanTree struct {
    size     int
    children []Plan
}

// NewPlanTree creates the new plan tree.
func NewPlanTree() *PlanTree {
    return &PlanTree{
        children: make([]Plan, 0, 8),
    }
}

其实就是新建了一个Plan类型的切片放在了PlanTree里。Plan是一个接口,有了接口就能实现多态,这是我之前讲过的:

Go语言之初识接口

刚刚好,不管是ddl_plan还是其他的plan,都实现了这个接口,也就是说实现了多态,可以将这些plan全都放在plan类型的切片里了。

Go的多态实现,或者说接口,我个人觉得没有Java好理解。

2. DDL Plan在干什么

其实DDL本无所谓什么plan,但是自从有了分库,就有了plan。
----周树人

分库的情况下,一切都变得比较复杂和迷乱了。其实拨开迷雾,总能看到一丝光亮,ddl_plan其实做了这么几件事:

  • 判断几种非法操作;

  • 进行简单的语法解析和替换。

先说几种非法操作,这个对于DBA来说还是应该知道并且烂熟于心的:

  • 不能删除shard key所在的列;

  • shard key的列不能被修改;

  • 只能在shard key所在的列上做唯一约束和主键。

接下来要遍历所有的segment,那么又引进了一个概念叫做segment。segment看起来是个元组:

// Segment tuple.
type Segment struct {
    // Segment table name.
    Table string `json:",omitempty"`
    // Segment backend name.
    Backend string `json:",omitempty"`
    // key range of this segment.
    Range KeyRange `json:",omitempty"`
}

其实接下来的代码就是对这个元组中的元素进行简单的处理了:

            segTable := segment.Table
            if node.Table.Qualifier.IsEmpty() {
                segTable = fmt.Sprintf("`%s`.`%s`", database, segTable)
                rawQuery := strings.Replace(p.RawQuery, "`", "", 2)
                re, _ := regexp.Compile(fmt.Sprintf(`\b(%s)\b`, table))
                query = re.ReplaceAllString(rawQuery, segTable)
            } else {
                segTable = fmt.Sprintf("`%s`.`%s`", database, segTable)
                newTable := fmt.Sprintf("%s.%s", database, table)
                rawQuery := strings.Replace(p.RawQuery, "`", "", 4)
                re, _ := regexp.Compile(fmt.Sprintf(`\b(%s)\b`, newTable))
                query = re.ReplaceAllString(rawQuery, segTable)
            }

代码逻辑很简单,会把我们在命令行上输入的类似create之类的语句进行一些字符串上的处理,主要是格式化输出和一些字符串替换等等,就不摊开讲了,没啥意思。

            tuple := xcontext.QueryTuple{
                Query:   query,
                Backend: segment.Backend,
                Range:   segment.Range.String(),
            }
            p.Querys = append(p.Querys, tuple)

接下来就是把刚才格式化好的查询,拼接在query字符串中。这一步完成之后,就完成了DDLPlan的构建。

其实,DDL的plan也没什么精彩的地方。

这世界上本来没有ddl的执行计划,有了分库之后,便有了ddl的执行计划,其实有和没有,终究是一样的。
----周树人

3. 小结

我猜,Insert的DDL会更有意思。

写入数据,本来是简单的事情,但是有了分库,这一切终究不再简单了。
----周树人

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章