學習RadonDB源碼(五)

1. 分佈式數據庫引入了不少新問題

移動互聯沒有興起,3G沒有開始之前,我們玩單機數據庫玩的不亦樂乎,而且也玩的很好了。

但是時代的大潮就是這麼浩浩湯湯,3G時代開始,移動互聯網興起,4G,5G的到來,給我們帶來了方便的同時,也帶來了海量的數據。此時我們的單機數據庫就開始逐漸無法處理這麼多數據庫了,單純的提升硬件水平已經無法解決這樣的數據了。

此時就只剩分佈式一條路可以走了,服務分佈式,數據庫也要分佈式。可是站在分佈式這個十字路口上,傳統的關係型數據庫似乎有點無所適從。這種無所適從給了noSQL一個機會,我們見證了類似MongoDB等等非關係型數據庫大放異彩。

不過noSQL也沒有能夠解決所有問題,人們還是需要關係型數據模型的,於是很多聰明的大神們就搞出了很多新的東西來,有改造MySQL的像我一直在研究的RadonDB,有兼容MySQL協議的NewSQL,比如TiDB。

分佈式,總是讓事情變得複雜一些。

比如我們都知道的CAP理論,比如Raft協議。不過還好我們有了很多現成的方案可以使用。

2. 繼續昨天的話題

昨天講到了DDL的優化器,比較無聊,因爲無非是將DDL語句做了一些格式化的操作。

今天看看insert的優化器。

所有的邏輯還是在Build方法中,首先需要注意的一個邏輯是這一段代碼:

    rows, ok := node.Rows.(sqlparser.Values)
    if !ok {
        return errors.Errorf("unsupported: rows.can.not.be.subquery[%T]", node.Rows)
    }

這裏的代碼顯示了一條insert語句的規則:

  • 不能用insert into table select * from table_a這種語句

RadonDB雖然支持分庫,不過也不是所有的表都要sharding,因此這裏代碼中對沒有配置sharding的表進行了邏輯判斷:

// Table is global or single table.
    if shardKey == "" {
        segments, err := p.router.Lookup(database, table, nil, nil)
        if err != nil {
            return err
        }
        for _, segment := range segments {
            buf := sqlparser.NewTrackedBuffer(nil)
            buf.Myprintf("%s %v%sinto %s.%s%v %v%v", node.Action, node.Comments, node.Ignore, database, segment.Table, node.Columns, node.Rows, node.OnDup)
            tuple := xcontext.QueryTuple{
                Query:   buf.String(),
                Backend: segment.Backend,
                Range:   segment.Range.String(),
            }
            p.Querys = append(p.Querys, tuple)
        }
        return nil
    }

代碼到時平平無奇,直接拼出insert語句就可以了,這種沒有sharding的表其實和單機時代的表沒有什麼區別了。

下面又要寫一條規則:

  • 不支持insert into ... on duplicate update語法

代碼邏輯是這樣的:

    // Check the OnDup.
    if len(node.OnDup) > 0 {
        // analyze shardkey changing.
        if isShardKeyChanging(sqlparser.UpdateExprs(node.OnDup), shardKey) {
            return errors.New("unsupported: cannot.update.shard.key")
        }
    }

如果寫了這樣的語句,就直接報錯了。

如果是sharding表,那麼處理邏輯是這樣的:

    // Find the shard key index.
    idx := -1
    for i, column := range node.Columns {
        if column.String() == shardKey {
            idx = i
            break
        }
    }
    if idx == -1 {
        return errors.Errorf("unsupported: shardkey.column[%v].missing", shardKey)
    }

首先遍歷所有的column,直到找到shardKey所在的那一列。這裏能看出來,RadonDB支持的是單個key進行sharding,因爲匹配成功後循環就跳出了。

for _, row := range rows {
        if idx >= len(row) {
            return errors.Errorf("unsupported: shardkey[%v].out.of.index:[%v]", shardKey, idx)
        }
        shardVal, ok := row[idx].(*sqlparser.SQLVal)
        if !ok {
            return errors.Errorf("unsupported: shardkey[%v].type.canot.be[%T]", shardKey, row[idx])
        }

        segments, err := p.router.Lookup(database, table, shardVal, shardVal)
        if err != nil {
            return err
        }
        rewrittenTable := segments[0].Table
        backend := segments[0].Backend
        rangi := segments[0].Range.String()
        val, ok := vals[rewrittenTable]
        if !ok {
            val = &valTuple{
                backend: backend,
                table:   rewrittenTable,
                rangi:   rangi,
                vals:    make(sqlparser.Values, 0, 16),
            }
            vals[rewrittenTable] = val
        }
        val.vals = append(val.vals, row)
    }

這裏又能引出一條規則:

  • shard key的類型限制在以下幾種類型:

居然還有Hex類型,不過我們平時用int或者String比較多吧,我想很多人都會這選擇。

接下來的邏輯和之前寫的DDL的差不多,就是遍歷segment,要注意,此時是在循環中的,就是對要寫入的行一行一行的循環遍歷中的。

        rewrittenTable := segments[0].Table
        backend := segments[0].Backend
        rangi := segments[0].Range.String()
        val, ok := vals[rewrittenTable]
        if !ok {
            val = &valTuple{
                backend: backend,
                table:   rewrittenTable,
                rangi:   rangi,
                vals:    make(sqlparser.Values, 0, 16),
            }
            vals[rewrittenTable] = val
        }
        val.vals = append(val.vals, row)

我們可以認爲這段代碼將要寫入的值,對應的分片條件都整合在了一起,放在了一個map中。

代碼邏輯的最後,會將查詢和路由信息一起寫入plan的Query域中。

這就比DDL的優化器要好玩一點了,因爲還有設計到分片路由信息的部分。我估計查詢計劃會更有意思。

3. 小結

我們來打算調試的,但是Windows下竟然會出現編譯失敗的問題,我查了一下錯誤的原因,似乎就是因爲平臺問題。而且我在MacOS上編譯運行也是沒有問題的。

還是期待官方早點寫詳細的中文文檔。

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