一、初识 Apache Doris

Apache Doris 是一款基于 MPP(大规模并行处理)架构的高性能、实时分析型数据库,以高效、简单和统一的特性著称,能够在亚秒级时间内返回海量数据的查询结果。它同时支持高并发的点查询和高吞吐的复杂分析场景,适合用于报表分析、即席查询、统一数仓构建、用户行为分析、订单分析等场景。

Doris 采用 MySQL 协议,高度兼容 MySQL 语法,支持标准 SQL,可以通过各类 MySQL 客户端工具直接连接,学习成本极低。

本文将聚焦于 Doris 的核心操作——增删改查,从建表到数据查询,提供完整的 SQL 示例,帮助读者快速上手。

二、数据模型概述(建表必读)

Doris 提供三种数据模型,在建表时需要根据业务场景选择:

模型 特点 适用场景
DUPLICATE KEY 保留所有原始数据,按 Key 列排序 明细数据存储,需要保留全部历史记录
UNIQUE KEY Key 列唯一,新数据覆盖旧数据 主键去重场景,如订单表、用户表
AGGREGATE KEY 按 Key 列聚合,支持预聚合 报表统计场景,如按维度汇总指标

新手注意:DUPLICATE KEY 不是“去重键”,而是“排序键”,数据写入时按该列排序存储,目的是优化查询过滤效率。

三种模型的数据都按 Key 列进行排序存储,建表时确定后无法修改,所以选择合适的数据模型非常重要。

三、准备工作

3.1 连接 Doris

使用 MySQL 客户端连接 Doris:

mysql -h <FE_HOST> -P <FE_PORT> -u <USERNAME> -p
# 默认 root 用户密码为空

3.2 创建数据库

-- 创建数据库
CREATE DATABASE IF NOT EXISTS doris_demo;
​
-- 切换到数据库
USE doris_demo;

四、建表(CREATE)

4.1 DUPLICATE KEY 模型(明细表)

场景:存储用户行为日志,保留所有访问记录,不进行任何聚合。

CREATE TABLE IF NOT EXISTS user_actions_log (
    user_id BIGINT NOT NULL COMMENT "用户ID",
    action_time DATETIME NOT NULL COMMENT "行为时间",
    action_type VARCHAR(50) NOT NULL COMMENT "行为类型",
    page_url VARCHAR(500) NOT NULL COMMENT "页面URL",
    duration INT DEFAULT 0 COMMENT "停留时长(秒)"
) DUPLICATE KEY(user_id, action_time)
DISTRIBUTED BY HASH(user_id) BUCKETS 10
PROPERTIES("replication_num" = "1");

4.2 UNIQUE KEY 模型(主键表)

场景:存储用户信息,保证每个 user_id 只有一条最新记录。

CREATE TABLE IF NOT EXISTS user_profile (
    user_id BIGINT NOT NULL COMMENT "用户ID",
    user_name VARCHAR(100) NOT NULL COMMENT "用户姓名",
    age INT COMMENT "年龄",
    city VARCHAR(100) COMMENT "城市",
    balance DECIMAL(10, 2) DEFAULT 0 COMMENT "账户余额"
) UNIQUE KEY(user_id)
DISTRIBUTED BY HASH(user_id) BUCKETS 10
PROPERTIES("replication_num" = "1");

4.3 AGGREGATE KEY 模型(聚合表)

场景:按日期和商品统计的销售报表,支持预聚合。

CREATE TABLE IF NOT EXISTS sales_summary (
    sale_date DATE NOT NULL COMMENT "销售日期",
    product_id BIGINT NOT NULL COMMENT "商品ID",
    total_amount DECIMAL(20, 2) SUM DEFAULT "0" COMMENT "总金额(SUM)",
    sale_count BIGINT SUM DEFAULT "0" COMMENT "销售数量(SUM)",
    max_price DECIMAL(10, 2) MAX DEFAULT "0" COMMENT "最高单价(MAX)",
    min_price DECIMAL(10, 2) MIN DEFAULT "0" COMMENT "最低单价(MIN)"
) AGGREGATE KEY(sale_date, product_id)
DISTRIBUTED BY HASH(product_id) BUCKETS 10
PROPERTIES("replication_num" = "1");

五、数据插入(INSERT)

5.1 单行插入

-- UNIQUE KEY 表插入单行数据
INSERT INTO user_profile(user_id, user_name, age, city, balance)
VALUES(1001, '张三', 25, '北京', 1000.00);
​
INSERT INTO user_profile(user_id, user_name, age, city, balance)
VALUES(1002, '李四', 30, '上海', 2500.50);

5.2 多行批量插入

