Doris

1.Doris简介

1.1Doris概述

Apache Doris 是一款基于 MPP(大规模并行处理) 架构的高性能、实时的分析型数据库,以高效、简单、统一的特点被人们所熟知,仅需亚秒级响应时间即可返回海量数据下的查询结果,不仅可以支持高并发的点查询场景,也能支持高吞吐的复杂分析场景。基于此,Apache Doris 能够较好的满足报表分析、即席查询、统一数仓构建、湖仓一体等使用场景,用户可以在此之上构建大屏看板、用户行为分析、AB 实验平台、日志检索分析、用户画像分析、订单分析等应用。

Apache Doris 最早是诞生于百度广告报表业务的 Palo 项目,2017 年正式对外开源,2018 年 7 月由百度捐赠给 Apache 基金会进行孵化,之后在 Apache 导师的指导下由孵化器项目管理委员会成员进行孵化和运营。2022 年 6 月,Apache Doris 成功从 Apache 孵化器毕业,正式成为 Apache 顶级项目(Top-Level Project,TLP)。目前 Apache Doris 社区已经聚集了来自不同行业数百家企业的 600 余位贡献者,并且每月活跃贡献者人数也超过 120 位。

Apache Doris 如今在中国乃至全球范围内都拥有着广泛的用户群体,截止目前,Apache Doris 已经在全球超过 4000 家中大型企业的生产环境中得到应用,在中国市值或估值排行前 50 的互联网公司中,有超过 80% 长期使用 Apache Doris,包括百度、美团、小米、京东、字节跳动、阿里巴巴、腾讯、网易、快手、微博等。同时在一些传统行业如金融、消费、电信、工业制造、能源、医疗、政务等领域也有着丰富的应用。在中国几乎所有的云厂商比如阿里云、华为云、天翼云、腾讯云、百度云、火山引擎等都在提供托管的 Apache Doris 的云服务。

Doris官网:Apache Doris: Open source data warehouse for real time data analytics - Apache Doris

MPP数据库特点:

  1. 分布式部署
  2. 列式存储
  3. 支持sql,注重分析
  4. 海量数据

分析型(OLAP)数据库产品。仅需亚秒级响应时间即可获得查询结果,有效地支持实时数据分析

Apache Doris 的分布式架构非常简洁,易于运维,并且可以支持 10PB 以上的超大数据集。

Apache Doris 可以满足多种数据分析需求,例如固定历史报表,实时数据分析,交互式数据分析和探索式数据分析等。

img

1.2OLTP和OLAP

  • 联机事务处理OLTP(On-Line Transaction Processing) 公司针对自己公司的业务构建出来的系统化

    • 公司业务系统使用数据库的场景,针对业务系统数据库有大量随机的增删改查
    • 高并发
    • 速度要快
    • 支持事务
  • img

  • 联机分析处理OLAP(On-Line Analytical Processing)

    • 公司的数据分析使用数据库的场景,对已经生成好的数据进行统计分析
    • 一次操作都是针对的整个数据集
    • 只有查这个动作,不会去增删改
    • 查询的响应速度相对慢点也能接受
    • 并发量要求不是太高
  • img

  • 常见的开源OLAP引擎

  • image.png

1.3 使用场景

如下图所示,数据源经过各种数据集成和加工处理后,通常会入库到实时数据仓库 Apache Doris 和离线湖仓(Hive, Iceberg, Hudi 中),广泛应用于 OLAP 分析场景。

Apache Doris 的使用场景

  • Apache Doris 被广泛应用在以下场景中:

    • 报表服务与即席查询:Doris 提供多维数据分析能力,为企业内部的报表、面相分析是的即系报表查询,面相用户的高并发报表提供稳定高性能服务支撑;
    • 实时数仓分析Doris 可以应用于实时数据处理与分析场景,提供秒级同步 TP 数据库的数据变更及亚秒级数据查询能力,服务于实时大屏、实时风控、实时订单分析、实时广告主报表等场景;
    • 湖仓一体统一分析:Doris 外表联邦分析位于 Hive、Iceberg、Hudi 等离线湖仓中的数据,在避免数据拷贝的前提下,查询性能大幅提升;
    • 日志检索与分析Doris 支持倒排索引和全文检索,能够很好的满足日志检索分析的场景,并且依赖其高效的查询引擎和存储引擎,相比传统的日志检索分析的方案可以有 10 倍性价比的优势。
    • 用户画像与行为分析:利用 Doris 内置的行为分析函数与 bitmap 类型可以支撑用户行为分析与画像圈人场景,可以提供高效的查询与实时分析能力,帮助企业快速获取用户洞察,优化用户体验和企业决策;

1.4 优势

  • **简单易用**:部署只需两个进程,不依赖其他系统;在线集群扩缩容,自动副本修复;兼容MySQL协议,并使用标准SQL。

  • **高性能**:依托列式存储引擎、现代化的MPP架构、向量化查询引擎、预聚合物化视图、数据引擎的实现,在低延迟和高吞吐查询上都达到了急速性能。

  • 统一数仓:单一系统,可以同时支持实时数据服务,交互数据分析和离线数据处理场景。

  • **联邦查询**:支持对Hive、lceberg、Hudi等数据湖和MySQL、Elasticsearch等数据库的联邦查询分析。

  • **多种导入**:支持从HDFS/S3等批量拉取导入和MySQL Binlog、Kafka等流式拉取导入;支持通过HTTP接口进行微批量推送写入和JDBC中使用Insert实时推送写入。

  • **生态丰富**:Spark利用 Spark Doris Connector 读取和写入 Doris; Flink Doris Connector配合 Flink CDC 实现数据 Exactly Once 写入 Doris;利用 DBT Doris Adapter,可以很容易的在 Doris 中完成数据转化。

1.5 架构

Apache Doris 采用 MySQL 协议,支持标准 SQL,用户可以通过各类客户端工具来访问 Apache Doris,并支持与 BI 工具的无缝对接。在部署 Apache 时,可以根据硬件环境与业务需求选择存算一体架构存算分离架构

1.5.1存算一体架构

Apache Doris 存算一体架构精简易于维护,如下图所示,只有两类进程:

  • Frontend(FE):主要负责用户请求的接入、查询解析规划、元数据的管理、节点管理相关工作
  • Backend(BE):主要负责数据存储、查询计划的执行。数据会被切分成分片,在 BE 中多副本存储。

在生产环境中可以部署多个 FE 节点做容灾备份,每个 FE 中都会维护全量的元数据副本。FE 分为三种角色:

角色 功能
Master FE Master 节点负责元数据的读写,在 Master 元数据发生变更后,会通过 BDB JE 协议同步给 Follower 或 Observer 节点。
Follower Follower 节点负责读取元数据,在 Master 节点发生故障时,Follower 节点可以被选取作为新的 Master 节点。
Observer Observer 节点负责读取元数据,主要为了增加集群的查询并发行。不参加集群选主。

FE 与 BE 进程都是可以横向扩展的,单集群可以支持到数百台机器,数十 PB 的存储容量。FE 与 BE 进程通过一致性协议来保证服务的高可用和数据的高可靠。存算一体架构高度集成,大幅降低了分布式系统的运维成本。

另外:Doris 是支持mysql Client ,也就是说mysql的客户端可以连接Doris 使用。

1.5.2存算分离架构

