1. 基本介绍

1.1 背景

Apache Doris是一款现代高性能MPP分析存储引擎,由百度开源贡献给Apche 基金会进行孵化。它是分布式架构,支持动态扩缩容;兼容mysql协议,使用任何Mysql客户端和BI工具都可以对接Doris;很高的查询性能,单机导入100M/s,查询亚秒级返回,单机QPS在10000以上

1.2 主要特性

1.2.1 现代化MPP架构

简单的MPP架构,不依赖外部系统,自身进行存储和计算。Doris中分成2种角色:FE(Frontend)和BE(Backend),FE负责元数据管理和执行计划的生成,BE负责数据存储和执行查询计划。集群通过多个FE和BE实现高可用。

一个简单的查询流程如下:

1.FE接收客户端的查询请求,生成执行计划
2.FE把执行计划分发给需要执行计划的BE
3.BE分别执行对应的执行计划
4.BE将查询结果汇总到FE指定的BE节点
5.指定BE将汇总结果返回给FE
6.FE将结果返回给客户端

1.2.2 高效的列式存储引擎与向量化查询

Doris使用列式存储,会对存储列进行压缩,针对列创建zone map信息,从而在查询时通过zone map过滤而无需读取文件数据,极大的提升查询效率。同时,与其他Mpp查询引擎一样,Doris通过分布式的向量化执行,能大大提升效率。

1.2.3 支持Mysql协议

Doris 支持标准的 SQL 语法,包括聚合,关联,排序,窗口函数,自定义函数等功能,用户可以通过标准的 SQL 对数据进行灵活的分析运算。 此外,Doris 还兼容 MySQL 协议语法,,所以 Doris 可以很轻易地和各种客户端工具、BI 软件做对接。用户可以使用各种 BI 工具对 Doris 中的数据进行拖拽式分析。

1.2.4 高效的聚合技术与Rollup

Doris 支持智能的 Rollup 机制。用户可以通过创建 Rollup,预先计算生成预聚合表用于加速聚合类查询请求。Doris 的 Rollup 能够在数据导入时自动完成汇聚,与原始表数据保持⼀致。并且在查询的时候,用户无需指定 Rollup,Doris 能够自动选择最优的 Rollup 来满足查询请求。

1.2.5 极简运维,弹性伸缩

Doris 的元数据和数据都是多副本存储,并且集群中所有模块都可以多实例同时运行,没有单点。这样就能够保证整个 Doris 服务的高可用。 任何节点的宕机、下线、异常都不会影响 Doris 集群服务的整体稳定性。

另外 Doris 还能够支持灵活的在线表模式变更,用户可以动态地修改表的定义,增加列,减少列,新建rollup,减少 rollup。这些操作都可以通过⼀条 SQL 命令搞定。 同样,在操作期间集群的可用性不会受到影响。所有的表仍然能够进行导入、读取等操作。

1.2.6 索引与近似去重

Doris 在存储引擎中支持 BitMap 索引和 BloomFilter 索引。用户可以根据具体的业务场景使用不同的索引技术来提升查询效率。

Doris 内置 HyperLogLog 类型以及 BitMap 类型。用户可以通过这两个类型完成数据快速的近似去重,或者精确去重。

1.3 常用场景

适用场景:

1、数据仓库建设 
2、OLAP/BI分析 
3、用户⾏为分析 
4、⼴告数据分析 
5、系统监控分析 
6、探针分析 APM(Application Performance Management)

不适用场景:

1、Doris不能作为在线业务数据库(OLTP),进行频繁的增删改操作。 
2、Doris不能处理复杂的ETL⼯作。

2. 编译安装

2.1 相关概念

1.schema 
	表结构,数据库的命名空间,通过标准SQL来创建修改
2.FrontEnd
	Doris的元数据和调度节点
3.BackEnd
	Doris的数据存储与计算节点
4.Broker
	一个无状态的进程,用于读取远端存储系统中文件的能力
5.Rollup
	一种新型预计算加速技术,可以理解为一种物化索引。

2.2 编译

推荐使用docker编译

2.3 配置与启动

2.3.1 启动FE

2.3.2 启动BE

2.3.3 启动HDFS Broker

2.4 基本使用

2.4.1 创建用户

同mysql创建用户

2.4.2 创建数据表

建库、授权同mysql

建表需要考虑表设计(后详细说明)

2.4.3 数据导入

先通过HDFS Broker导入数据到Doris

2.4.4 数据查询

支持多种查询,简单select、带in 子查询的查询、join查询

2.4.5 Schema修改

涉及修改表结构或者增加表的rollup

3.表设计

3.1 数据模型

Doris支持多数数据模型,用户在建表时需要指定对应的数据模型,以应对不同场景的业务需求。

3.1.1 明细模型

