美文网首页IT@程序员猿媛RadonDBGo
学习RadonDB源码(五)

学习RadonDB源码(五)

作者: 有财君 | 来源:发表于2019-05-19 23:36 被阅读1次

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

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

    相关文章

      网友评论

        本文标题:学习RadonDB源码(五)

        本文链接:https://www.haomeiwen.com/subject/hluczqtx.html