-- DUPLICATE KEY 表批量插入
INSERT INTO user_actions_log VALUES
(1001, '2024-01-15 10:30:00', 'click', '/home', 5),
(1001, '2024-01-15 10:35:00', 'click', '/product', 30),
(1002, '2024-01-15 10:32:00', 'purchase', '/checkout', 120),
(1003, '2024-01-15 10:40:00', 'view', '/home', 10),
(1004, '2024-01-15 10:45:00', 'click', '/search', 8);
​
-- AGGREGATE KEY 表插入
INSERT INTO sales_summary VALUES
('2024-01-15', 101, 1500.00, 3, 600.00, 400.00),
('2024-01-15', 102, 2500.00, 5, 550.00, 450.00),
('2024-01-16', 101, 800.00, 2, 420.00, 380.00);

5.3 INSERT INTO SELECT(数据复制)

-- 从现有表插入数据到新表
INSERT INTO user_profile_backup SELECT * FROM user_profile;

注意:Doris 默认提供 UPSERT 语义,对于 UNIQUE KEY 表,插入相同主键的数据会覆盖原有记录。

六、数据查询(SELECT)

6.1 基础查询

-- 查询所有数据
SELECT * FROM user_profile;
​
-- 查询指定字段
SELECT user_id, user_name, city FROM user_profile;
​
-- 条件查询(WHERE 子句)
SELECT * FROM user_actions_log 
WHERE action_type = 'click' AND user_id = 1001;

6.2 聚合查询

-- 统计每种行为类型的数量
SELECT action_type, COUNT(*) as cnt 
FROM user_actions_log 
GROUP BY action_type;
​
-- 统计用户行为次数
SELECT user_id, action_type, COUNT(*) as action_count
FROM user_actions_log
GROUP BY user_id, action_type
ORDER BY action_count DESC;

6.3 多条件与范围查询

-- IN 查询
SELECT * FROM user_actions_log 
WHERE action_type IN ('click', 'view');
​
-- 范围查询(BETWEEN)
SELECT * FROM user_actions_log 
WHERE action_time BETWEEN '2024-01-15 10:00:00' AND '2024-01-15 12:00:00';
​
-- LIMIT 分页
SELECT * FROM user_profile ORDER BY user_id LIMIT 10;

6.4 多表关联查询

-- 查询用户行为详情(关联 behavior_log 和 user_profile)
SELECT a.user_id, a.action_type, a.action_time, b.user_name, b.city
FROM user_actions_log a
LEFT JOIN user_profile b ON a.user_id = b.user_id
WHERE a.action_type = 'purchase';

七、数据更新(UPDATE / UPSERT)

7.1 UPDATE 语句

Doris 支持标准 SQL 的 UPDATE 语法,非常适合低频、批量更新场景。

-- 更新指定用户的年龄
UPDATE user_profile SET age = age + 1 WHERE user_id = 1001;
​
-- 批量更新:将所有北京用户的余额增加 100 元
UPDATE user_profile SET balance = balance + 100 WHERE city = '北京';
​
-- 更新后查看结果
SELECT * FROM user_profile;

7.2 通过 INSERT 实现 UPSERT

对于 UNIQUE KEY 表,插入相同主键的数据会自动覆盖旧记录,实现 Upsert 效果:

-- 插入相同 user_id(1001)的新数据,自动覆盖原记录
INSERT INTO user_profile(user_id, user_name, age, city, balance)
VALUES(1001, '张三', 26, '北京', 1200.00);
​
-- 查看更新结果
SELECT * FROM user_profile WHERE user_id = 1001;

7.3 批量 UPSERT

-- 一次性更新多条记录
INSERT INTO user_profile VALUES
(1001, '张三', 26, '北京', 1200.00),
(1002, '李四', 31, '上海', 2700.00),
(1005, '王五', 28, '深圳', 3000.00);  -- 新增用户

7.4 部分列更新(2.0 版本以上)

如果只想更新部分列而非整行,需要先开启会话变量:

-- 开启部分列更新功能
SET enable_unique_key_partial_update = true;
​
-- 只更新 age 和 city 两列(必须包含所有 Key 列)
INSERT INTO user_profile(user_id, age, city)
VALUES(1001, 27, '广州');

注意:使用部分列更新时,插入的列必须至少包含所有 Key 列。

八、数据删除(DELETE)

8.1 DELETE 语句

-- 删除指定用户的行为日志
DELETE FROM user_actions_log WHERE user_id = 1004;
​
-- 删除特定类型的行为记录
DELETE FROM user_actions_log WHERE action_type = 'view';
​
-- 删除时间范围内的记录
DELETE FROM user_actions_log 
WHERE action_time < '2024-01-15 10:30:00';