3.1.1.1 适用场景

  1. 需要保留最原始的数据进行分析
  2. 分析方式不固定,相对灵活
  3. 数据更新不是很频繁,一般为日志型数据

3.1.1.2 模型原理

doris会为明细模型的表维护【排序列】,数据会按照【排序列】排序存储。排序列可以是一列,也可以是几列,由用户指定。

3.1.1.3 如何使用

为了提升查询效率,需要将查询常用的列放在前面,提升检索效率。

CREATE TABLE IF NOT EXISTS test_model_detail ( 
event_time DATETIME NOT NULL COMMENT "datetime of event", 
event_type INT NOT NULL COMMENT "type of event", 
user_id INT COMMENT "id of user", 
device_code INT COMMENT "device of ", 
channel INT COMMENT "" 
)
DUPLICATE KEY(event_time, event_type, user_id) 
DISTRIBUTED BY HASH(user_id) BUCKETS 8;

3.1.1.4 注意事项

建表时默认的模型就是明细模型,在往Doris中增加完全相同的两条记录时,Doris会认为是两条不同的数据。

3.1.2 聚合模型

3.1.2.1 适用场景

1.业务查询类型都是汇总类型查询,不需要查询最原始的数据

2.老数据不会频繁更新,只会被追加

3.1.2.2 模型原理

数据在追加时,会根据表结构中的维度和汇总字段进行聚合,数据量会指数级下降。

3.1.2.3 如何使用

表中的聚合字段需要添加有SUM, MIN, MAX, REPLACE等聚合函数

CREATE TABLE IF NOT EXISTS test_model_aggregate ( 
date DATE NOT NULL COMMENT "日期", 
country VARCHAR(20) NOT NULL COMMENT "国家", 
pv BIGINT SUM DEFAULT "0" COMMENT "total page views" 
)
AGGREGATE KEY(date, country) 
DISTRIBUTED BY HASH(country) BUCKETS 8;

3.1.2.4 注意事项

对于聚合列上的过滤条件,Doris 并不能够直接对于扫描出来的单行数据进行过滤。Doris 会将所有Key 列(指的是上面的 date, country 两个列,他们没有被定义为聚合列)数据以及相应的聚合列数据全部读取出来。然后将 Key 列相同的不同版本数据进行合并计算出对应的聚合列内容。最后再对计算出的最终聚合列内容进行条件过滤。

3.1.3 更新模型

3.1.3.1 适用场景

1.对于已写入的数据有大量更新需求

2.需要进行实时数据分析

3.1.3.2 模型原理

在发生数据更新时,doris底层实际使用了类似HBase版本的机制,让用户只看到主键对应的最新版本数据。

3.1.3.3 如何使用

使用uniq key,并将主键字段放在表字段前面。这样在进行更新时,会根据主键进行更新。

CREATE TABLE IF NOT EXISTS test.test_model_update ( 
create_time DATE NOT NULL COMMENT "创建时间", 
order_id BIGINT NOT NULL COMMENT "订单ID", 
order_state INT COMMENT "订单状态", 
total_price BIGINT COMMENT "订单总价格" 
)UNIQUE KEY(create_time, order_id) 
DISTRIBUTED BY HASH(order_id) BUCKETS 8;

3.1.3.4 注意事项

1.更新时需要将其他字段进行补全。
2.版本过多会导致查询性能降低,应该根据需求选择适当的导入频率,避免短时间产生过多版本。
3.对于value字段的过滤通常在版本合并之后,因此可以将经常查询且不变化的字段放在key中,从而在版本合并前进行过滤,提升查询性能。

3.2 数据分布

数据分布是指将数据均衡的分布在各个节点上,从而在大查询时突出并发的性能。

3.2.1 数据分布方式

1、Round-Robin : 以轮询的方式把数据逐个放置在相邻节点上。 
2、Range : 按区间进行数据分布,图中区间 [1-3] , [4-6] 分别对应不同 Range。 
3、List : 直接基于离散的各个取值做数据分布,性别、省份等数据就满足这种离散的特性。每个离散值会 映射到一个节点上,不同的多个取值可能也会映射到相同节点上。 
4、Hash : 按哈希函数把数据映射到不同节点上。

为了更灵活地划分数据,现代分布式数据库除了单独采用上述四种数据分布方式之外,也会视情况采用
组合数据分布。常见的组合方式有Hash-Hash、Range-Hash、Hash-List。

3.2.2 Doris数据分布方式

doris支持Hash和Range-Hash,即支持分区(range)和分桶(Hash)。

分区主要是对冷热数据进行隔离,热数据更可能被查询,为提升性能可以考虑将热数据存储在SSD上,冷数据可能查询较少,可放在SATA(普通硬盘)上。进行分区也可以通过分区裁剪,过滤大量无关数据。

