學習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會更有意思。

寫入數據,本來是簡單的事情,但是有了分庫,這一切終究不再簡單了。
----周樹人

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