自 3.0 版本后,可以选择存算分离部署架构。Apache Doris 存算分离版使用统一的共享存储层作为数据存储空间。存储和计算分离,用户可以独立扩展存储容量和计算资源,从而实现最佳性能和成本效益。如下图所示,存算分离架构分为三层:

  • 元数据层:元数据层主要负责请求规划,查询解析规划与元数据的存储与管理;
  • 计算层:计算层由多个计算组组成,每个计算组可以作为一个独立的租户承担业务计算。在每一个计算组中,有多个无状态的 BE 节点,计算组中可以随时弹性扩缩容 BE 节点;
  • 存储层:存储层可以使用 S3、HDFS、OSS、COS、OBS、Minio、Ceph 等共享存储存放 Doris 的数据文件,包含包括 Segment 文件、反向索引的索引文件等。

存算分离整体架构和技术特点

1.6Doris和Elasticsearch的区别

Elasticsearch(简称ES)和Doris是两种不同类型的数据库,它们在架构、功能和使用场景等方面存在显著的差异。以下是对这两者的详细比较:

1.6.1数据库类型与存储方式

  • Elasticsearch
    • 是一个面向文档的数据库,更适合存储非结构化数据,如日志、事件、文本等。
    • 数据以JSON格式存储,非常适合全文搜索、日志分析、安全智能等场景。
  • Doris
    • 是一个列式存储的关系型数据库,更适合存储结构化数据,如数据仓库中的表格数据。
    • 采用列式存储方式,按列进行数据的编码压缩和读取,能够实现极高的压缩比,同时减少大量非相关数据的扫描,从而更加有效利用IO和CPU资源。

1.6.2查询与分析能力

  • Elasticsearch
    • 提供了全文搜索、聚合分析、实时分析等功能。
    • 非常适合进行复杂的文本搜索和数据分析。
  • Doris
    • 支持SQL查询,可以进行复杂的数据分析和报表生成。
    • 支持多维度的快速聚合分析,适合进行OLAP(联机分析处理)操作。

1.6.3扩展性与可用性

  • Elasticsearch
    • 是一个分布式系统,可以很容易地通过增加节点来扩展存储容量和计算能力。
    • 提供了高可用性和容错机制,保证了数据的可靠性和稳定性。
  • Doris
    • 同样是一个分布式系统,具有良好的扩展性和可用性。
    • 支持一键加减节点,自动均衡数据,保证了系统的高性能和稳定性。

1.6.4使用场景

  • Elasticsearch
    • 更适合用于日志分析、全文搜索、安全智能等场景。
    • 尤其适合处理非结构化数据。
  • Doris
    • 更适合用于数据仓库、大数据分析、报表生成等场景。
    • 尤其适合处理结构化数据和高维度的数据分析。

2.安装doris

2.1doris端口说明

实例名称 端口名称 默认端口 通信方向 说明
BE be_port 9060 FE -> BE BE 上 Thrift Server 的端口,用于接收来自 FE 的请求
BE webserver_port 8040 BE <-> BE BE 上的 HTTP Server 端口
BE heartbeat_service_port 9050 FE -> BE BE 上的心跳服务端口(Thrift),用于接收来自 FE 的心跳
BE brpc_port 8060 FE <-> BE,BE <-> BE BE 上的 BRPC 端口,用于 BE 之间的通信
FE http_port 8030 FE <-> FE,Client <-> FE FE 上的 HTTP Server 端口
FE rpc_port 9020 BE -> FE,FE <-> FE FE 上的 Thrift Server 端口,每个 FE 的配置需保持一致
FE query_port 9030 Client <-> FE FE 上的 MySQL Server 端口
FE edit_log_port 9010 FE <-> FE FE 上的 bdbje 通信端口
Broker broker_ipc_port 8000 FE -> Broker,BE -> Broker Broker 上的 Thrift Server 端口,用于接收请求

2.2安装包安装doris集群

集群情况

ip 说明
1FE 192.168.116.134 doris01
3BE 192.168.116.134 doris01
192.168.116.132 doris02
192.168.116.133 doris03

2.2.1集群ssh免密连接

  1. 首先关闭三台机器的防火墙
systemctl disable firewalld.service
  1. 先在主机doris01上生成ssh私钥和公钥,会在~/.ssh目录下生成公钥id_rsa.pub和私钥文件id_rsa
ssh-keygen -t rsa
  1. 实现本地免密登录,将id_rsa.pub中的内容拷贝到authorized_keys,~/.ssh目录下会生成一个新的文件:authorized_keys
ssh-copy-id localhost
  1. 完成上述步骤后就可以本地SSH免密登录了
ssh localhost

[root@localhost .ssh]# ssh localhost
Last login: Fri Jan 24 02:39:38 2025 from localhost

  1. 在doris01上的/etc/hosts文件中添加上述信息
192.168.116.132 doris02
192.168.116.133 doris03
  1. 再执行以下代码将本机的~/.ssh文件夹复制到其他主机上,提示输入密码时,输入远程主机密码回车即可
scp -r ~/.ssh doris02:~/
scp -r ~/.ssh doris03:~/	
  1. 设置三台的hostname
hostnamectl set-hostname doris01
hostnamectl set-hostname doris02
hostnamectl set-hostname doris03
  1. 测试免密连接

image-20250124190143973

2.2.1配置JDK8环境

如果原先存在jdk环境先卸载

rpm -qa | grep -i java | xargs -n1 rpm -e --nodeps

三台都需要

将主节点上的jdk发送到从节点上

scp -r /usr/local/jdk1.8.0_202 root@doris02:/usr/local
scp -r /usr/local/jdk1.8.0_202 root@doris03:/usr/local

并切换到从节点怕配置jdk

2.2.2官网下载安装包

image-20250124205450945

doris01下载完成后解压,再将其发送到doris02和doris03

tar -zxvf apache-doris-2.1.8-bin-x64.tar.gz
mv apache-doris-2.1.8-bin-x64 doris
scp -r doris/ root@doris02:/
scp -r doris/ root@doris03:/

2.2.3修改环境变量

  1. 修改系统最大打开文件句柄数

通过以下命令可以调整最大文件句柄数。在调整后,需要重启会话以生效配置:

vi /etc/security/limits.conf 
* soft nofile 1000000
* hard nofile 1000000
  1. 修改虚拟内存区域

通过以下命令可以永久修改虚拟内存区域至少为 2000000,并立即生效:

cat >> /etc/sysctl.conf << EOF
vm.max_map_count = 2000000
EOF

sysctl -p
  1. 关闭交换分区

在部署 Doris 时,建议关闭 swap 分区。swap 分区是内核发现内存紧张时,会按照自己的策略将部分内存数据移动到配置的 swap 分区,由于内核策略不能充分了解应用的行为,会对 Doris 性能造成较大影响。所以建议关闭。

永久关闭,使用 Linux root 账户,注释掉 /etc/fstab 中的 swap 分区,重启即可彻底关闭 swap 分区。

# /etc/fstab
# <file system>        <dir>         <type>    <options>             <dump> <pass>
tmpfs                  /tmp          tmpfs     nodev,nosuid          0      0
/dev/sda1              /             ext4      defaults,noatime      0      1
# /dev/sda2              none          swap      defaults              0      0
/dev/sda3              /home         ext4      defaults,noatime      0      2