分桶主要是通过数据分片,使数据均衡分布,提升分布式性能。在进行分桶时,一方面需要考虑常用查询字段,另一方面也要考虑数据均衡避免数据热点(通过hash分布,有可能出现热点)。

3.2.3 分区列如何选择

用户⼀般选取时间列作为分区字段,具体划分的粒度视数据量而定,单个分区原始数据量建议维持在 100G 以内。

3.2.4 分桶列如何选择

需要根据查询基本特征进行分桶,比如根据site_id进行查询,那么可以考虑将site_id加入分桶字段,对短查询可以直接过滤很多分片数据;但是在进行长查询时,可能扫描很多分片,这时可能出现热点问题,需要将数据均匀分片以提升分布式性能,这时可以利用组合字段分桶,如 site_id+city_code,既能满足site_id查询,也能时数据均匀分布。

同时,分桶数据文件不宜太大或太小,一般建议不超过10G(原始数据,压缩后约4-5G)。比如现有100G数据,可以考虑建10个桶,每个桶10G,如果有20台机器,可以考虑加大分桶数,建20个桶。

3.3 动态分区管理

动态分区表需要进行设置,自动删除某时间之前的分区,并创建某时间之后的分区。

3.4 排序索引

3.4.1 排序列(Sort Key)

Doris 中为加速查询,会把表中数据按照指定列排序,这部分用于排序的列,可以称之为 Sort Key。明细模型中Sort Key 就是指定的用于排序的列,聚合模型中 Sort Key 列就是用于聚合的列,更新模型中Sort Key 就是指定的满足唯一性约束的列。

3.4.2 排序索引概念(Sort Index)

为了定位到数据行的位置,需进行二分查找,以找到指定区间。假设数据行非常多,直接对site_id, city_code 进行二分查找,需要把两列数据都加载到内存中,这会消耗大量内存空间。为优化这个细节,Doris 在 Sort Key 的基础上引入排序索引(Sort Index)的概览,每 1024 行条目创建⼀个索引项(Index Item),二分查找是先定位索引项,找到数据所在区间,而后扫描实际数据

可以看出,Sort Index 实际是是排序列的抽取而构建的索引,数据量比Sort Key少1024倍,因此是直接加载在内存中的。这个类似与Kafka中的稀疏索引,快速实现数据查找。

3.4.3 如何选择排序列

排序列的顺序会影响查询的性能,在设置排序列时,需要充分考虑查询的场景频率,区分度高、查询频率高的字段放前面。例如,一张表中有city_code、site_id、user_name、access_time等几个字段,可以根据具体场景进行排序列的设置:

1、如果用户需要经常按 site_id + city_code 的组合进⾏查询,那么把 site_id 放在 Sort Key 第一列就是更加有效的一种⽅式。 
2、如果用户需要经常⽤ city_code 进⾏查询,偶尔按照 site_id + city_code 组合查询,那么把 city_code 放在 Sort Key 的第⼀列就更为合适。 
3、当然有⼀种极端情况,就是按 site_id + city_code 组合查询、以及 city_code 单独查询的⽐例 不相上下。那么这个时候,可以创建⼀个 city_code 为第⼀列的 Rollup,Rollup 会为 city_code 再建⼀个 Sort Index。
注意:
1、排序列中包含的列必须是从第⼀列开始,并且连续。
2、排序列的顺序是由create table语句中的列顺序决定的。DUPLICATE/UNIQUE/AGGREGATE KEY中顺 序需要和create table语句保持⼀致。
3.Doris 中限制 Sort Index 中索引项最长只有36个字节

3.5 Rollup构建

3.5.1 Rollup概念

Doris中Rollup,是一种新型的预计算加速技术,能显著提高查询性能和查询并发,可以理解为一个基于基表(base表)构建的物化索引。物化是指其数据独立于base表单独存储,索引是指rollup可以调整排序列顺序以增加排序索引命中概率(明细模型中),也可以减少key数量以增加数据的聚合度(聚合模型中)。

3.5.2 Rollup原理

Rollup实际是Doris基于Base表独立构建的一个加速物理表,相当于Base表的子集,但是其排序列顺序不一致或者数据量更少,从而起到加速查询的效果。

3.5.3 Rollup使用

Rollup的创建:Doris在创建表是默认不会创建Rollup,需要手动创建,创建后rollup表的数据会自动同步,在导入数据到base表的时候,也会自动同步到rollup表。

Rollup的场景:Base表数据量特别大,数据延时不能达到需求时,需要重新构建一个新的排序索引或者进行聚合汇总时,可以考虑构建rollup。如无必要,不构建为好,不然增加磁盘存储。

多个rollup的路由规则:

1、选择包含所有查询列的Rollup 
2、按照过滤和排序的Column筛选最符合的Rollup 
3、按照Join的Column筛选最符合的Rollup 
4、⾏数最⼩的Rollup 
5、列数最⼩的Rollup

