学习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上编译运行也是没有问题的。

还是期待官方早点写详细的中文文档。

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