Skip to content

Commit

Permalink
Merge pull request #13515 from Yulei-Yang/master
Browse files Browse the repository at this point in the history
完成数据开发->建表模块的更新
  • Loading branch information
vjunwuwu authored Sep 6, 2022
2 parents dd6f463 + 7134e31 commit 72c2f51
Show file tree
Hide file tree
Showing 4 changed files with 853 additions and 645 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# 数据分布和副本

## 数据分片

Doris 表按两层结构进行数据划分,分别是分区和分桶。示意如下:
![](https://qcloudimg.tencent-cloud.cn/raw/d99a7c54f29b2ffe86b033c639f2d4da.jpg)

每个分桶文件就是一个数据分片(Tablet),Tablet是数据划分的最小**逻辑**单元。每个 Tablet 包含若干数据行。各个 Tablet 之间的数据没有交集,并且在物理上是独立存储的。

一个 Tablet 只属于一个 Partition,相应的多个 Tablet 在逻辑上归属于不同的分区(Partition)。而一个 Partition 包含若干个 Tablet。因为 Tablet 在物理上是独立存储的,所以可以视为 Partition 在物理上也是独立。Tablet 是数据移动、复制等操作的最小物理存储单元。


## 副本

为了提高保存数据的可靠性和计算时的性能,Doris对每个表复制多份进行存储。数据的每份复制就叫做一个副本。Doris按Tablet为基本单元对数据进行副本存储,默认一个分片有3个副本。建表时可在PROPERTIES中设置副本的数量:
```sql
PROPERTIES
(
"replication_num" = "3"
);
```


下图示例,有两个表分别导入Doris,表1 导入后按 3 副本存储,表2 导入后按 2 副本存储。
![](https://qcloudimg.tencent-cloud.cn/raw/43e72155d5f3578596c2aa3da6782d86.jpg)

**关于副本**
- 每个 分片 的副本数量默认为3,建议保持默认即可。在建表语句中,所有 Partition 中的 Tablet 副本数量统一指定。而在增加新分区时,可以单独指定新分区中分片的副本数量。
- 最大副本数量取决于集群中部署BE服务的独立 IP 的数量(注意不是 BE 数量)。Doris 中副本分布的原则是:不允许同一个 Tablet 的副本分布在同一台物理机上,而识别物理机即通过 IP。所以,即使在同一台物理机上部署了 3 个或更多 BE 实例,如果这些 BE 的 IP 相同,则依然只能设置副本数为 1。
- 副本数量可以在运行时修改。
- 副本数量强烈建议保持为奇数。
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# 索引、排序列和前缀索引

## 索引

Doris 支持比较丰富的索引结构来减少数据的扫描和提高查询效率,目前支持的索引类型有:

- Sorted Compound Key Index,可以最多指定三个列组成复合排序键,通过该索引,能够有效进行数据裁剪,从而能够更好支持高并发的报表场景

- Z-order Index :可以高效对数据模型中的任意字段组合进行范围查询

- Min/Max :有效过滤数值类型的等值和范围查询

- Bloom Filter :对高基数列的等值过滤裁剪非常有效

- Invert Index :能够对任意字段实现快速检索


不同于传统的数据库设计,Doris 不支持在任意列上创建索引。Doris 这类 MPP 架构的 OLAP 数据库,通常都是通过提高并发来处理大量数据的。

## 排序列(Sort Key)

为了提高查询性能,Doris优化了数据存储的组织结构。本质上,Doris 的数据存储在类似 SSTable(Sorted String Table)的数据结构中。该结构是一种有序的数据结构,可以按照指定的列进行排序存储(可以是一列或者多列),这些列即称为排序列。在这种数据结构上,以排序列作为条件进行查找,会非常的高效。

在 Aggregate、Unique 和 Duplicate 三种数据模型中。底层的数据存储,是按照各自建表语句中,AGGREGATE KEY、UNIQUE KEY 和 DUPLICATE KEY 中指定的列进行排序存储的。Rollup 可以指定自己的排序列,但排序列必须是 Rollup 列顺序的前缀。

**注意**

- 在建表语句中的列定义中,排序列的定义必须出现在其他列的定义之前。
- 排序列的顺序是由create table语句中的列顺序决定的。

## 前缀索引

即在排序的数据结构(SSTable)基础上,实现的一种根据给定前缀列,快速查询数据的索引方式。对于能使用上排序结构的查询,Doris采用二分查找算法定位到目标数据的区间。但如果表中数据行数很多,直接对排序列进行二分查找需要把所有filter列的数据都加载到内存中,这会消耗大量内存空间。为优化这个细节,Doris在Sort Key的基础上引入稀疏的Shortkey Index(前缀索引),Sortkey Index的内容会比数据量少1024倍(Doris把每 1024 行数据组成一个逻辑数据块 (称作Data Block),每个Data Block在前缀索引中存储一行索引),因此会全量缓存在内存中,实际查找的过程中可以有效加速查询。

当Sort Key列数非常多时,会占用大量内存,为了性能考虑,对前缀索引项做了限制:
- 最多可选取 3 列作为Shortkey 列;
- 不能使用 FLOAT / DOUBLE 类型的列;
- 只能按排序键的顺序来构造前缀索引
- VARCHAR / CHAR 类型列只能出现一次,并且只能是最后位置;
- 所有列字节数不超过36字节,VARCHAR / CHAR列按剩余字节数折断;
- 当用户在建表语句中指定short_key属性时,例如"short_key" = "4"指定4个列作为short_key, 可突破上述限制;

我们举例说明:

### 示例

1. 以下表结构的前缀索引为 user_id(8 Bytes) + age(4 Bytes) + message(prefix 20 Bytes)。

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

2. 以下表结构的前缀索引为 user_name(20 Bytes)。即使没有达到 36 个字节,因为遇到 VARCHAR,所以直接截断,不再往后继续。

| ColumnName | Type |
| -------------- | ------------ |
| user_name | VARCHAR(20) |
| age | INT |
| message | VARCHAR(100) |
| max_dwell_time | DATETIME |
| min_dwell_time | DATETIME |

当我们的查询条件是**前缀索引的前缀**时,可以极大的加快查询速度。比如在第一个例子中,我们执行如下查询:

```sql
SELECT * FROM table WHERE user_id=1829239 and age=20
```

该查询的效率会**远高于**如下查询:

```sql
SELECT * FROM table WHERE age=20
```

所以在建表时,**正确的选择列顺序,能够极大地提高查询效率**

### 最佳实践

- **通过ROLLUP来调整前缀索引**

因为建表时已经指定了列顺序,所以一个表只有一种前缀索引。这对于使用其他不能命中前缀索引的列作为条件进行的查询来说,效率上可能无法满足需求。因此,我们可以通过创建 ROLLUP 来人为的调整列顺序。

- **优化排序列的先后顺序来提高查询性能**

当Sort Key涉及多个列的时候,谁先谁后也有讲究,区分度高、经常查询的列建议放在前面。

- **注意排序列的数量**

1. 如果选择了大量的列用于排序列,那么数据导入时排序的开销会增大整个导入过程的耗时;
2. 设计良好的少量排序列也能快速定位到数据行所在的位置,增加更多列进行排序也不会带来查询的提升
Loading

0 comments on commit 72c2f51

Please sign in to comment.