4.索引

除了Doris本身自带的排序索引和Rollup物化索引之外,Doris还提供了两种比较高效的索引方式,Bitmap索引和Bloomfilter索引。

4.1 Bitmap索引

4.1.1 Bitmap索引原理

Bit是经典计算机中的最小单位,只有0和1两个值。那么多个bit可以组成一个序列,标识一个值。bitmap就是一种使用bit数组(bit set)来标识数据的数据结构。

Bitmap索引就是使用Bitmap来定位数据的一种倒排索引(索引排列由低位到高位)。

比如,如下表:

create table test_user(
id int primary key,
name varchar(100) comment '姓名',
age tinyint comment '年龄',
married tinyint comment '婚姻状态:0-未婚、1-已婚、2-离婚'
)
;
对应数据如下:
1,jack,11,0
2,lily,30,1
3,tom,23,1
4,tony,55,2
5,jerry,35,0

当我们对married字段建立Bitmap Index时,索引底层就会对married的所有值构建一个字典,

字典ID字典值备注
10未婚
21已婚
32离婚

然后对字典中每一个值构建一个Bitmap,最终的形式如下:

字典ID对应Bitmap倒排索引备注
110001第1、5行是该值
200110第2、3行是该值
301000第4行是该值

那么在通过where married=1时,就会先找到married=1对应的字典ID为2,然后再找到字典ID为2的Bitmap,从而筛选出第2、3行数据。

4.1.2 Doris中如何使用Bitmap索引

相关操作:

CREATE INDEX test_user_bitmap_index ON test_user (married) USING BITMAP COMMENT 'xxxx';
SHOW INDEX FROM test.test_user;
DROP INDEX test_user_bitmap_index ON test.test_user;

注意事项:

1、对于明细模型,所有列都可以建 BitMap 索引;对于聚合模型,只有 Key 列可以建 BitMap 索引。
2、不支持对Float、Double、Decimal 类型的列建 BitMap 索引。
3、如果要查看某个查询是否命中了 BitMap 索引,可以通过查询的 Profile 信息查看。

4.2 Bloomfiter索引

4.2.1 Bloomfilter索引原理

Bloomfilter是一个用于判断某个元素在集合中是否存在的数据结构,优点是空间效率和时间效率都非常高,缺点是存在误判概率。

它是通过一个bit数组和多个hash函数组成。bit数组初始值均为0,当插入一个元素时,会通过hash函数计算hash值,并将对应位上的数字置为1 。当要判断一个值在集合中是否存在时,就会对这个值使用多个不同hash函数进行判断,如何hash后对应位上的值均为1,说明这个值很可能在这个集合存在;反之,如果有一个值不为1,则说明该字段值一定不在该数据集合中。由于Bloomfilter的bit数组位数有限及hash函数特点(不同值的hash值可能相同),存在误判的可能。

以下是Bloomfilter算法的一个示意图:
在这里插入图片描述

Doris中的Bloomfilter索引会对每个数据文件建立一个Bloomfilter,当进行查询时,当Bloomfilter判断该值在数据文件中不存在,就不会读取该文件;反之如果Bloomfilter判断改制在数据文件中存在时,会读取文件进行筛选。
4.2.2 Bloomfilter索引如何使用

相关使用:

ALTER TABLE test.test_user SET ("bloom_filter_columns"="married"); --建表后创建Bloomfilter索引,也可以在建表时设置
ALTER TABLE test.test_user SET ("bloom_filter_columns"=""); --删除Bloomfilter索引
show create table test.test_user ;

注意事项:

1、不支持对 Tinyint、Float、Double 类型的列建 BloomFilter 索引。
2、Bloom Filter 索引只对in和=过滤查询有加速效果。
3、如果要查看某个查询是否命中了 Bloom Filter 索引,可以通过查询的 Profile 信息查看。

5.数据导入

Doris目前支持多种数据导入方式,如:Broker Load、Stream Load、Routine Load、Insert into等

5.1 Broker Load

一种针对特定数据源,通过对应Broker进程离线导入的方式,如文件系统(HDFS、BOS、AFS),常用HDFS Broker进行hadoop离线导入

5.2 Stream Load

一种同步导入方式,通过HTTP将本地文件或数据流导入到Doris中。常用于本地文件导入

5.3 Routine Load

一种常规导入方式,即自动从指定数据源同步数据。常用于Kafka等消息系统实时导入

5.4 Insert Into

一种SQL导入方式,通常在命令行中执行,于Mysql的执行方式基本一致,有Insert Into Values和Insert Into Select两种方式,第一种不建议使用。

6.其他功能

Logo

大数据从业者之家,一起探索大数据的无限可能!

更多推荐