2.2.4下载MySQL Client

Apache Doris 采用 MySQL 网络连接协议,兼容 MySQL 生态的命令行工具、JDBC/ODBC 和各种可视化工具。同时 Apache Doris 也内置了一个简单的 Web UI,方便使用。

从 MySQL 官方网站下载 MySQL Client,或者下载Doris提供的 Linux 上免安装的 MySQL 客户端。当前 Doris 主要兼容 MySQL 5.7 及其以上的客户端。

wget https://cdn.selectdb.com/download/mysql-client/mysql-5.7.22-linux-glibc2.12-x86_64.tar.gz

安装完成后再解压

tar -xzvf mysql-5.7.22-linux-glibc2.12-x86_64.tar.gz -C /
mv mysql-5.7.22-linux-glibc2.12-x86_64/ mysql

2.2.5安装 FE

在doris01配置 FE

选择独立于 BE 数据存储的硬盘,创建 FE 的元数据目录

mkdir -p /data/doris/fe/meta  

创建 FE 元数据目录的符号链接

ln -s /doris/fe/doris_meta /data/doris/fe/meta  

修改 FE 配置文件 doris/fe/conf/fe.conf 的以下内容:

# 指定 Java 环境
JAVA_HOME=/usr/local/jdk1.8.0_202

# 修改 FE 元数据目录
meta_dir = /data/doris/fe/meta

# 指定 FE 监听 IP 的 CIDR 网段
priority_networks=192.168.116.131/24

启动 FE

通过 start_fe.sh 脚本运行 FE 进程:

/doris/fe/bin/start_fe.sh --daemon

检查 FE 启动状态

jps

image-20250125111621205

通过 MySQL Client 连接 Doris 集群,初始化用户为root,密码为空。

/mysql/bin/mysql -uroot -P9030 -h127.0.0.1 --skip-ssl
  1. -P :这里是我们连接 Doris 的查询端口,默认端口是 9030,对应的是fe.conf里的 query_port

  2. -h : 这里是我们连接的 FE IP地址,如果你的客户端和 FE 安装在同一个节点可以使用127.0.0.1。

设置root用户密码

SET PASSWORD = '密码';

密码不能够使用明文形式,否则会报错:Password hash should be a 41-digit hexadecimal number(密码哈希应该是41位十六进制数字)

使用sql命令查看密码的41位16进制形式并重新设置密码。

SELECT PASSWORD('密码')

image-20241224222724441

检查 FE 状态,确定 Join 与 Alive 列都为 true

SHOW PROC '/frontends'\G;

image-20250125105907428

2.2.5安装 BE

配置 BE

创建数据目录

mkdir -p /data/doris/be/storage

修改 BE 配置文件 doris/be/conf/be.conf 的以下内容:

## 指定 Java 环境
JAVA_HOME=/usr/local/jdk1.8.0_202

# 指定 BE 监听 IP 的 CIDR 网段
priority_networks=192.168.116.134/24

# 配置BE存储路径
storage_root_path =/data/doris/be/storage

# 分发be配置文件
scp /doris/be/conf/be.conf root@doris02:/doris/be/conf/
scp /doris/be/conf/be.conf root@doris03:/doris/be/conf/

# 修改各自BE 配置文件中的priority_networks
mkdir -p /data/doris/be/storage
priority_networks = 192.168.116.132/24 

mkdir -p /data/doris/be/storage
priority_networks = 192.168.116.133/24

登录MySQL,默认无密码

/mysql/bin/mysql -uroot -P9030 -h127.0.0.1 --skip-ssl

MySQL添加BE节点

ALTER SYSTEM ADD BACKEND "192.168.116.134:9050";
ALTER SYSTEM ADD BACKEND "192.168.116.132:9050";
ALTER SYSTEM ADD BACKEND "192.168.116.133:9050";

9050端口是BE 上心跳服务端口,用于接收来自 FE 的心跳

image-20250124005617764

登录到doris01、doris02和doris03,分别启动be

/doris/be/bin/start_be.sh --daemon

登录MySQL客户端,检查 BE 状态,确定 Alive 列为 true

/mysql/bin/mysql -uroot -P9030 -h127.0.0.1 --skip-ssl
SHOW PROC '/backends'\G;

可以看到集群三个BE节点Alive: true

image-20250125113506515

2.2.6登录Doris Web查看集群情况

FE所在IP(Doris01)+ 8030端口

查看集群情况

SHOW backends;

image-20250125114320970

2.3部署 FS_Broker(可选)

Broker 以插件的形式,独立于 Doris 部署。如果需要从第三方存储系统导入数据,需要部署相应的 Broker,默认提供了读取 HDFS、百度云 BOS 及 Amazon S3 的 fs_broker。fs_broker 是无状态的,建议每一个 FE 和 BE 节点都部署一个 Broker。

doris包下有Broker扩展插件

image-20250126003600526

将Broker放到fe下,并发送到子节点

mv /doris/extensions/apache_hdfs_broker/ /doris/fe/

scp -r /doris/fe/apache_hdfs_broker/ root@doris02:/doris/fe/
scp -r /doris/fe/apache_hdfs_broker/ root@doris03:/doris/fe/

分别启动Broker

/doris/fe/apache_hdfs_broker/bin/start_broker.sh --daemon

jps查看

image-20250126004054814

在主节点使用 mysql-client 连接启动的 FE,执行以下命令:

/mysql/bin/mysql -uroot -P9030 -h127.0.0.1

ALTER SYSTEM ADD BROKER broker_name "doris01:8000","doris02:8000","doris03:8000";

查看 Broker 状态

SHOW PROC "/brokers";

image-20250126004308400

3.数据表设计

3.1字段类型

Apache Doris 已支持的数据类型列表如下:

3.1.1数值类型

类型名 存储空间(字节) 描述
BOOLEAN 1 布尔值,0 代表 false,1 代表 true。
TINYINT 1 有符号整数,范围 [-128, 127]。
SMALLINT 2 有符号整数,范围 [-32768, 32767]。
INT 4 有符号整数,范围 [-2147483648, 2147483647]
BIGINT 8 有符号整数,范围 [-9223372036854775808, 9223372036854775807]。
LARGEINT 16 有符号整数,范围 [-2^127 + 1 ~ 2^127 - 1]。
FLOAT 4 浮点数,范围 [-3.410^38 ~ 3.410^38]。
DOUBLE 8 浮点数,范围 [-1.7910^308 ~ 1.7910^308]。
DECIMAL 4/8/16 高精度定点数,格式:DECIMAL(M[,D])。其中,M 代表一共有多少个有效数字(precision),D 代表小数位有多少数字(scale)。有效数字 M 的范围是 [1, 38],小数位数字数量 D 的范围是 [0, precision]。0 < precision <= 9 的场合,占用 4 字节。9 < precision <= 18 的场合,占用 8 字节。16 < precision <= 38 的场合,占用 16 字节。

3.1.2日期类型

类型名 存储空间(字节) 描述
DATE 16 日期类型,目前的取值范围是 [‘0000-01-01’, ‘9999-12-31’],默认的打印形式是 ‘yyyy-MM-dd’。
DATETIME 16 日期时间类型,格式:DATETIME([P])。可选参数 P 表示时间精度,取值范围是 [0, 6],即最多支持 6 位小数(微秒)。不设置时为 0。 取值范围是 [‘0000-01-01 00:00:00[.000000]’, ‘9999-12-31 23:59:59[.999999]’]。打印的形式是 ‘yyyy-MM-dd HH:mm:ss.SSSSSS’。

