• Doris 实战 踏雪扬尘


    概述

    安装

    FE URL地址如下:
    http://hostname:8030/system?path=//frontends
    SHOW PROC '/backends';

    SHOW PROC '/frontends';

    简单使用

    Doris 采用 MySQL 协议进行通信,用户可通过 MySQL client 或者 MySQL JDBC连接到 Doris 集群。选择 MySQL client 版本时建议采用5.1 之后的版本

    HELP CREATE TABLE; 语法提示支持
    Doris支持支持单分区和复合分区两种建表方式。
    在复合分区中:

    • 第一级称为 Partition,即分区。用户可以指定某一维度列作为分区列(当前只支持整型和时间类型的列),并指定每个分区的取值范围。
    • 第二级称为 Distribution,即分桶。用户可以指定一个或多个维度列以及桶数对数据进行 HASH 分布。
      单分区:
      建立一个名字为 table1 的逻辑表。分桶列为 siteid,桶数为 10。
    CREATE TABLE table1
    (
        siteid INT DEFAULT '10',
        citycode SMALLINT,
        username VARCHAR(32) DEFAULT '',
        pv BIGINT SUM DEFAULT '0'
    )
    AGGREGATE KEY(siteid, citycode, username)
    DISTRIBUTED BY HASH(siteid) BUCKETS 10
    PROPERTIES("replication_num" = "1");
    

    复合分区
    我们使用 event_day 列作为分区列,建立3个分区: p201706, p201707, p201708

    p201706:范围为 [最小值, 2017-07-01)
    p201707:范围为 [2017-07-01, 2017-08-01)
    p201708:范围为 [2017-08-01, 2017-09-01)
    注意区间为左闭右开。

    每个分区使用 siteid 进行哈希分桶,桶数为10
    AGGREGATE KEY(event_day, siteid, citycode, username) 代表按照这些列聚合,然后对 pv 列进行sum 计算。
    PROPERTIES("replication_num" = "1"); 代表一副本,但是为了高可用生产环境中建议设置为 3 副本,保证高可用

    CREATE TABLE table2
    (
        event_day DATE,
        siteid INT DEFAULT '10',
        citycode SMALLINT,
        username VARCHAR(32) DEFAULT '',
        pv BIGINT SUM DEFAULT '0'
    )
    AGGREGATE KEY(event_day, siteid, citycode, username)
    PARTITION BY RANGE(event_day)
    (
        PARTITION p201706 VALUES LESS THAN ('2017-07-01'),
        PARTITION p201707 VALUES LESS THAN ('2017-08-01'),
        PARTITION p201708 VALUES LESS THAN ('2017-09-01')
    )
    DISTRIBUTED BY HASH(siteid) BUCKETS 10
    PROPERTIES("replication_num" = "1");
    

    导入数据

    流式导入
    流式导入通过 HTTP 协议向 Doris 传输数据,可以不依赖其他系统或组件直接导入本地数据。详细语法帮助可以参阅 HELP STREAM LOAD;
    示例1:以 "table1_20170707" 为 Label,使用本地文件 table1_data 导入 table1 表。
    FE_HOST 是任一 FE 所在节点 IP,8030 为 fe.conf 中的 http_port。
    可以使用任一 BE 的 IP,以及 be.conf 中的 webserver_port 进行导入。如:BE_HOST:8040

    curl --location-trusted -u test:test_passwd -H "label:table1_20170707" -H "column_separator:," -T table1_data http://FE_HOST:8030/api/example_db/table1/_stream_load

    curl --location-trusted -u 'root:xxxxx' -H "label:table1_20170707" -H "column_separator:," -T ./table1_data http://172.26.xx.xx:8030/api/rtdw_bm/table1/_stream_load

    table1_data数据结构大概如下:
    1,1,jim,2
    2,1,grace,2
    3,2,tom,2
    4,3,bush,3
    5,3,helen,3

    curl --location-trusted -u test:test -H "label:table2_20170707" -H "column_separator:|" -T table2_data http://127.0.0.1:8030/api/example_db/table2/_stream_load
    注意事项:

    1. 采用流式导入建议文件大小限制在 10GB 以内,过大的文件会导致失败重试代价变大。
    2. 每一批导入数据都需要取一个 Label,Label 最好是一个和一批数据有关的字符串,方便阅读和管理。Doris 基于 Label 保证在一个Database 内,同一批数据只可导入成功一次。失败任务的 Label 可以重用。
    3. 流式导入是同步命令。命令返回成功则表示数据已经导入,返回失败表示这批数据没有导入。

    Broker 导入
    Broker 导入通过部署的 Broker 进程,读取外部存储上的数据进行导入。
    示例:以 "table1_20170708" 为 Label,将 HDFS 上的文件导入 table1 表

    LOAD LABEL table1_20170708
    (
        DATA INFILE("hdfs://your.namenode.host:port/dir/table1_data")
        INTO TABLE table1
    )
    WITH BROKER hdfs 
    (
        "username"="hdfs_user",
        "password"="hdfs_password"
    )
    PROPERTIES
    (
        "timeout"="3600",
        "max_filter_ratio"="0.1"
    );
    

    Broker 导入是异步命令。以上命令执行成功只表示提交任务成功。导入是否成功需要通过 SHOW LOAD; 查看。如:
    SHOW LOAD WHERE LABEL = "table1_20170708";

    高级使用

    表结构变更
    我们新增一列 uv,类型为 BIGINT,聚合类型为 SUM,默认值为 0: SUM 是 Doris区别于 MySQL的地方
    其代表着可以用于预聚合计算,也就相当于CK的聚合视图

    ALTER TABLE table1 ADD COLUMN uv BIGINT SUM DEFAULT '0' after pv;
    -- 以下查看 ALTER 进度, 查看列操作进度
    SHOW ALTER TABLE COLUMN;
    



    Rollup
    Rollup 可以理解为 Table 的一个物化索引结构。物化 是因为其数据在物理上独立存储,而 索引 的意思是,Rollup可以调整列顺序以增加前缀索引的命中率,也可以减少key列以增加数据的聚合度。
    如果业务方经常有看城市 pv 总量的需求,可以建立一个只有 citycode, pv 的rollup。
    ALTER TABLE table1 ADD ROLLUP rollup_city(citycode, pv);

    以下命令查看ROLLUP任务进度
    SHOW ALTER TABLE ROLLUP;

    Rollup 建立完成之后可以使用 DESC table1 ALL 查看表的 Rollup 信息。
    Rollup 建立之后,查询不需要指定 Rollup 进行查询。还是指定原有表进行查询即可。程序会自动判断是否应该使用 Rollup。是否命中 Rollup可以通过 EXPLAIN your_sql; 命令进行查看。
    如下实验结果发现 rollup: rollup_city 使用了该 rollup_city 预上卷结果

    数据表的查询注意事项

    1. 内存限制
      为了防止用户的一个查询可能因为消耗内存过大。查询进行了内存控制,一个查询任务,在单个 BE 节点上默认使用不超过 2GB 内存。
      用户在使用时,如果发现报 Memory limit exceeded 错误,一般是超过内存限制了。
      遇到这种问题优先优化SQL语句,如果确切发现2GB内存不能满足,可以手动设置内存参数。

    显示查询内存限制,查询资源使用

    SHOW VARIABLES LIKE "%mem_limit%";
    以下看到默认内存大小是2GB

    可以使用如下进行修改
    SET exec_mem_limit = 8589934592;
    以上修改都是 session级别的, 如果需要修改全局变量,可以这样设置:SET GLOBAL exec_mem_limit = 8589934592;。设置完成后,断开 session 重新登录,参数将永久生效。

    1. 查询超时
      当前默认查询时间设置为最长为 300 秒,如果一个查询在 300 秒内没有完成,则查询会被 Doris 系统 cancel 掉。用户可以通过这个参数来定制自己应用的超时时间,实现类似 wait(timeout) 的阻塞方式。
      SHOW VARIABLES LIKE "%query_timeout%";

      SET query_timeout = 60;

    2. Broadcast/Shuffle Join
      系统默认实现 Join 的方式,是将小表进行条件过滤后,将其广播到大表所在的各个节点上,形成一个内存 Hash 表,然后流式读出大表的数据进行Hash Join。但是如果当小表过滤后的数据量无法放入内存的话,此时 Join 将无法完成,通常的报错应该是首先造成内存超限。
      如果遇到上述情况,建议显式指定 Shuffle Join,也被称作 Partitioned Join。即将小表和大表都按照 Join 的 key 进行 Hash,然后进行分布式的 Join。这个对内存的消耗就会分摊到集群的所有计算节点上。
      Doris会自动尝试进行 Broadcast Join,如果预估小表过大则会自动切换至 Shuffle Join。注意,如果此时显式指定了 Broadcast Join 也会自动切换至 Shuffle Join。
      使用 Broadcast Join(显式指定):

    select sum(table1.pv) from table1 join [broadcast] table2 where table1.siteid = 2;
    

    使用 Shuffle Join:

    select sum(table1.pv) from table1 join [shuffle] table2 where table1.siteid = 2;
    
    1. 查询重试和高可用
      当部署多个 FE 节点时,用户可以在多个 FE 之上部署负载均衡层来实现 Doris 的高可用。
    • 自己在应用层代码进行重试和负载均衡。比如发现一个连接挂掉,就自动在其他连接上进行重试。应用层代码重试需要应用自己配置多个doris前端节点地址。
    • 如果使用 mysql jdbc connector 来连接Doris,可以使用 jdbc 的自动重试机制:
      jdbc:mysql://[host:port],[host:port].../[database][?propertyName1][=propertyValue1][&propertyName2][=propertyValue2]...
    • 应用可以连接到和应用部署到同一机器上的 MySQL Proxy,通过配置 MySQL Proxy 的 Failover 和 Load Balance 功能来达到目的。
      http://dev.mysql.com/doc/refman/5.6/en/mysql-proxy-using.html

    数据模型、ROLLUP 及前缀索引

    Column 可以分为两大类:Key 和 Value。从业务角度看,Key 和 Value 可以分别对应维度列和指标列。
    Doris 的数据模型主要分为3类:

    • Aggregate
    • Uniq
    • Duplicate

    Aggregate 模型

    就类比于 CK 的 聚合表, 通过聚合视图提前进行数据聚合,不存储明细
    表中的列按照是否设置了 AggregationType,分为 Key (维度列) 和 Value(指标列)。没有设置 AggregationType 的,如 user_id、date、age ... 等称为 Key,而设置了 AggregationType 的称为 Value。
    当我们导入数据时,对于 Key 列相同的行会聚合成一行,而 Value 列会按照设置的 AggregationType 进行聚合。 AggregationType 目前有以下四种聚合方式:

    1. SUM:求和,多行的 Value 进行累加。
    2. REPLACE:替代,下一批数据中的 Value 会替换之前导入过的行中的 Value。
    3. MAX:保留最大值。
    4. MIN:保留最小值。
      经过聚合,Doris 中最终只会存储聚合后的数据。换句话说,即明细数据会丢失,用户不能够再查询到聚合前的明细数据了。
      对于聚合表,数据聚合大概分为三个阶段
      1、每一批次数据导入的 ETL 阶段。该阶段会在每一批次导入的数据内部进行聚合。
      2、底层 BE 进行数据 Compaction 的阶段。该阶段,BE 会对已导入的不同批次的数据进行进一步的聚合。
      3、数据查询阶段。在数据查询时,对于查询涉及到的数据,会进行对应的聚合。

    保留明细数据

    即增加了一列 timestamp,记录精确到秒的数据灌入时间。因为加入了 timestamp 可以保证 key 肯定不一样,即使在聚合模型下,也能保证保存完整的数据。

    Uniq 模型

    在某些多维分析场景下,用户更关注的是如何保证 Key 的唯一性,即如何获得 Primary Key 唯一性约束。因此,我们引入了 Uniq 的数据模型。
    此种方式就类似于 Aggregate 模型的把非 UNIQUE KEY 的 column 设置为 REPLACE 指标

    CREATE TABLE IF NOT EXISTS example_db.expamle_tbl
    (
        `user_id` LARGEINT NOT NULL COMMENT "用户id",
        `username` VARCHAR(50) NOT NULL COMMENT "用户昵称",
        `city` VARCHAR(20) COMMENT "用户所在城市",
        `age` SMALLINT COMMENT "用户年龄",
        `sex` TINYINT COMMENT "用户性别",
        `phone` LARGEINT COMMENT "用户电话",
        `address` VARCHAR(500) COMMENT "用户地址",
        `register_time` DATETIME COMMENT "用户注册时间"
    )
    UNIQUE KEY(`user_id`, `username`)
    ... /* 省略 Partition 和 Distribution 信息 */
    ;
    

    Duplicate 模型

    在某些多维分析场景下,数据既没有主键,也没有聚合需求。因此,我们引入 Duplicate 数据模型来满足这类需求。举例说明。
    这种数据模型区别于 Aggregate 和 Uniq 模型。数据完全按照导入文件中的数据进行存储,不会有任何聚合。即使两行数据完全相同,也都会保留。
    而在建表语句中指定的 DUPLICATE KEY,只是用来指明底层数据按照那些列进行排序。(更贴切的名称应该为 “Sorted Column”,这里取名 “DUPLICATE KEY” 只是用以明确表示所用的数据模型。

    CREATE TABLE IF NOT EXISTS example_db.expamle_tbl
    (
        `timestamp` DATETIME NOT NULL COMMENT "日志时间",
        `type` INT NOT NULL COMMENT "日志类型",
        `error_code` INT COMMENT "错误码",
        `error_msg` VARCHAR(1024) COMMENT "错误详细信息",
        `op_id` BIGINT COMMENT "负责人id",
        `op_time` DATETIME COMMENT "处理时间"
    )
    DUPLICATE KEY(`timestamp`, `type`)
    ... /* 省略 Partition 和 Distribution 信息 */
    ;
    

    ROLLUP

    ROLLUP 在多维分析中是“上卷”的意思,即将数据按某种指定的粒度进行进一步聚合。
    在 Doris 中,我们将用户通过建表语句创建出来的表称为 Base 表(Base Table)。Base 表中保存着按用户建表语句指定的方式存储的基础数据。
    在 Base 表之上,我们可以创建任意多个 ROLLUP 表。这些 ROLLUP 的数据是基于 Base 表产生的,并且在物理上是独立存储的。
    ROLLUP 表的基本作用,在于在 Base 表的基础上,获得更粗粒度的聚合数据。
    对 table 创建一个 ROLLUP (user_id,cost) 则以下 SQL Doris 会自动命中这个 ROLLUP 表,从而只需扫描极少的数据量,即可完成这次聚合查询。
    SELECT user_id, sum(cost) FROM table GROUP BY user_id;
    在 Doris 里 Rollup 作为一份聚合物化视图,其在查询中可以起到两个作用:

    • 索引
    • 聚合数据(仅用于聚合模型,即aggregate key)

    前缀索引与 ROLLUP

    前缀索引
    Doris 不支持在任意列上创建索引。Doris 这类 MPP 架构的 OLAP 数据库,通常都是通过提高并发,来处理大量数据的。 这块是 MPP架构和 MySQL的最本质区别,MySQL适合在多个列上建索引,提高查询性能。
    MPP的话 主要是使用多进程多线程并发模型提高OLAP分析的效率,底层存储是 Sorted String Table ,基于排序列查询会非常快。此处类比 CK 的 order by 属性当建表时候不指定主键时,order by 字段组就是主键,这些字段组会建立索引,并且每个shard 数据存储的排序方式
    就是order by 指定的字段。 CK 的这种索引是稀疏索引,可以使用很小的空间对大量的数据提供所有能力。

    本质上,Doris 的数据存储在类似 SSTable(Sorted String Table)的数据结构中。该结构是一种有序的数据结构,可以按照指定的列进行排序存储。在这种数据结构上,以排序列作为条件进行查找,会非常的高效。
    在 Aggregate、Uniq 和 Duplicate 三种数据模型中。底层的数据存储,是按照各自建表语句中,AGGREGATE KEY、UNIQ KEY 和 DUPLICATE KEY 中指定的列进行排序存储的。
    而前缀索引,即在排序的基础上,实现的一种根据给定前缀列,快速查询数据的索引方式。
    我们将一行数据的前 36 个字节 作为这行数据的前缀索引。当遇到 VARCHAR 类型时,前缀索引会直接截断。
    当我们的查询条件,是前缀索引的前缀时,可以极大的加快查询速度。
    所以在建表时,正确的选择列顺序,能够极大地提高查询效率。
    因为建表时已经指定了列顺序,所以一个表只有一种前缀索引。 可以调整 ROLLUP 修改列顺序,引导使用索引。

    Doris 会把 Base/Rollup 表中的前 36 个字节(有 varchar 类型则可能导致前缀索引不满 36 个字节,varchar 会截断前缀索引,并且最多使用 varchar 的 20 个字节)在底层存储引擎单独生成一份排序的稀疏索引数据(数据也是排序的,用索引定位,然后在数据中做二分查找)

    Base 表结构如下:

    ColumnName Type
    user_id BIGINT
    age INT
    message VARCHAR(100)
    max_dwell_time DATETIME
    min_dwell_time DATETIME

    我们可以在此基础上创建一个 ROLLUP 表:

    ColumnName Type
    age INT
    user_id BIGINT
    message VARCHAR(100)
    max_dwell_time DATETIME
    min_dwell_time DATETIME

    ROLLUP 的几点说明

    • ROLLUP 最根本的作用是提高某些查询的查询效率(无论是通过聚合来减少数据量,还是修改列顺序以匹配前缀索引)。因此 ROLLUP 的含义已经超出了 “上卷” 的范围。这也是为什么我们在源代码中,将其命名为 Materialized Index(物化索引)的原因。
    • ROLLUP 是附属于 Base 表的,可以看做是 Base 表的一种辅助数据结构。用户可以在 Base 表的基础上,创建或删除 ROLLUP,但是不能在查询中显式的指定查询某 ROLLUP。是否命中 ROLLUP 完全由 Doris 系统自动决定。
    • ROLLUP 的数据是独立物理存储的。因此,创建的 ROLLUP 越多,占用的磁盘空间也就越大。同时对导入速度也会有影响(导入的ETL阶段会自动产生所有 ROLLUP 的数据),但是不会降低查询效率(只会更好)。
    • ROLLUP 的数据更新与 Base 表是完全同步的。用户无需关心这个问题。
    • ROLLUP 中列的聚合方式,与 Base 表完全相同。在创建 ROLLUP 无需指定,也不能修改。
    • 查询能否命中 ROLLUP 的一个必要条件(非充分条件)是,查询所涉及的所有列(包括 select list 和 where 中的查询条件列等)都存在于该 ROLLUP 的列中。否则,查询只能命中 Base 表。
    • 某些类型的查询(如 count(*))在任何条件下,都无法命中 ROLLUP。具体参见接下来的 聚合模型的局限性 一节。
    • 可以通过 EXPLAIN your_sql; 命令获得查询执行计划,在执行计划中,查看是否命中 ROLLUP。
    • 可以通过 DESC tbl_name ALL; 语句显示 Base 表和所有已创建完成的 ROLLUP。

    聚合模型的局限性

    Doris select count(*) 的本质就是 扫描所有的 AGGREGATE KEY 列,查询时聚合, 所以性能比较弱
    可以的优化手段包括:

    1. 增加一个 count BIGINT SUM 类型的列,要是 AGGREGATE KEY 列不重复,直接 select count 就行
    2. 要是有重复的, 改为 replace类型,直接 select sum(count) 就行
      Duplicate 模型数据不会重复没有聚合语义, 随便select 一个字段都可以

    数据模型的选择建议

    1. Aggregate 模型可以通过预聚合,极大地降低聚合查询时所需扫描的数据量和查询的计算量,非常适合有固定模式的报表类查询场景。但是该模型对 count(*) 查询很不友好。同时因为固定了 Value 列上的聚合方式,在进行其他类型的聚合查询时,需要考虑语意正确性。
    2. Uniq 模型针对需要唯一主键约束的场景,可以保证主键唯一性约束。但是无法利用 ROLLUP 等预聚合带来的查询优势(因为本质是 REPLACE,没有 SUM 这种聚合方式)。
    3. Duplicate 适合任意维度的 Ad-hoc 查询。虽然同样无法利用预聚合的特性,但是不受聚合模型的约束,可以发挥列存模型的优势(只读取相关列,而不需要读取所有 Key 列)。

    数据划分

    Row & Column
    Column 可以分为两大类:Key 和 Value。从业务角度看,Key 和 Value 可以分别对应维度列和指标列。从聚合模型的角度来说,Key 列相同的行,会聚合成一行。其中 Value 列的聚合方式由用户在建表时指定。
    一个 Tablet 只属于一个 Partition。而一个 Partition 包含若干个 Tablet。
    若干个 Partition 组成一个 Table。Partition 可以视为是逻辑上最小的管理单元。数据的导入与删除,都可以或仅能针对一个 Partition 进行。
    也就是 一个 Partition 可能由多个 Tablet 进行存储。

    数据划分

    -- Range Partition
    
    CREATE TABLE IF NOT EXISTS example_db.expamle_range_tbl
    (
        `user_id` LARGEINT NOT NULL COMMENT "用户id",
        `date` DATE NOT NULL COMMENT "数据灌入日期时间",
        `timestamp` DATETIME NOT NULL COMMENT "数据灌入的时间戳",
        `city` VARCHAR(20) COMMENT "用户所在城市",
        `age` SMALLINT COMMENT "用户年龄",
        `sex` TINYINT COMMENT "用户性别",
        `last_visit_date` DATETIME REPLACE DEFAULT "1970-01-01 00:00:00" COMMENT "用户最后一次访问时间",
        `cost` BIGINT SUM DEFAULT "0" COMMENT "用户总消费",
        `max_dwell_time` INT MAX DEFAULT "0" COMMENT "用户最大停留时间",
        `min_dwell_time` INT MIN DEFAULT "99999" COMMENT "用户最小停留时间"
    )
    ENGINE=olap
    AGGREGATE KEY(`user_id`, `date`, `timestamp`, `city`, `age`, `sex`)
    PARTITION BY RANGE(`date`)
    (
        PARTITION `p201701` VALUES LESS THAN ("2017-02-01"),
        PARTITION `p201702` VALUES LESS THAN ("2017-03-01"),
        PARTITION `p201703` VALUES LESS THAN ("2017-04-01")
    )
    DISTRIBUTED BY HASH(`user_id`) BUCKETS 16
    PROPERTIES
    (
        "replication_num" = "3",
        "storage_medium" = "SSD",
        "storage_cooldown_time" = "2018-01-01 12:00:00"
    );
    
    -- List Partition
    
    CREATE TABLE IF NOT EXISTS example_db.expamle_list_tbl
    (
        `user_id` LARGEINT NOT NULL COMMENT "用户id",
        `date` DATE NOT NULL COMMENT "数据灌入日期时间",
        `timestamp` DATETIME NOT NULL COMMENT "数据灌入的时间戳",
        `city` VARCHAR(20) COMMENT "用户所在城市",
        `age` SMALLINT COMMENT "用户年龄",
        `sex` TINYINT COMMENT "用户性别",
        `last_visit_date` DATETIME REPLACE DEFAULT "1970-01-01 00:00:00" COMMENT "用户最后一次访问时间",
        `cost` BIGINT SUM DEFAULT "0" COMMENT "用户总消费",
        `max_dwell_time` INT MAX DEFAULT "0" COMMENT "用户最大停留时间",
        `min_dwell_time` INT MIN DEFAULT "99999" COMMENT "用户最小停留时间"
    )
    ENGINE=olap
    AGGREGATE KEY(`user_id`, `date`, `timestamp`, `city`, `age`, `sex`)
    PARTITION BY LIST(`city`)
    (
        PARTITION `p_cn` VALUES IN ("Beijing", "Shanghai", "Hong Kong"),
        PARTITION `p_usa` VALUES IN ("New York", "San Francisco"),
        PARTITION `p_jp` VALUES IN ("Tokyo")
    )
    DISTRIBUTED BY HASH(`user_id`) BUCKETS 16
    PROPERTIES
    (
        "replication_num" = "3",
        "storage_medium" = "SSD",
        "storage_cooldown_time" = "2018-01-01 12:00:00"
    );
    
    

    分区与分桶
    DORIS 支持2种数据划分方式
    第一层是 Partition,支持 Range 和 List 的划分方式。
    第二层是 Bucket(Tablet),仅支持 Hash 的划分方式。
    Partition:
    Partition 列可以指定一列或多列。分区类必须为 KEY 列。
    当不使用 Partition 建表时,系统会自动生成一个和表名同名的,全值范围的 Partition

    Bucket:
    如果使用了 Partition,则 DISTRIBUTED ... 语句描述的是数据在各个分区内的划分规则。如果不使用 Partition,则描述的是对整个表的数据的划分规则。 也就是说
    分桶列的选择,是在 查询吞吐 和 查询并发 之间的一种权衡:

    1. 分桶越细则查询没到细节末节时,会触发全桶扫描, 适合这样能均衡的把数据均衡分布到不同的桶中,适合高吞吐低延迟的查询
    2. 分桶粗点, 可能就一个字段,则会导致点查大部分只在一个bucket中, 适合低延时搞并发查询,但是吞吐低一些, 试想大量的数据都需要从一个 bucket出,尤其是存储数据量非常大的适合
      一个表的 Tablet 总数量等于 (Partition num * Bucket num)。
      一个表的 Tablet 数量,在不考虑扩容的情况下,推荐略多于整个集群的磁盘数量。
      单个 Tablet 的数据量理论上没有上下界,但建议在 1G - 10G 的范围内。如果单个 Tablet 数据量过小,则数据的聚合效果不佳,且元数据管理压力大。如果数据量过大,则不利于副本的迁移、补齐,且会增加 Schema Change 或者 Rollup 操作失败重试的代价(这些操作失败重试的粒度是 Tablet)。

    ENGINE:
    本示例中,ENGINE 的类型是 olap,即默认的 ENGINE 类型。在 Doris 中,只有这个 ENGINE 类型是由 Doris 负责数据管理和存储的。其他 ENGINE 类型,如 mysql、broker、es 等等,本质上只是对外部其他数据库或系统中的表的映射,以保证 Doris 可以读取这些数据。而 Doris 本身并不创建、管理和存储任何非 olap ENGINE 类型的表和数据。
    保留字。当用户自定义名称遇到保留字时,需要用反引号 `` 引起来。建议所有自定义名称使用这个符号引起来。

  • 相关阅读:
    数据清洗
    JAVA多线程三种实现方式
    QT-4.8.6 编译配置过程
    qt 编译问题总结
    [转载]tslib1.4与Qt4.8.6的交叉编译与移植
    STC12C5A60S2 @ 22.0184Mhz 精确延时
    STC12C5A60S2 双串口通信
    C# Bitmap 复制
    TextMate2 最新版下载及源码编译过程
    mac系统 PHP Nginx环境变量修改
  • 原文地址:https://www.cnblogs.com/yyystar/p/15571114.html
Copyright © 2020-2023  润新知