从零开始构建高可用Doris集群:部署、扩容与运维实战
2026/4/15 14:28:49 网站建设 项目流程

1. 为什么选择Doris构建实时分析平台

第一次接触Doris是在处理电商大促期间的实时看板需求时。当时我们的Hive数仓面对高并发查询完全撑不住,而Kafka+Spark Streaming的方案又太复杂。测试了三个开源方案后,Doris以亚秒级响应MySQL协议兼容性胜出。京东广告报表每天100亿行数据写入、上万QPS查询的场景,用Doris能做到99分位延迟150ms——这个真实案例让我决定深入使用。

Doris的MPP架构天生适合实时分析场景。去年帮一家物流公司迁移旧架构时,他们把原本需要Spark+HBase+Phoenix的复杂链路,简化成了纯Doris方案。最直观的效果是查询速度从分钟级降到秒级,运维成本降低了60%。这里分享一个对比表格:

场景传统方案Doris方案效果提升
实时看板Flink+Kudu+ImpalaDoris直接导入延迟降低80%
用户行为分析Hive+Spark SQL+RedisDoris聚合模型查询提速10倍
日志分析Elasticsearch集群Doris倒排索引存储节省50%

特别要提的是Doris的向量化执行引擎。在宽表聚合场景下,实测性能是非向量化引擎的5-10倍。我曾用单台16核BE节点处理每秒20万行的数据扫描,CPU利用率还能保持在70%以下。

2. 生产环境部署实战指南

2.1 硬件选型的黄金法则

在AWS上部署Doris集群时踩过不少坑。有一次客户为了省钱用了c5.xlarge实例,结果BE节点频繁OOM。现在我的配置原则是:FE节点内存=元数据量×3,BE节点内存=热数据量×1.5。具体推荐配置:

  • 开发环境:FE 8核16GB+100GB SSD,BE 8核32GB+500GB SSD(三节点起步)
  • 生产环境:FE 16核64GB+200GB NVMe,BE 32核128GB+4TB SSD(建议10节点起)

磁盘配置有个血泪教训:曾经用SATA盘存BE数据,compaction时IOPS直接打满。现在强制要求:

  1. 元数据目录必须用NVMe
  2. 数据目录每TB预留4000 IOPS
  3. 禁用swap分区(swapoff -a要写入/etc/rc.local)

2.2 系统调优关键步骤

在CentOS 7上部署时需要这些必做操作:

# 文件句柄数调整 echo "* soft nofile 65536" >> /etc/security/limits.conf echo "* hard nofile 65536" >> /etc/security/limits.conf # 关闭透明大页 echo never > /sys/kernel/mm/transparent_hugepage/enabled # 内核参数优化 sysctl -w vm.max_map_count=2000000 sysctl -w net.ipv4.tcp_retries2=5

时钟同步一定要用chrony而不是ntpdate:

yum install -y chrony systemctl enable chronyd systemctl start chronyd chronyc sources -v

3. 高可用集群搭建详解

3.1 FE节点部署技巧

第一次搭建时没注意Follower数量,用了2个Follower+1个Observer,结果Leader宕机时选举直接僵住。现在严格遵守奇数Follower原则

  1. 最小高可用配置:3台FE(全部Follower)
  2. 读写分离配置:3台Follower + N台Observer
  3. 关键参数示例:
# fe.conf meta_dir=/data/doris-meta priority_networks=192.168.1.0/24 http_port=8030 rpc_port=9020 query_port=9030

启动顺序很重要:

# 第一个FE节点 ./start_fe.sh --daemon # 后续节点需要--helper参数 ./start_fe.sh --helper 192.168.1.1:9010 --daemon

3.2 BE节点最佳实践

遇到过最坑的问题是BE磁盘空间计算错误。建议按这个公式规划:

总空间 = 原始数据量 × 副本数 × 1.4(compaction开销)

配置示例:

# be.conf storage_root_path=/data1/doris,50;/data2/doris,50 priority_networks=192.168.1.0/24 be_port=9060 webserver_port=8040 heartbeat_service_port=9050