3.1.3字符串类型

类型名 存储空间(字节) 描述
CHAR M 定长字符串,M 代表的是定长字符串的字节长度。M 的范围是 1-255。
VARCHAR 不定长 变长字符串,M 代表的是变长字符串的字节长度。M 的范围是 1-65533。变长字符串是以 UTF-8 编码存储的,因此通常英文字符占 1 个字节,中文字符占 3 个字节。
STRING 不定长 变长字符串,默认支持 1048576 字节(1MB),可调大到 2147483643 字节(2GB)。可通过 BE 配置 string_type_length_soft_limit_bytes 调整。String 类型只能用在 Value 列,不能用在 Key 列和分区分桶列。

3.1.4半结构类型

类型名 存储空间(字节) 描述
ARRAY 不定长 由 T 类型元素组成的数组,不能作为 Key 列使用。目前支持在 Duplicate 和 Unique 模型的表中使用。
MAP 不定长 由 K, V 类型元素组成的 map,不能作为 Key 列使用。目前支持在 Duplicate 和 Unique 模型的表中使用。
STRUCT 不定长 由多个 Field 组成的结构体,也可被理解为多个列的集合。不能作为 Key 使用,目前 STRUCT 仅支持在 Duplicate 模型的表中使用。一个 Struct 中的 Field 的名字和数量固定,总是为 Nullable。
JSON 不定长 二进制 JSON 类型,采用二进制 JSON 格式存储,通过 JSON 函数访问 JSON 内部字段。长度限制和配置方式与 String 相同
VARIANT 不定长 动态可变数据类型,专为半结构化数据如 JSON 设计,可以存入任意 JSON,自动将 JSON 中的字段拆分成子列存储,提升存储效率和查询分析性能。长度限制和配置方式与 String 相同。Variant 类型只能用在 Value 列,不能用在 Key 列和分区分桶列。

3.1.5聚合类型

类型名 存储空间(字节) 描述
HLL 不定长 HLL 是模糊去重,在数据量大的情况性能优于 Count Distinct。HLL 的误差通常在 1% 左右,有时会达到 2%。HLL 不能作为 Key 列使用,建表时配合聚合类型为 HLL_UNION。用户不需要指定长度和默认值。长度根据数据的聚合程度系统内控制。HLL 列只能通过配套的 hll_union_agg、hll_raw_agg、hll_cardinality、hll_hash 进行查询或使用。
BITMAP 不定长 Bitmap 类型的列可以在 Aggregate 表、Unique 表或 Duplicate 表中使用。在 Unique 表或 Duplicate 表中使用时,其必须作为非 Key 列使用。在 Aggregate 表中使用时,其必须作为非 Key 列使用,且建表时配合的聚合类型为 BITMAP_UNION。用户不需要指定长度和默认值。长度根据数据的聚合程度系统内控制。BITMAP 列只能通过配套的 bitmap_union_count、bitmap_union、bitmap_hash、bitmap_hash64 等函数进行查询或使用。
QUANTILE_STATE 不定长 QUANTILE_STATE 是一种计算分位数近似值的类型,在导入时会对相同的 Key,不同 Value 进行预聚合,当 value 数量不超过 2048 时采用明细记录所有数据,当 Value 数量大于 2048 时采用 TDigest 算法,对数据进行聚合(聚类)保存聚类后的质心点。QUANTILE_STATE 不能作为 Key 列使用,建表时配合聚合类型为 QUANTILE_UNION。用户不需要指定长度和默认值。长度根据数据的聚合程度系统内控制。QUANTILE_STATE 列只能通过配套的 QUANTILE_PERCENT、QUANTILE_UNION、TO_QUANTILE_STATE 等函数进行查询或使用。
AGG_STATE 不定长 聚合函数,只能配合 state/merge/union 函数组合器使用。AGG_STATE 不能作为 Key 列使用,建表时需要同时声明聚合函数的签名。用户不需要指定长度和默认值。实际存储的数据大小与函数实现有关。

3.1.6IP 类型

类型名 存储空间(字节) 描述
IPv4 4 字节 以 4 字节二进制存储 IPv4 地址,配合 ipv4_* 系列函数使用。
IPv6 16 字节 以 16 字节二进制存储 IPv6 地址,配合 ipv6_* 系列函数使用。

也可通过SHOW DATA TYPES;语句查看 Apache Doris 支持的所有数据类型。

3.2 表的基本概念

3.2.1 Row & Column

一张表包括行(Row)和列(Column);

Row 即用户的一行数据。Column 用于描述一行数据中不同的字段。

doris中的列分为两类:key列和value列

key列在doris中有两种作用:

​ 聚合表模型中,key是聚合和排序的依据

​ 其他表模型中,key是排序依据

3.2.2 分区(Partition)与分桶(tablet)

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

3.2.2.1Partition

  • Partition 列可以指定一列或多列,在聚合模型中,分区列必须为 KEY 列。
  • 不论分区列是什么类型,在写分区值时,都需要加双引号。
  • 分区数量理论上没有上限。
  • 当不使用 Partition 建表时,系统会自动生成一个和表名同名的,全值范围的 Partition。该 Partition 对用户不可见,并且不可删改。
  • 创建分区时不可添加范围重叠的分区
3.2.2.1.1Range分区

Range分区列通常为时间列,以方便的管理新旧数据。Range 分区支持的列类型 DATE, DATETIME, TINYINT, SMALLINT, INT, BIGINT, LARGEINT。

  • range分区创建语法

分区信息,支持两种写法:

  1. FIXED RANGE:定义分区的左闭右开区间
PARTITION BY RANGE(col1[, col2, ...])                                             
(                                            
    PARTITION partition_name1 VALUES [("k1-lower1", "k2-lower1", "k3-lower1",...), ("k1-upper1", "k2-upper1", "k3-upper1", ...)),                                                                                                      
    PARTITION partition_name2 VALUES [("k1-lower1-2", "k2-lower1-2", ...), ("k1-upper1-2", MAXVALUE, ))                                            
)                                                                         

示例如下:

PARTITION BY RANGE(`date`)
(
    PARTITION `p201701` VALUES [("2017-01-01"),  ("2017-02-01")),
    PARTITION `p201702` VALUES [("2017-02-01"), ("2017-03-01")),
    PARTITION `p201703` VALUES [("2017-03-01"), ("2017-04-01"))
)
  1. LESS THAN:仅定义分区上界。下界由上一个分区的上界决定。
PARTITION BY RANGE(col1[, col2, ...])                                                
(                                       
    PARTITION partition_name1 VALUES LESS THAN MAXVALUE | ("value1", "value2", ...),
    PARTITION partition_name2 VALUES LESS THAN MAXVALUE | ("value1", "value2", ...)         
)                                    

示例如下:

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"),
    PARTITION `p2018` VALUES [("2018-01-01"), ("2019-01-01")),
    PARTITION `other` VALUES LESS THAN (MAXVALUE)
)
create database test;

