美文网首页Doris
0.1 Doris 模型介绍

0.1 Doris 模型介绍

作者: 冰菓_ | 来源:发表于2023-07-18 01:26 被阅读0次

    Apache doris 数据模型 (hf200012.github.io)

    一. 数据划分

    Row & Column

    一张表包括行(Row)和列(Column);
    Row 即用户的一行数据。Column 用于描述一行数据中不同的字段。
    doris中的列分为两类:key列和value列
    key列在doris中有两种作用:
    聚合表模型中,key是聚合和排序的依据
    其他表模型中,key是排序依据

    分区与分桶
    • partition(分区):是在逻辑上将一张表按行(横向)划分
    • tablet(又叫bucket,分桶):在物理上对一个分区再按行(横向)划分

    range分区创建语法

    CREATE TABLE IF NOT EXISTS test.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 "用户性别"
    )
    ENGINE=OLAP
    DUPLICATE KEY(`user_id`, `date`) -- 表模型
    -- 分区的语法
    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 1;
    
    Bucket
    • 如果使用了 Partition,则 DISTRIBUTED ... 语句描述的是数据在各个分区内的划分规则。如果不使用 Partition,则描述的是对整个表的数据的划分规则。
    • 分桶列可以是多列,但必须为 Key 列。分桶列可以和 Partition 列相同或不同。
    • 分桶列的选择,是在 查询吞吐 和 查询并发 之间的一种权衡:
      1. 如果选择多个分桶列,则数据分布更均匀。如果一个查询条件不包含所有分桶列的等值条件,那么该查询会触发所有分桶同时扫描,这样查询的吞吐会增加,单个查询的延迟随之降低。这个方式适合大吞吐低并发的查询场景。
      2. 如果仅选择一个或少数分桶列,则对应的点查询可以仅触发一个分桶扫描。此时,当多个点查询并发时,这些查询有较大的概率分别触发不同的分桶扫描,各个查询之间的IO影响较小(尤其当不同桶分布在不同磁盘上时),所以这种方式适合高并发的点查询场景。

    二. 数据表设计

    • partition(分区):是在逻辑上将一张表按行(横向)划分
    • tablet(又叫bucket,分桶):在物理上对一个分区再按行(横向)划分
    Duplicate 明细模型
    CREATE TABLE IF NOT EXISTS   demo.duplicate_demo
    (
        `timestamp` DATETIME NOT NULL COMMENT "日志时间",
        `type` INT NOT NULL COMMENT "日志类型",
        `action`  VARCHAR(1024) NOT NULL COMMENT "日志",
        `op_time` DATETIME COMMENT "处理时间"
    )
    DUPLICATE KEY(`timestamp`, `type`)
    DISTRIBUTED BY HASH(`timestamp`) BUCKETS 1
    PROPERTIES (
        "replication_allocation" = "tag.location.default: 1"
    );
    

    DUPLICATE KEY,只是用来指明底层数据按照那些列进行排序,在 DUPLICATE KEY 的选择上,建议适当的选择前 2-4 列

    数据导入测试
    insert into demo.duplicate_demo  values\
    ('2017-10-01 08:00:05',1,'not found page', '2017-10-01 08:00:05'),\
    ('2017-10-01 08:00:05',1,'not found page', '2017-10-01 08:00:05'),\
    ('2017-10-01 08:00:05',2,'not found page', '2017-10-01 08:00:06'),\
    ('2017-10-01 08:00:06',2,'not found page', '2017-10-01 08:00:07');
    
    Aggregate 聚合模型

    是相同key的数据进行自动聚合的表模型。表中的列按照是否设置了 AggregationType,分为 Key(维度列)和 Value(指标列),没有设置 AggregationType 的称为 Key,设置了 AggregationType 的称为 Value。当我们导入数据时,对于 Key 列相同的行会聚合成一行,而 Value 列会按照设置的AggregationType 进行聚合。AggregationType 目前有以下四种聚合方式:

    • SUM:求和,多行的 Value 进行累加。
    • REPLACE:替代,下一批数据中的 Value 会替换之前导入过的行中的 Value。
    • REPLACE_IF_NOT_NULL :当遇到 null 值则不更新。
    • MAX:保留最大值。
    • MIN:保留最小值。
    -- 这是一个用户消费和行为记录的数据表
    CREATE TABLE IF NOT EXISTS ex_user
    (
     `user_id` LARGEINT NOT NULL COMMENT "用户 id",
     `date` DATE 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`, `city`, `age`, `sex`)
    -- 分区
    -- 分桶
    DISTRIBUTED BY HASH(`user_id`) BUCKETS 1
    PROPERTIES (
        "replication_allocation" = "tag.location.default: 1"
    );
    
    insert into ex_user values\
    (10000,'2017-10-01','北京',20,0,'2017-10-01 06:00:00',20,10,10),\
    (10000,'2017-10-01','北京',20,0,'2017-10-01 07:00:00',15,2,2),\
    (10001,'2017-10-01','北京',30,1,'2017-10-01 17:05:45',2,22,22),\
    (10002,'2017-10-02','上海',20,1,'2017-10-02 12:59:12',200,5,5),\
    (10003,'2017-10-02','广州',32,0,'2017-10-02 11:20:00',30,11,11),\
    (10004,'2017-10-01','深圳',35,0,'2017-10-01 10:00:15',100,3,3),\
    (10004,'2017-10-03','深圳',35,0,'2017-10-03 10:20:22',11,6,6);
    

    数据在不同时间,可能聚合的程度不一致。比如一批数据刚导入时,可能还未与之前已存在的数据进行聚合。但是对于用户而言,用户只能查询到聚合后的数据。即不同的聚合程度对于用户查询而言是透明的。用户需始终认为数据以最终的完成的聚合程度存在,而不应假设某些聚合还未发生。聚合模型的局限性
    例如在:在 count() 查询中,Doris 必须扫描所有的 AGGREGATE KEY 列,并且聚合后,才能得到语意正确的结果,当聚合列非常多时,count() 查询需要扫描大量的数据,效率低下。因此, 当业务上有频繁的 count(*) 查询时 ,我们建议用户 通过增加一个值恒为 1 的,聚合类型为 SUM 的列来模拟 count

    UNIQUE 模型

    是相同key的数据进行自动去重的表模型。在某些多维分析场景下,用户更关注的是如何保证 Key 的唯一性,即如何获得 Primary Key 唯一性约束。因此,引入了 Uniq 的数据模型。该模型本质上是聚合模型的一个特例,也是一种简化的表结构表示方式。
    Uniq 模型完全可以用聚合模型中的 REPLACE 方式替代。其内部的实现方式和数据存储方式也完全一样。

    
        CREATE TABLE IF NOT EXISTS user
        (
        -- key列
         `user_id` LARGEINT NOT NULL COMMENT "用户 id",
         `username` VARCHAR(50) NOT NULL COMMENT "用户昵称",
         -- value列
         `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`)
        DISTRIBUTED BY HASH(`user_id`) BUCKETS 1
    PROPERTIES (
        "replication_allocation" = "tag.location.default: 1"
    );
    
    
    insert into user values\
    (10000,'zss','北京',18,0,12345678910,'北京朝阳区 ','2017-10-01 07:00:00'),\
    (10000,'zss','北京',19,0,12345678910,'北京顺义区 ','2018-10-01 07:00:00'),\
    (10000,'lss','北京',20,0,12345678910,'北京海淀区','2017-11-15 06:10:20');
    

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

    三. 索引

    索引用于帮助快速过滤或查找数据。
    目前 Doris 主要支持两类索引:

    • 内建的智能索引:包括前缀索引和 ZoneMap 索引。
    • 用户创建的二级索引:包括 Bloom Filter 索引 和 Bitmap倒排索引。
      其中 ZoneMap 索引是在列存格式上,对每一列自动维护的索引信息,包括 Min/Max,Null 值个数等等。这种索引对用户透明。

    doris中,对于前缀索引有如下约束:

    1. 他的索引键最大长度是36个字节
    2. 当他遇到了varchar数据类型的时候,即使没有超过36个字节,也会自动截断


      自动截断
    Doris BloomFilter

    Doris BloomFilter适用场景
    满足以下几个条件时可以考虑对某列建立Bloom Filter 索引:

    1. BloomFilter是在无法利用前缀索引的查询场景中,来加快查询速度的。
    2. 查询会根据该列高频过滤,而且查询条件大多是 in 和 = 过滤。
    3. 不同于Bitmap, BloomFilter适用于高基数列。比如UserID。因为如果创建在低基数的列上,比如 “性别” 列,则每个Block几乎都会包含所有取值,导致BloomFilter索引失去意义。字段随机
      Doris BloomFilter使用注意事项
    4. 不支持对Tinyint、Float、Double 类型的列建Bloom Filter索引。
    5. Bloom Filter索引只对 in 和 = 过滤查询有加速效果。
    6. 可以通过explain来查看命中了哪种索引 --没办法查看
    Bitmap 索引
    Bitmap 索引
    CREATE INDEX [IF NOT EXISTS] index_name ON table1 (siteid) USING BITMAP COMMENT 'balabala';
    
    create index 索引名称  on 表名(给什么字段创建bitmap索引) using bitmap COMMENT 'balabala';
    
    create index user_id_bitmap on sale_detail_bloom(sku_id) USING BITMAP COMMENT '使用user_id创建的bitmap索引';
    
    • bitmap 索引仅在单列上创建。
    • bitmap 索引能够应用在 Duplicate、Uniq 数据模型的所有列和 Aggregate模型的key列上。
    • bitmap 索引支持的数据类型如下:(老版本只支持bitmap类型)
      TINYINT,SMALLINT,INT,BIGINT,CHAR,VARCHAR,DATE,DATETIME,LARGEINT,DECIMAL,BOOL
    • bitmap索引仅在 Segment V2 下生效(Segment V2是升级版本的文件格式)。当创建 index 时,表的存储格式将默认转换为 V2 格式

    四. Rollup

    ROLLUP 在多维分析中是“上卷”的意思,即将数据按某种指定的粒度进行进一步聚合。

    alter table aggregate表名 add rollup "rollup表的表名" (user_id,city,date,cost);
    
    alter table ex_user add rollup rollup_ucd_cost(user_id,city,date,cost);
    alter table ex_user add rollup rollup_u_cost(user_id,cost);
    alter table ex_user add rollup rollup_cd_cost(city,date,cost);
    
    alter table ex_user drop rollup rollup_u_cost;
    alter table ex_user drop rollup rollup_cd_cost;
    
    --如果是replace聚合类型得value,需要指定所有得key
    -- alter table ex_user add rollup rollup_cd_visit(city,date,last_visit_date);
    -- ERROR 1105 (HY000): errCode = 2, detailMessage = Rollup should contains 
    -- all keys if there is a REPLACE value
    
    --添加完成之后可以show一下,看看底层得rollup有没有执行完成
    SHOW ALTER TABLE ROLLUP;
    
    Doris 会自动命中这个 ROLLUP 表,从而只需扫描极少的数据量,即可完成这次聚合查询。
    
    explain SELECT user_id, sum(cost) FROM ex_user GROUP BY user_id;
    

    五. 物化视图

    就是查询结果预先存储起来的特殊的表。物化视图的出现主要是为了满足用户,既能对原始明细数据的任意维度分析,也能快速的对固定维度进行分析查询

    物化视图 VS Rollup

    • 明细模型表下,rollup和物化视图的差别:
      物化视图:都可以实现预聚合,新增一套前缀索引
      rollup:对于明细模型,新增一套前缀索引
    • 聚合模型下,功能一致
    drop table sales_records;
    create table sales_records(
    record_id int, 
    seller_id int, 
    store_id int, 
    sale_date date, 
    sale_amt bigint) 
    duplicate key (record_id,seller_id,store_id,sale_date)
    distributed by hash(record_id) buckets 2
    properties("replication_num" = "1");
    
    
    
    
    -- 插入数据
    insert into sales_records values \
    (1,1,1,'2022-02-02',100),\
    (2,2,1,'2022-02-02',200),\
    (3,3,2,'2022-02-02',300),\
    (4,3,2,'2022-02-02',200),\
    (5,2,1,'2022-02-02',100),\
    (6,4,2,'2022-02-02',200),\
    (7,7,3,'2022-02-02',300),\
    (8,2,1,'2022-02-02',400),\
    (9,9,4,'2022-02-02',100);
    
    
    -- 不同门店,看总销售额的一个场景
    select store_id, sum(sale_amt)  
    from sales_records  
    group by store_id; 
    
    CREATE MATERIALIZED VIEW store_id_sale_amonut as 
    select store_id, sum(sale_amt)  
    from sales_records  
    group by store_id;
    
    CREATE MATERIALIZED VIEW store_amt as 
    select store_id, sum(sale_amt)  as sum_amount
    from sales_records  
    group by store_id; 
    
    
    --针对上述场景做一个物化视图
    create materialized view store_amt as  
    select store_id, sum(sale_amt) as sum_amount 
    from sales_records  
    group by store_id; 
    
    
    show alter table materialized view from 库名  order by CreateTime desc limit 1;
    
    show alter table materialized view from test order by CreateTime desc limit 1;
    
    
    +-------+---------------+---------------------+---------------------+---------------+-----------------+----------+---------------+----------+------+----------+---------+
    | JobId | TableName     | CreateTime          | FinishTime          | BaseIndexName | RollupIndexName | RollupId | TransactionId | State    | Msg  | Progress | Timeout |
    +-------+---------------+---------------------+---------------------+---------------+-----------------+----------+---------------+----------+------+----------+---------+
    | 15093 | sales_records | 2022-11-25 10:32:33 | 2022-11-25 10:32:59 | sales_records | store_amt       | 15094    | 3008          | FINISHED |      | NULL     | 86400   |
    +-------+---------------+---------------------+---------------------+---------------+-----------------+----------+---------------+----------+------+----------+---------+
    
    
    查看 Base 表的所有物化视图 
    desc sales_records all;
    +---------------+---------------+-----------+--------+------+-------+---------+-------+---------+
    | IndexName     | IndexKeysType | Field     | Type   | Null | Key   | Default | Extra | Visible |
    +---------------+---------------+-----------+--------+------+-------+---------+-------+---------+
    | sales_records | DUP_KEYS      | record_id | INT    | Yes  | true  | NULL    |       | true    |
    |               |               | seller_id | INT    | Yes  | true  | NULL    |       | true    |
    |               |               | store_id  | INT    | Yes  | true  | NULL    |       | true    |
    |               |               | sale_date | DATE   | Yes  | true  | NULL    |       | true    |
    |               |               | sale_amt  | BIGINT | Yes  | false | NULL    | NONE  | true    |
    |               |               |           |        |      |       |         |       |         |
    | store_amt     | AGG_KEYS      | store_id  | INT    | Yes  | true  | NULL    |       | true    |
    |               |               | sale_amt  | BIGINT | Yes  | false | NULL    | SUM   | true    |
    +---------------+---------------+-----------+--------+------+-------+---------+-------+---------+
    
    EXPLAIN SELECT store_id, sum(sale_amt) FROM sales_records GROUP BY store_id;
    
    Create olap table should contain distribution desc

    be节点只有一个,建表遇到问题,没加 DISTRIBUTED BY HASH()

    PROPERTIES (
        "replication_allocation" = "tag.location.default: 1"
    );
    
    创建分区时不可添加范围重叠的分区,Doris导数据时不会主动创建分区,要导入数据先创建对应的分区
    able[sales_records]'s state is not NORMAL. Do not allow doing materialized view
    doris的物化视图中,不支持count(distint) ==> bitmap_union
    bitmap_union(to_bitmap(user_id)) uv_bitmap
    
    动态分区创建
    Bloom Filter 索引
    索引性能测试
    本地导入的方式上传数据:(1000万数据)
    curl  --location-trusted -u root: -T /opt/output.txt  -H "label:dwd_policy_main_d"   -H "column_separator:,"  http://127.0.0.1:8040/api/demo/dwd_policy_main_d/_stream_load
    

    相关文章

      网友评论

        本文标题:0.1 Doris 模型介绍

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