添加BE节点时有个小技巧:先用ALTER SYSTEM ADD BACKEND注册,再启动BE进程,可以避免端口冲突。

4. 动态扩缩容实战手册

4.1 FE节点扩容陷阱

曾经在扩容Observer时直接复制了Follower的元数据目录,导致集群元数据混乱。正确步骤应该是:

  1. 先在MySQL客户端添加节点
ALTER SYSTEM ADD OBSERVER "192.168.1.4:9010";
  1. 准备干净的元数据目录
mkdir -p /data/doris-meta
  1. 用--helper启动
./start_fe.sh --helper 192.168.1.1:9010 --daemon

4.2 BE节点优雅下线

直接DROP BACKEND会导致数据丢失!应该用DECOMMISSION:

-- 安全下线 ALTER SYSTEM DECOMMISSION BACKEND "192.168.1.101:9050"; -- 查看进度(TabletNum逐渐减少) SHOW PROC '/backends'; -- 取消下线 CANCEL DECOMMISSION BACKEND "192.168.1.101:9050";

有个案例:某客户要下线10个BE节点,但剩余空间不足。我们的解决方案是:

  1. 先扩容20%的新节点
  2. 设置disable_balance=true暂停均衡
  3. 分批下线旧节点

5. 运维中的避坑指南

5.1 升级注意事项

从2.0.3升级到2.0.10时遇到的兼容性问题:

  1. 一定要先备份元数据:
tar czf doris-meta-backup.tar.gz /data/doris-meta
  1. 关闭自动均衡:
ADMIN SET FRONTEND CONFIG ("disable_balance" = "true");
  1. 灰度升级BE节点步骤:
# 老版本停止 ./stop_be.sh # 替换bin/lib目录 mv bin bin_back && mv lib lib_back cp -r /path/to/new/version/{bin,lib} . # 启动验证 ./start_be.sh --daemon

5.2 常见故障处理

案例1:FE启动报错"Check whether the machine time is synchronized"

  • 解决方法:在所有节点部署chrony,确保时间偏差<5000ms

案例2:BE日志出现"Too many open files"

  • 根治方案:
echo "ulimit -n 65536" >> /etc/profile sysctl -w fs.file-max=655360

案例3:查询突然变慢

  • 排查路径:
  1. 检查SHOW BACKENDS的LastHeartbeat
  2. 查看SHOW PROC '/backends'\G的NumRunningQueries
  3. EXPLAIN分析慢查询

6. 性能调优实战技巧

6.1 查询优化三板斧

  1. 分区裁剪:按天分区的表查询时要带上分区条件
-- 反例(全表扫描) SELECT * FROM user_events; -- 正例 SELECT * FROM user_events WHERE dt='2023-08-01';
  1. 索引利用:Z-order索引对多字段范围查询特别有效
ALTER TABLE sensor_data ADD INDEX z_idx(device_id, timestamp) USING ZORDER;
  1. 物化视图:自动路由的物化视图能加速聚合查询
CREATE MATERIALIZED VIEW mv_order_stats DISTRIBUTED BY HASH(order_id) REFRESH ASYNC AS SELECT product_id, COUNT(*) as cnt, SUM(amount) as total FROM orders GROUP BY product_id;

6.2 写入性能优化

遇到过的最棘手问题是Stream Load导入速度波动。优化方案:

  1. 调整BE参数:
# be.conf streaming_load_rpc_max_alive_time_sec=1200 write_buffer_size=104857600
  1. 使用并行导入:
# 多个文件并行导入 curl -X PUT "http://be:8040/api/db/tbl/_stream_load" \ -H "format: json" \ -T data1.json & curl -X PUT "http://be:8040/api/db/tbl/_stream_load" \ -H "format: json" \ -T data2.json &
  1. 监控导入状态:
SHOW ROUTINE LOAD WHERE NAME = "example_load";

7. 安全与权限管理

7.1 用户权限体系

给业务团队开权限时经常遇到权限粒度问题。推荐这样分配:

-- 只读用户 CREATE USER 'readonly' IDENTIFIED BY 'password'; GRANT SELECT_PRIV ON db_name.* TO 'readonly'; -- 数据开发用户 CREATE USER 'etl' IDENTIFIED BY 'password'; GRANT SELECT_PRIV,LOAD_PRIV,ALTER_PRIV ON db_name.* TO 'etl'; -- 管理员 CREATE USER 'admin' IDENTIFIED BY 'password'; GRANT NODE_PRIV,GRANT_PRIV ON *.* TO 'admin';

7.2 网络隔离方案

在某金融客户项目中实施的方案:

  1. 用priority_networks绑定内网IP
  2. 通过iptables限制9030/8030端口访问
  3. 启用SSL加密:
# fe.conf ssl_enabled=true ssl_keystore_password=123456 ssl_keystore_path=/path/to/keystore.jks

8. 监控与告警体系

8.1 关键监控指标

用Prometheus监控这些核心指标:

  1. FE指标

    • fe_jvm_heap_used
    • fe_qps
    • fe_request_latency
  2. BE指标

    • be_memtable_flush_count
    • be_compaction_score
    • be_tablet_num

Grafana面板配置示例:

SELECT avg(be_compaction_score) as score FROM doris_be_metrics WHERE time > now() - 1h GROUP BY host

8.2 自定义告警规则

这些规则帮我们避免过多次生故障:

  1. BE节点compaction积压:
alert: BECompactionBacklog expr: avg(be_compaction_score) by (host) > 1000 for: 30m
  1. 查询内存超限:
alert: QueryMemoryExceeded expr: sum(be_query_mem_bytes) by (host) / be_mem_limit_bytes > 0.8 for: 5m

9. 数据迁移实战

9.1 从Hive迁移

用Spark Connector高效迁移:

val df = spark.sql("SELECT * FROM hive_table") df.write .format("doris") .option("doris.table.identifier", "db.target_table") .option("doris.fenodes", "fe:8030") .option("user", "user") .option("password", "pass") .save()

9.2 从MySQL迁移

用DataX配置示例:

{ "job": { "content": [{ "reader": { "name": "mysqlreader", "parameter": { "username": "root", "password": "123456", "column": ["*"], "connection": [{ "table": ["source_table"], "jdbcUrl": ["jdbc:mysql://mysql:3306/db"] }] } }, "writer": { "name": "doriswriter", "parameter": { "feNodes": "fe:8030", "username": "root", "password": "", "database": "db", "table": "target_table", "column": ["*"] } } }] } }

10. 典型应用场景实现

10.1 实时数仓架构

某电商公司的实时Pipeline:

Kafka → Routine Load → Doris明细表 → 聚合模型物化视图 → BI工具

关键配置:

CREATE ROUTINE LOAD db.job ON tbl COLUMNS(col1, col2) PROPERTIES ( "desired_concurrent_number"="3", "max_batch_interval"="20" ) FROM KAFKA ( "kafka_broker_list" = "kafka:9092", "kafka_topic" = "topic", "property.group.id" = "doris_consumer" );

10.2 用户画像系统

用Aggregate Key模型实现标签更新:

CREATE TABLE user_profile ( user_id BIGINT, gender VARCHAR(10), age INT, last_active DATETIME, tags JSON ) UNIQUE KEY(user_id) DISTRIBUTED BY HASH(user_id) PROPERTIES ( "replication_num" = "3", "enable_persistent_index" = "true" );

更新标签时的妙招:

INSERT INTO user_profile VALUES (123, 'male', 25, NOW(), '{"preference":"electronics"}') ON DUPLICATE KEY UPDATE last_active=VALUES(last_active), tags=JSON_MERGE(tags, VALUES(tags));

11. 高级特性深度应用

11.1 倒排索引优化

日志分析场景的优化方案:

CREATE TABLE error_logs ( log_time DATETIME, service VARCHAR(32), level VARCHAR(16), message TEXT, INDEX idx_message(message) USING INVERTED ) DUPLICATE KEY(log_time, service) PARTITION BY RANGE(log_time) () DISTRIBUTED BY HASH(service); -- 快速检索错误日志 SELECT * FROM error_logs WHERE message MATCH 'NullPointerException';

11.2 Colocate Group

解决大表Join性能问题:

-- 创建Colocate Group CREATE TABLE orders ( order_id BIGINT, user_id BIGINT, amount DOUBLE ) DISTRIBUTED BY HASH(user_id) PROPERTIES ( "colocate_with" = "user_group" ); CREATE TABLE users ( user_id BIGINT, name VARCHAR(100) ) DISTRIBUTED BY HASH(user_id) PROPERTIES ( "colocate_with" = "user_group" ); -- 自动本地Join SELECT u.name, SUM(o.amount) FROM users u JOIN orders o ON u.user_id = o.user_id GROUP BY u.name;

12. 疑难问题解决方案

12.1 内存控制技巧

处理过最棘手的内存问题是BE节点OOM。现在采用组合拳:

  1. 设置查询内存限制:
SET exec_mem_limit = 8589934592; -- 8GB
  1. 启用Spill功能:
# be.conf disable_storage_page_cache=false storage_engine_cache_size=10737418240
  1. 监控内存使用:
SHOW BACKENDS\G -- 查看MemUsedPct

12.2 副本修复策略

当出现副本缺失时的处理流程:

  1. 先检查BE状态:
SHOW PROC '/backends'\G
  1. 手动触发修复:
ADMIN REPAIR TABLE db.tbl PARTITION(p1);
  1. 设置副本优先级:
ALTER TABLE db.tbl SET ("replica_allocation" = "tag.location.zone1:2, tag.location.zone2:1");

13. 成本优化实践

13.1 冷热数据分离

某IoT客户的降本方案:

-- 热数据(SSD) CREATE TABLE device_recent ( device_id BIGINT, metric DOUBLE ) DISTRIBUTED BY HASH(device_id) PROPERTIES ( "storage_medium" = "SSD", "storage_cooldown_time" = "7 days" ); -- 冷数据(HDD) CREATE TABLE device_history ( device_id BIGINT, metric DOUBLE ) DISTRIBUTED BY HASH(device_id) PROPERTIES ( "storage_medium" = "HDD" );

13.2 资源隔离方案

通过资源组实现多租户隔离:

-- 创建资源组 CREATE RESOURCE GROUP etl_group TO ( "user1" = "80%", "user2" = "20%" ) WITH ( "cpu_share" = "10", "memory_limit" = "30%" ); -- 查询时指定资源组 SET resource_group = 'etl_group';

14. 未来演进方向

Doris社区最近发布的2.1版本有几个值得期待的特性:

  1. Light Schema Change:毫秒级DDL操作
  2. Nested Type:原生支持Map/Struct类型
  3. Workload Group:更精细的资源隔离

在测试新版物化视图时发现个实用技巧:通过EXPLAIN查看查询是否命中物化视图:

EXPLAIN SELECT product_id, COUNT(*) FROM orders GROUP BY product_id; -- 输出中找到:SCAN MATERIALIZED VIEW mv_order_stats

15. 真实案例复盘

15.1 电商大促备战

某次双11前做的优化:

  1. 提前扩容50% BE节点
  2. 设置disable_auto_compaction=true暂停后台压缩
  3. 调整Stream Load参数:
streaming_load_max_mb = 2048 streaming_load_rpc_max_alive_time_sec = 600

大促期间保持稳定的关键指标:

  • 查询P99延迟 < 500ms
  • 导入延迟 < 30s
  • BE CPU利用率 < 70%

15.2 金融级容灾方案

为某银行设计的双活架构:

  1. 两地各部署完整集群
  2. 用Binlog Load同步数据
  3. 通过VIP实现故障切换
-- 主集群配置 CREATE SYNC JOB sync_to_dr FROM Doris TO Doris PROPERTIES ( "host" = "dr_fe:8030", "port" = "9030", "user" = "sync_user", "password" = "password" );

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询