CREATE TABLE IF NOT EXISTS test.example_range_tbl
(
    `user_id`   BIGINT   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`) -- 指定分区类型和分区列,其中`p20250101`的分区名
(
    PARTITION `p20250101` VALUES LESS THAN ("2025-01-01"),
    PARTITION `p20250201` VALUES LESS THAN ("2025-02-01"),
    PARTITION `p20250301` VALUES LESS THAN ("2025-03-01"),
    PARTITION `p20250401` VALUES LESS THAN ("2025-04-01")
)
DISTRIBUTED BY HASH(`user_id`) BUCKETS 2
PROPERTIES
(
    "replication_num" = "1"
);

副本是1 ,因为我们搭建的doris 只有一台
image.png image.png
  • 查看表中分区的情况
SHOW PARTITIONS FROM example_range_tbl;

image-20250125211230431

  • 比如在分区内插入数据,显示成功:
INSERT INTO example_range_tbl VALUES (1, "2025-03-25", "2025-03-25 00:00:00", "北京", 20, 1);

image-20250125212050217

  • 在分区外插入数据,插入失败:

image-20250125212353694

  • 增加分区
ALTER TABLE example_range_tbl ADD PARTITION `p20250501` VALUES LESS THAN ("2025-05-01");
  • 删除分区
ALTER TABLE example_range_tbl DROP PARTITION `p20250501`;
3.2.2.1.2List 分区

分区列支持 BOOLEAN, TINYINT, SMALLINT, INT, BIGINT, LARGEINT, DATE, DATETIME, CHAR, VARCHAR 数据类型,分区值为枚举值。只有当数据为目标分区枚举值其中之一时,才可以命中分区。

Partition 支持通过 VALUES IN (...) 来指定每个分区包含的枚举值。

举例如下:

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")
)

List 分区也支持多列分区,示例如下:

PARTITION BY LIST(id, city)
(
    PARTITION p1_city VALUES IN (("1", "Beijing"), ("1", "Shanghai")),
    PARTITION p2_city VALUES IN (("2", "Beijing"), ("2", "Shanghai")),
    PARTITION p3_city VALUES IN (("3", "Beijing"), ("3", "Shanghai"))
)

List分区创建语法

-- List Partition
CREATE TABLE IF NOT EXISTS test.example_list_tbl
(
    `user_id` LARGEINT NOT NULL COMMENT "用户id",
    `date` DATE NOT NULL COMMENT "数据灌入日期时间",
    `timestamp` DATETIME NOT NULL COMMENT "数据灌入的时间戳",
    `city` VARCHAR(20) NOT NULL COMMENT "用户所在城市",
    `age` SMALLINT NOT NULL COMMENT "用户年龄",
    `sex` TINYINT NOT NULL 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 1
PROPERTIES
(
    "replication_num" = "1"
);

如上 example_list_tbl 示例,当建表完成后,会自动生成如下3个分区:

p_cn: (“Beijing”, “Shanghai”, “Hong Kong”)
p_usa: (“New York”, “San Francisco”)
p_jp: (“Tokyo”)

当我们增加一个分区 p_uk VALUES IN (“London”),分区结果如下:

ALTER TABLE example_list_tbl ADD PARTITION `p_uk` VALUES IN ("London");

p_cn: (“Beijing”, “Shanghai”, “Hong Kong”)
p_usa: (“New York”, “San Francisco”)
p_jp: (“Tokyo”)
p_uk: (“London”)

当我们删除分区 p_jp,分区结果如下:

ALTER TABLE example_list_tbl DROP PARTITION `p_jp`;

p_cn: (“Beijing”, “Shanghai”, “Hong Kong”)
p_usa: (“New York”, “San Francisco”)
p_uk: (“London”)

3.2.2.2 Bucket

  • 如果使用了 Partition,则 DISTRIBUTED … 语句描述的是数据在各个分区内的划分规则。如果不使用 Partition,则描述的是对整个表的数据的划分规则。

  • 分桶列可以是多列,但必须为Key 列。分桶列可以和 Partition 列相同或不同。

  • 分桶列的选择,是在**查询吞吐** 和 查询并发 之间的一种权衡:

    • 如果选择多个分桶列,则数据分布更均匀。如果一个查询条件不包含所有分桶列的等值条件,那么该查询会触发所有分桶同时扫描,这样查询的吞吐会增加,单个查询的延迟随之降低这个方式适合大吞吐低并发的查询场景。
    • 如果仅选择一个或少数分桶列,则对应的点查询可以仅触发一个分桶扫描。此时,当多个点查询并发时,这些查询有较大的概率分别触发不同的分桶扫描,各个查询之间的IO影响较小(尤其当不同桶分布在不同磁盘上时),所以这种方式适合高并发的点查询场景。
  • 分桶的数量理论上没有上限

选择多个分桶列:

img

选择一个或少数分桶列:

image.png

3.3数据表模型

Doris 的数据模型主要分为3类:

  • 明细模型(Duplicate Key Model):允许指定的 Key 列重复,Doirs 存储层保留所有写入的数据,适用于必须保留所有原始数据记录的情况;
  • 主键模型(Unique Key Model):每一行的 Key 值唯一,可确保给定的 Key 列不会存在重复行,Doris 存储层对每个 key 只保留最新写入的数据,适用于数据更新的情况;
  • 聚合模型(Aggregate Key Model):可根据 Key 列聚合数据,Doris 存储层保留聚合后的数据,从而可以减少存储空间和提升查询性能;通常用于需要汇总或聚合信息(如总数或平均值)的情况。

在建表后,表模型的属性已经确认,无法修改。针对业务选择合适的模型至关重要:

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

表模型能力对比:

明细模型 主键模型 聚合模型
Key 列唯一约束 不支持,Key 列可以重复 支持 支持
同步物化视图 支持 支持 支持
异步物化视图 支持 支持 支持
UPDATE 语句 不支持 支持 不支持
DELETE 语句 部分支持 支持 不支持
导入时整行更新 不支持 支持 不支持
导入时部分列更新 不支持 支持 部分支持

3.3.1Aggregate模型(聚合模型)

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

  • SUM:求和,多行的 Value 进行累加。
  • REPLACE:替代,下一批数据中的 Value 会替换之前导入过的行中的 Value。
  • REPLACE_IF_NOT_NULL :当遇到 null 值则不更新。
  • MAX:保留最大值。
  • MIN:保留最小值。

有如下场景:需要创建一个表,来记录公司每个用户的每一次消费行为信息,有如下字段

img

而且,公司对这份数据,特别关心一个报表

每一个用户最后一次访问我们页面的时间,用户消费的总金额,用户停留在我们页面上的最大最小时长

img

每次要看这个报表,都需要在“明细表”上运行一个统计sql

Select
    user_id,data,city,age,gender,
    max(visit_data) as last_visit_data,
    sum(cost) as cost,
    max(dwell_time) as max_dwell_time,
    min(dwell_time) as min_dwell_time
From  t
Group by (user_id,data,city,age,gender) -- 对应的是聚合模型型key

聚合模型

img

sql示例:

-- 这是一个用户消费和行为记录的数据表
CREATE TABLE IF NOT EXISTS test.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_num" = "1"
);

向表中插入7条数据

insert into test.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);

查询结果只有6条:

user_id date city age sex last_visit_date cost max_dwell_time min_dwell_time
10000 2017-10-01 北京 20 0 2017-10-01 07:00:00 35 10 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

3.3.2UNIQUE 模型(主键模型)

相同key的数据进行自动去重的表模型。在某些多维分析场景下,用户更关注的是如何保证 Key 的唯一性,即如何获得 Primary Key 唯一性约束。因此,引入了 Unique 的数据模型。该模型本质上是聚合模型的一个特例,也是一种简化的表结构表示方式。

建表示例:

CREATE TABLE IF NOT EXISTS test.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_num" = "1"
);

插入数据:

insert into test.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');

查询结果进行去重,去重是后插入的数据替换先前的数据,这种情况可以使用聚合模型的REPLACE完成。

user_id username city age sex phone address register_time
10000 lss 北京 20 0 12345678910 北京海淀区 2017-11-15 06:10:20
10000 zss 北京 19 0 12345678910 北京顺义区 2018-10-01 07:00:00

3.3.3Duplicate模型(明细模型)

就是存明细数据的表模型,既不做聚合也不做去重。在某些多维分析场景下,数据既没有主键,也没有聚合需求。Duplicate 数据模型可以满足这类需求。数据完全按照导入文件中的数据进行存储,不会有任何聚合。即使两行数据完全相同,也都会保留。 而在建表语句中指定的 DUPLICATE KEY,只是用来指明底层数据按照那些列进行排序。

建表语句:

CREATE TABLE IF NOT EXISTS test.log_detail
(
 `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`)
DISTRIBUTED BY HASH(`timestamp`) BUCKETS 1
PROPERTIES
(
    "replication_num" = "1"
);

插入部分数据

insert into test.log_detail values
('2017-10-01 08:00:05',1,404,'not found page', 101, '2017-10-01 08:00:05'),
('2017-10-01 08:00:05',1,404,'not found page', 101, '2017-10-01 08:00:05'),
('2017-10-01 08:00:05',2,404,'not found page', 101, '2017-10-01 08:00:06'),
('2017-10-01 08:00:06',2,404,'not found page', 101, '2017-10-01 08:00:07');

查询结果后发现,插入的数据全部会被保留,即使两条数据一模一样,也会保留,正常可以操作用户行为日志数据这种。

timestamp type error_code error_msg op_id op_time
2017-10-01 08:00:05 1 404 not found page 101 2017-10-01 08:00:05
2017-10-01 08:00:05 1 404 not found page 101 2017-10-01 08:00:05
2017-10-01 08:00:05 2 404 not found page 101 2017-10-01 08:00:06
2017-10-01 08:00:06 2 404 not found page 101 2017-10-01 08:00:07

3.3.4数据模型的选择

数据模型在建表时就已经确定,且无法修改;所以,选择一个合适的数据模型非常重要。

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

3.4表索引

索引概述 - Apache Doris

下面说一下倒排索引

倒排索引,是信息检索领域常用的索引技术,将文本分成一个个词,构建 词 -> 文档编号 的索引,可以快速查找一个词在哪些文档出现。

从 2.0.0 版本开始,Doris 支持倒排索引,可以用来进行文本类型的全文检索、普通数值日期类型的等值范围查询,快速从海量数据中过滤出满足条件的行。

建表时定义倒排索引

在建表语句中 COLUMN 的定义之后是索引定义:

CREATE TABLE table_name
(
  column_name1 TYPE1,
  column_name2 TYPE2,
  column_name3 TYPE3,
  INDEX idx_name1(column_name1) USING INVERTED [PROPERTIES(...)] [COMMENT 'your comment'],
  INDEX idx_name2(column_name2) USING INVERTED [PROPERTIES(...)] [COMMENT 'your comment']
)
table_properties;

语法说明如下:

1. idx_column_name(column_name) 是必须的,column_name 是建索引的列名,必须是前面列定义中出现过的,idx_column_name 是索引名字,必须表级别唯一,建议命名规范:列名前面加前缀 idx_

2. USING INVERTED 是必须的,用于指定索引类型是倒排索引

3. PROPERTIES 是可选的,用于指定倒排索引的额外属性,目前支持的属性如下:

  • parser 指定分词器

    - 默认不指定代表不分词

    - english 是英文分词,适合被索引列是英文的情况,用空格和标点符号分词,性能高

    - chinese 是中文分词,适合被索引列主要是中文的情况,性能比 English 分词低

    - unicode 是多语言混合类型分词,适用于中英文混合、多语言混合的情况。它能够对邮箱前缀和后缀、IP 地址以及字符数字混合进行分词,并且可以对中文按字符分词。

    分词的效果可以通过 TOKENIZE SQL 函数进行验证,具体参考后续章节。

  • parser_mode

    用于指定分词的模式,目前 parser = chinese 时支持如下几种模式:

    - fine_grained:细粒度模式,倾向于分出比较短、较多的词,比如 ‘武汉市长江大桥’ 会分成 ‘武汉’, ‘武汉市’, ‘市长’, ‘长江’, ‘长江大桥’, ‘大桥’ 6 个词

    - coarse_grained:粗粒度模式,倾向于分出比较长、较少的词,,比如 ‘武汉市长江大桥’ 会分成 ‘武汉市’ ‘长江大桥’ 2 个词

    - 默认 coarse_grained

  • support_phrase

    用于指定索引是否支持 MATCH_PHRASE 短语查询加速

    - true 为支持,但是索引需要更多的存储空间

    - false 为不支持,更省存储空间,可以用 MATCH_ALL 查询多个关键字

    - 默认 false

    例如下面的例子指定中文分词,粗粒度模式,支持短语查询加速。

    INDEX idx_name(column_name) USING INVERTED PROPERTIES("parser" = "chinese", "parser_mode" = "coar
  • char_filter

    用于指定在分词前对文本进行预处理,通常用于影响分词行为

    char_filter_type:指定使用不同功能的 char_filter(目前仅支持 char_replace)

    char_replace 将 pattern 中每个 char 替换为一个 replacement 中的 char

    - char_filter_pattern:需要被替换掉的字符数

    - char_filter_replacement:替换后的字符数组,可以不用配置,默认为一个空格字符

    例如下面的例子将点和下划线替换成空格,达到将点和下划线作为单词分隔符的目的,影响分词行为。

    INDEX idx_name(column_name) USING INVERTED PROPERTIES("parser" = "unicode", "char_filter_type" = "char_replace", "char_filter_pattern" = "._", "char_filter_replacement" = " ")
  • ignore_above

    用于指定不分词字符串索引(没有指定 parser)的长度限制

    - 长度超过 ignore_above 设置的字符串不会被索引。对于字符串数组,ignore_above 将分别应用于每个数组元素,长度超过 ignore_above 的字符串元素将不被索引。

    - 默认为 256,单位是字节

  • lower_case

    是否将分词进行小写转换,从而在匹配的时候实现忽略大小写

    - true: 转换小写

    - false:不转换小写

    - 从 2.1.2 版本开始默认为 true,自动转小写,之前的版本默认为 false

  • stopwords

    指明使用的停用词表,会影响分词器的行为

    默认的内置停用词表包含一些无意义的词:’is’、’the’、’a’ 等。在写入或者查询时,分词器会忽略停用词表中的词。

    - none: 使用空的停用词表

下面建表查询测试:

​ 下面建表语句中,对comment字段添加倒排索引,parser 参数指定的分词器类型为chinese,即中文分词,分词模式为细粒度模式,表模型为主键模型(能够进行update操作)。

CREATE TABLE hackernews_1m
(
    `id` BIGINT,
    `deleted` TINYINT,
    `type` String,
    `author` String,
    `timestamp` DateTimeV2,
    `comment` String,
    `dead` TINYINT,
    `parent` BIGINT,
    `poll` BIGINT,
    `children` Array<BIGINT>,
    `url` String,
    `score` INT,
    `title` String,
    `parts` Array<INT>,
    `descendants` INT,
    INDEX idx_comment (`comment`) USING INVERTED PROPERTIES("parser" = "chinese","parser_mode" = "fine_grained") COMMENT 'inverted index for comment'
)
    UNIQUE KEY(`id`)
DISTRIBUTED BY HASH(`id`) BUCKETS 10
PROPERTIES ("replication_num" = "1");

插入100000条数据:

curl --location-trusted \
-u root:xu.123456 \
-T /data/file/hacknernews_1m.csv \
http://doris01:8030/api/test_inverted_index/hackernews_1m/_stream_load

image-20250126220306855

更改数据,测试分词

UPDATE hackernews_1m SET comment = '你好世界' WHERE id = 30;
UPDATE hackernews_1m SET comment = '你好啊世界' WHERE id = 33;
UPDATE hackernews_1m SET comment = '世界真美好' WHERE id = 35;
UPDATE hackernews_1m SET comment = '你好啊' WHERE id = 44;
UPDATE hackernews_1m SET comment = '你好啊美丽的世界' WHERE id = 46;
UPDATE hackernews_1m SET comment = '你好美啊,世界' WHERE id = 61;
  1. LIKE 匹配计算 comment 中含有 ‘你好’ 的行数,耗时 0.08s

    SELECT count(1) FROM hackernews_1m WHERE comment LIKE '%你好%';

image-20250126223011113

  1. 用基于倒排索引的全文检索 MATCH_ANY 计算 comment 中含有’你好’的行数,耗时 0.02s,在更大的数据集上效果会更加明显

    SELECT count(1) FROM hackernews_1m WHERE comment MATCH_ANY '你好';

image-20250126223301556

  1. 使用倒排索引查询,查看分词查询结果。

    SELECT id,comment FROM hackernews_1m WHERE comment MATCH_ANY '你好世界';

    image-20250126224419425

4.数据的导入导出

4.1导入数据

4.1.1Insert Into Values

用户可以通过 MySQL 协议,使用 INSERT 语句进行数据导入。

INSERT 语句的使用方式和 MySQL 等数据库中 INSERT 语句的使用方式类似。 INSERT 语句支持以下两种语法:

* INSERT INTO table SELECT ...
* INSERT INTO table VALUES(...)

对于 Doris 来说,一个 INSERT 命令就是一个完整的导入事务。

因此不论是导入一条数据,还是多条数据,我们都不建议在生产环境使用这种方式进行数据导入。高频次的 INSERT 操作会导致在存储层产生大量的小文件,会严重影响系统性能。

该方式仅用于线下简单测试或低频少量的操作。

4.1.2Stream Load

Stream Load 用于将本地文件导入到doris中。Stream Load 支持通过 HTTP 协议将本地文件或数据流导入到 Doris 中。Stream Load 是一个同步导入方式,执行导入后返回导入结果,可以通过请求的返回判断导入是否成功。一般来说,可以使用 Stream Load 导入 10GB 以下的文件,如果文件过大,建议将文件进行切分后使用 Stream Load 进行导入。Stream Load 可以保证一批导入任务的原子性,要么全部导入成功,要么全部导入失败。

提示

相比于直接使用 curl 的单并发导入,更推荐使用专用导入工具 Doris Streamloader。该工具是一款用于将数据导入 Doris 数据库的专用客户端工具,可以提供多并发导入的功能,降低大数据量导入的耗时。

Stream Load 是通过 HTTP 协议与 Doris 进行连接交互的。

该方式中涉及 HOST:PORT 都是对应的HTTP 协议端口。

  • BE 的 HTTP 协议端口,默认为 8040。
  • FE 的 HTTP 协议端口,默认为 8030。

但须保证客户端所在机器网络能够联通FE, BE 所在机器。

基本原理如下: 1.FE提交导入请求 ->生成导入计划->下发给每一个BE节点

导入数据语法,执行 curl 命令导入本地文件(这个命令不是在mysql端执行的哦):

# 语法示例
 curl \
 -u user:passwd \  # 账号密码
 -H "label:load_local_file_test" \  # 本次任务的唯一标识
 -T 文件地址 \
 http://主机名:端口号/api/库名/表名/_stream_load
  • user:passwd 为在 Doris 中创建的用户。初始用户为 root,密码初始状态下为空,[如果有设置以实际为准]。
  • host:port 为 BE 的 HTTP 协议端口,默认是 8040,可以在 Doris 集群 WEB UI页面查看。
  • label: 可以在 Header 中指定 Label 唯一标识这个导入任务。

建表测试:

drop table if exists load_local_file_test;
CREATE TABLE IF NOT EXISTS test.load_local_file_test
(
    id INT,
    name VARCHAR(50),
    age TINYINT
)
unique key(id)
DISTRIBUTED BY HASH(id) BUCKETS 3
PROPERTIES
(
    "replication_num" = "1"
);

在主机上创建导入的数据文件并写入数据( -H “column_separator:,” \表示数据之间以“,”分隔)

1,zss,28
2,lss,28
3,ww,88
curl \
 -u root:xu.123456 \
 -H "label:load_local_file" \
 -H "column_separator:," \
 -T /data.txt \
 http://doris01:8030/api/test/load_local_file_test/_stream_load

curl的一些参数:

  1. label: 导入任务的标签,相同标签的数据无法多次导入。(标签默认保留30分钟)
  2. column_separator:用于指定导入文件中的列分隔符,默认为\t。
  3. line_delimiter:用于指定导入文件中的换行符,默认为\n。
  4. columns:用于指定文件中的列和table中列的对应关系,默认一一对应
image-20250125231820392

4.1.3Broker Load

Broker Load 通过 MySQL API 发起,Doris 会根据 LOAD 语句中的信息,主动从数据源拉取数据。Broker Load 是一个异步导入方式,需要通过 SHOW LOAD 语句查看导入进度和导入结果。

Broker Load 适合源数据存储在远程存储系统,比如对象存储或 HDFS,且数据量比较大的场景。 从 HDFS 或者 S3 直接读取,也可以通过 湖仓一体/TVF 中的 HDFS TVF 或者 S3 TVF 进行导入。基于 TVF 的 Insert Into 当前为同步导入,Broker Load 是一个异步的导入方式。

4.1.3.1 适用场景

  • 源数据在 Broker 可以访问的存储系统中,如 HDFS。
  • 数据量在几十到百 GB 级别。

4.1.3.2 基本原理

  1. 创建提交导入的任务
  2. FE生成执行计划并将执行计划分发到多个BE节点上(每个BE节点都导入一部分数据)
  3. BE收到执行计划后开始执行,从broker上拉取数据到自己的节点上
  4. 所有BE都完成后,FE决定是否导入成功,返回结果给客户端

4.1.3.3使用限制

支持的存储后端:

  • S3 协议
  • HDFS 协议
  • 其他协议(需要相应的 Broker 进程)

支持的数据类型:

  • CSV
  • JSON
  • PARQUET
  • ORC

支持的压缩类型:

  • PLAIN
  • GZ
  • LZO
  • BZ2
  • LZ4FRAME
  • DEFLATE
  • LZOP
  • LZ4BLOCK
  • SNAPPYBLOCK
  • ZLIB
  • ZSTD

4.2数据导出

数据导出概述 - Apache Doris

5.doris的查询函数

5.1doris查询语法的整体结构

SELECT
[ALL | DISTINCT | DISTINCTROW ]            -- 对查询字段的结果是否需要去重,还是全部保留等参数
select_expr [, select_expr ...]            -- select的查询字段
[FROM table_references
 [PARTITION partition_list]                 -- from 哪个库里面的那张表甚至哪一个(几个)分区
 [WHERE where_condition]                    -- WHERE 查询
 [GROUP BY {col_name | expr | position}     -- group by  聚合
 [ASC | DESC], ... [WITH ROLLUP]]
 [HAVING where_condition]                   -- having 针对聚合函数的再一次过滤
 [ORDER BY {col_name | expr | position}     -- 对结果数据按照字段进行排序
 [ASC | DESC], ...]                                       -- 排序规则
 [LIMIT {[offset,] row_count | row_count OFFSET offset}]  -- 限制输出多少行内容
 [INTO OUTFILE 'file_name']                 -- 将查询的结果导出到文件中

5.2doris的内置函数

5.2.1条件函数

  1. if函数

语法示例:

if(boolean condition, type valueTrue, type valueFalseOrNull)
--如果表达式 condition 成立,返回结果 valueTrue;否则,返回结果 valueFalseOrNull
--返回值类型:valueTrue 表达式结果的类型

示例:

SELECT IF(`username` = 'zss', true, false) FROM user;
  1. ifnull,nvl,coalesce,nullif函数

语法示例:

ifnull(expr1, expr2)
--如果 expr1 的值不为 NULL 则返回 expr1,否则返回 expr2
select ifnull(null,200);
select ifnull(100,200);

nvl(expr1, expr2)
--如果 expr1 的值不为 NULL 则返回 expr1,否则返回 expr2
select nvl(100,200);

coalesce(expr1, expr2, ...., expr_n))
--返回参数中的第一个非空表达式(从左向右)
select coalesce(100,200,300);
select coalesce(null,null,300);

nullif(expr1, expr2)
-- 如果两个参数相等,则返回NULL。否则返回第一个参数的值

select nullif(100,100);
select nullif(100,200);
  1. case函数

语法示例:

-- 方式一
CASE expression
    WHEN condition1 THEN result1
    [WHEN condition2 THEN result2]
    ...
    [WHEN conditionN THEN resultN]
    [ELSE result]
END


-- 方式二
CASE WHEN condition1 THEN result1
    [WHEN condition2 THEN result2]
    ...
    [WHEN conditionN THEN resultN]
    [ELSE result]
END

-- 将表达式和多个可能的值进行比较,当匹配时返回相应的结果

5.2.2聚合函数

5.2.2.1 min,max,sum,avg,count

5.2.2.2 min_by和max_by

MAX_BY(expr1, expr2)
返回expr2最大值所在行的 expr1 (求分组top1的简介函数)

MIN_BY(expr1, expr2)
返回expr2最小值所在行的 expr1 (求分组top1的简介函数)

练习:

# 向doris中写入数据
zss,chinese,99
zss,math,89
zss,English,79
lss,chinese,88
lss,math,88
lss,English,22
www,chinese,99
www,math,45
zll,chinese,23
zll,math,88
zll,English,80
www,English,94

-- 建表语句
create table test.score
(
  name varchar(50),
  subject varchar(50),
  score double
)
DUPLICATE KEY(name)
DISTRIBUTED BY HASH(name) BUCKETS 1
PROPERTIES
(
    "replication_num" = "1"
);


curl \
 -u root:xu.123456 \
 -H "label:score" \
 -H "column_separator:," \
 -T /data.txt \
 http://doris01:8040/api/test/score/_stream_load
  1. 查询每门课程成绩最高分的那个人

    # MySQL的方式
    SELECT s1.name, s1.subject, s1.score
    FROM score AS s1
    JOIN (SELECT score.subject, MAX(score) AS max_score
          FROM score GROUP BY subject) AS s2
        ON s1.subject = s2.subject AND s1.score = s2.max_score

    image-20250126114126897

    SELECT subject, MAX_BY(name, score)
    FROM score
    GROUP BY subject;

    image-20250126114236417

5.2.2.3 group_concat

VARCHAR GROUP_CONCAT([DISTINCT] VARCHAR 列名[, VARCHAR sep]

该函数是类似于 sum() 的聚合函数,group_concat 将结果集中的多行结果连接成一个字符串

– group_concat对于收集的字段只能是string,varchar,char类型,当不指定分隔符的时候,默认使用 ‘,’

VARCHAR :代表GROUP_CONCAT函数返回值类型,[DISTINCT]:可选参数,针对需要拼接的列的值进行去 重,[, VARCHAR sep]:拼接成字符串的分隔符,默认是 ‘,’

SELECT score.name,GROUP_CONCAT(score.subject, ',') FROM score GROUP BY score.name

image-20250126114550724

5.2.2.4 collect_list,collect_set

语法示例:

ARRAY<T> collect_list(expr)
--返回一个包含 expr 中所有元素(不包括NULL)的数组,数组中元素顺序是不确定的。

ARRAY<T> collect_set(expr)
--返回一个包含 expr 中所有去重后元素(不包括NULL)的数组,数组中元素顺序是不确定的。

5.2.3日期函数

5.2.3.1 获取当前时间

curdate

current_date

now

curtime

current_time

current_timestamp

5.2.3.2last_day

DATE last_day(DATETIME date) -返回输入日期中月份的最后一天

5.2.3.3from_unixtime

DATETIME FROM_UNIXTIME(INT unix_timestamp[, VARCHAR string_format])
– 将 unix 时间戳转化为对应的 time 格式,返回的格式由 string_format 指定
–支持date_format中的format格式,默认为 %Y-%m-%d %H:%i:%s

– 正常使用的三种格式

  • yyyyMMdd
  • yyyy-MM-dd
  • yyyy-MM-dd HH:mm:ss

5.2.2.4unix_timestamp

将日期转换成时间戳,返回值是一个int类型

  • UNIX_TIMESTAMP(),
  • UNIX_TIMESTAMP(DATETIME date),
  • UNIX_TIMESTAMP(DATETIME date, STRING fmt) – 给一个日期,指定这个日期的格式

5.2.3.5to_date

返回 DATETIME 类型中的日期部分

DATE TO_DATE(DATETIME)

5.2.3.6extract

提取DATETIME某个指定单位的值。–unit单位可以为year, month, day, hour, minute或者second

extract(unit FROM DATETIME)

5.2.3.7date_format

将日期类型按照format的类型转化为字符串

VARCHAR DATE_FORMAT(DATETIME date, VARCHAR format)

5.2.4字符串函数

查看Doris官网

6.Flink Doris Connector

Flink Doris Connector - Apache Doris


Doris
https://xhablog.online/2025/06/23/Doris/
作者
Xu huaiang
发布于
2025年6月23日
许可协议