8.2 清空表数据

-- 删除表中所有数据(保留表结构)
TRUNCATE TABLE user_actions_log;
​
-- 或使用 DELETE 不加条件
DELETE FROM user_actions_log;

8.3 删除表

-- 删除整个表
DROP TABLE IF EXISTS user_actions_log;

九、高阶操作:表结构变更

在实际使用中,经常需要修改表结构,Doris 提供了灵活的 SCHEMA CHANGE 能力。

9.1 增加列

-- 增加新列
ALTER TABLE user_profile ADD COLUMN phone VARCHAR(20) DEFAULT '' COMMENT "手机号";
​
-- 在指定列后增加新列
ALTER TABLE user_profile ADD COLUMN email VARCHAR(200) AFTER city;

9.2 删除列

-- 删除指定列
ALTER TABLE user_profile DROP COLUMN phone;

9.3 修改列类型

-- 修改列类型
ALTER TABLE user_profile MODIFY COLUMN age BIGINT;

9.4 查看 Schema Change 进度

-- 查看表结构变更作业进度
SHOW ALTER TABLE COLUMN;
​
-- 当作业状态为 FINISHED 时,表示修改完成

十、实战:完整电商用户分析示例

以下是一个完整的电商用户行为分析流程,涵盖建表、插入、查询、更新、删除全链路:

Step 1:建表

-- 创建数据库
CREATE DATABASE IF NOT EXISTS ecommerce;
USE ecommerce;
​
-- 1. 用户信息表(UNIQUE KEY)
CREATE TABLE users (
    user_id BIGINT NOT NULL,
    name VARCHAR(100) NOT NULL,
    register_date DATE NOT NULL,
    level VARCHAR(20) DEFAULT '普通会员'
) UNIQUE KEY(user_id)
DISTRIBUTED BY HASH(user_id) BUCKETS 10;
​
-- 2. 订单表(UNIQUE KEY + 聚合列)
CREATE TABLE orders (
    order_id BIGINT NOT NULL,
    user_id BIGINT NOT NULL,
    order_time DATETIME NOT NULL,
    product_id BIGINT NOT NULL,
    amount DECIMAL(10, 2) NOT NULL
) UNIQUE KEY(order_id)
DISTRIBUTED BY HASH(order_id) BUCKETS 10;

Step 2:插入数据

-- 用户数据
INSERT INTO users VALUES
(1, 'Alice', '2024-01-10', '金牌会员'),
(2, 'Bob', '2024-01-12', '银牌会员'),
(3, 'Carol', '2024-01-15', '普通会员');
​
-- 订单数据
INSERT INTO orders VALUES
(1001, 1, '2024-01-15 10:00:00', 501, 299.00),
(1002, 1, '2024-01-15 14:30:00', 502, 99.00),
(1003, 2, '2024-01-16 09:15:00', 501, 299.00);

Step 3:查询分析

-- 查询每位用户的订单总金额
SELECT u.user_id, u.name, SUM(o.amount) as total_spent
FROM users u
LEFT JOIN orders o ON u.user_id = o.user_id
GROUP BY u.user_id, u.name
ORDER BY total_spent DESC;

Step 4:更新用户等级

-- 消费累计超过 500 元的用户升级为金牌会员
UPDATE users SET level = '钻石会员' 
WHERE user_id IN (
    SELECT user_id FROM orders GROUP BY user_id HAVING SUM(amount) >= 500
);

Step 5:删除数据

-- 删除 2024-01-15 之后的订单记录
DELETE FROM orders WHERE order_time > '2024-01-15 23:59:59';

总结

本文全面介绍了 Apache Doris 的增删改查操作,涵盖:

操作 核心语法 关键注意事项
建表 CREATE TABLE ... [模型] KEY(...) 选对数据模型,不可修改
插入 INSERT INTO ... VALUES UNIQUE KEY 表自动 Upsert
查询 SELECT ... FROM ... WHERE ... 兼容 MySQL 语法,支持 JOIN
更新 UPDATE ... SET ... WHERE ... 适合低频批量更新
删除 DELETE FROM ... WHERE ... 支持条件删除和清空表
表结构变更 ALTER TABLE ... ADD/DROP/MODIFY COLUMN Schema Change 异步执行

相比传统数据库,Doris 的优势在于其 MPP 架构和列式存储带来的海量数据亚秒级查询能力。本文提供的示例代码在 Doris 2.x 及以上版本均可运行,读者可以根据实际业务场景进行调整。如有疑问,欢迎留言交流!

📌 参考资料:本文基于 Apache Doris 官方文档整理,更多详细信息可访问官网:https://doris.apache.org/zh-CN/

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