资讯详情

资讯详情

Apache Doris实战:构建农业物联网作物生长数据分析平台

今年年初我接手了公司农业科技团队的作物生长数据分析平台建设项目。老实讲这个项目听着很务农核心技术落到 Apache Doris 上之后整个方案才算真正立起来了。传感器每5分钟上报一次3000个设备一天就产生80多万条记录加上气象和农事数据两三年下来就是几十亿条MySQL 单库已经扛不住实时大屏和离线报表的需求还把数据链路撕成了两半。今天想把从选型、部署、建模、导数据到跑分析、调优的完整过程原原本本记录下来。对于正在做物联网时序数据、农业大数据分析或者单纯在纠结 Doris 到底适不适合自己场景的朋友这篇应该能帮你省掉不少弯路。1. 从几百亿条传感器记录说起为什么这个农业数据场景最终选了 Doris1.1 先拆业务我们要分析的作物生长数据长什么样很多人一听农业数据就觉得量不大这是误解。现代化大棚里温度、湿度、光照、CO2 浓度、土壤 EC 值、土壤 pH 值每一类都是一个独立的采集项。我这边一个中型园区是 3000 个传感器每 5 分钟一次采样单园区每天就是 86.4 万行数据。如果前后端本地网关还做了秒级缓存补传峰值还会更高。除了物联网设备数据还有两条并行数据流一条是气象站的小时级历史数据包括室外温湿度、风速、雨量虽然量不大但参与后续积温模型运算另一条是农事操作记录比如施肥、灌溉、喷药的开始时间和结束时间是典型的低频事件型数据。这三类数据合流之后分析需求也变得很具体一是给园区看板、总部大屏提供实时指标比如当前温度、过去 24 小时湿度曲线二是周期性跑产量预测、病虫害风险评估需要把所有历史数据拉出来做大宽表三是排查设备异常比如某个传感器连续上报越界值要能快速定位。这种“既要实时、又要全量历史、还要能灵活 join 维度表”的组合传统的单机数据库很难满足。1.2 技术选型对比MySQL、HiveSpark、ClickHouse 与 Doris 的取舍选型这件事我其实折腾了两周。不是方案太少而是方案太多每套都有自己的拥趸。我把自己真实的对比过程列出来尽量说人话。方案优势在我们业务里的真实问题MySQL生态熟、运维简单、BI 工具直接连单表数据过亿后聚合查询明显变慢写入高峰和报表查询互相抢资源Hive Spark离线计算能力强、吞吐大从采集到出报表小时级延迟实时大屏完全做不了ClickHouse单表聚合极快、压缩比高主键模型下更新比较弱运维和调优门槛高团队上手慢Doris实时导入 大规模并发查询 MySQL 协议兼容需要额外学习建表和分桶策略但相对平滑最终我选 Doris核心原因有三个。第一它对外提供 MySQL 协议意味着数据大屏工具、低代码平台、Python 的 pymysql 都能直接连 9030 端口不需要专门写一堆适配层。第二Doris 的导入链路很完整Kafka Routine Load 处理实时流Stream Load 灌历史文件Broker Load 拉对象存储一套体系覆盖所有场景。第三Doris 同时支持明细模型、主键模型和聚合模型在建模时可以针对不同表分开设计灵活性比“只能用一种模型”的方案强太多。1.3 平台目标定下来秒级大屏、全量历史、低成本运维选型之后我和团队把平台目标压缩成三条硬指标大屏指标延迟控制在 5 秒以内传感器的最新读数不能超过两条采集周期。历史数据全量入库保留至少两年支持按大棚、作物、时间范围任意切片。整个数据链路只用 Doris Kafka 少量 Python API 服务不养专职运维日常两三个人就能扛住。这三条目标听起来不难但真做起来从部署第一台 Doris 到跑通全链路前后花了一个月。期间踩过的坑接下来按阶段细说。2. 部署这套集群时我实际用到的硬件规划与关键配置2.1 三台物理机的拓扑安排Doris 由 FEFrontend和 BEBackend两类节点组成。FE 管元数据和查询解析BE 管数据存储和计算。官方推荐最小三节点起步我这边是 3 台 16 核 64G 的物理机每台配 2 块 NVMe 固态盘做数据盘万兆内网。拓扑上选的是 FE 和 BE 混合部署也就是每台机器上既跑一个 FE 进程也跑一个 BE 进程。好处是省机器坏处是资源互相抢。我的做法是限制 FE 的内存大小FE 本身不算重给 8G 足够其余内存全部留给 BE 做数据查询缓存。生产环境如果数据量大、并发高还是建议把 FE 独立出去至少把 FE 中的 Follower 节点和 BE 分开部署。网络配置是第一个坑。服务器通常有两块网卡一块管理网一块业务数据网。Doris 必须通过 priority_networks 参数指定业务网段否则节点之间可能走错网卡导致心跳时好时坏。我一开始跳过了这个配置结果 BE 注册后状态一直显示为不可用日志里全是连接超时。2.2 fe.conf 和 be.conf 里容易被忽略的参数部署 Doris 时官方默认配置能跑通 demo但跑生产业务必须调几个参数。我在两个配置文件中实际改过的核心项如下文件参数我的取值说明fe.confpriority_networks192.168.10.0/24绑定业务网段避免心跳错乱fe.confmeta_dir/data/doris/feFE 元数据目录必须放独立磁盘be.confpriority_networks192.168.10.0/24BE 同样要绑网段be.confstorage_root_path/data1/doris, /data2/doris多目录用逗号分隔均衡存储be.confmemory_limit80%默认 90%留出系统余量be.conftablet_meta保持默认元数据路径要保证存在storage_root_path 是最容易被忽视的。如果你有两块数据盘不要只配一块否则另一半空间浪费。Doris 内部会按照目录做数据均衡配好之后Broker Load 和 Compaction 阶段都能更充分地利用磁盘 IO。2.3 从零到三个 BE 节点注册完成的操作步骤我习惯用 tar 包部署不折腾容器。下载二进制包后流程很固定tar -xzf apache-doris-2.0.14-bin-x86_64.tar.gz cd apache-doris-2.0.14-bin-x86_64 mkdir -p /data/doris/fe mkdir -p /data/doris/be # 分别在每台机器编辑对应配置 cd fe vi conf/fe.conf cd ../be vi conf/be.confFE 参数改好之后先启动 FEcd fe sh bin/start_fe.sh --daemon然后启动 BEcd be sh bin/start_be.sh --daemon最后在 FE 上把三个 BE 节点注册进来。注意端口默认是 9050这是 BE 的 heartbeat 端口不要和对外服务端口搞混mysql -h127.0.0.1 -P9030 -uroot -e ALTER SYSTEM ADD BACKEND 192.168.10.11:9050; mysql -h127.0.0.1 -P9030 -uroot -e ALTER SYSTEM ADD BACKEND 192.168.10.12:9050; mysql -h127.0.0.1 -P9030 -uroot -e ALTER SYSTEM ADD BACKEND 192.168.10.13:9050;我一开始漏了关闭系统的 swap。Doris 对内存敏感一旦发生 swap查询延迟会从毫秒级直接跳到秒级而且不好排查。部署阶段老老实实在/etc/sysctl.conf里把vm.swappiness0设好同时把文件句柄数调到 655350。2.4 怎么判断集群状态是健康的注册完 BE 之后不要急着建表先做三件事。第一用SHOW BACKENDS;查看 Alive 字段是否全部为 true。第二用SHOW FRONTENDS;查看 FE 状态。第三用curl http://127.0.0.1:8030/api/health确认 FE 的 HTTP 服务正常。我每次操作后都会顺手执行一次SHOW PROC /cluster_balance;这个视图能反映数据分片在 BE 节点间的分布情况。如果看到某些 BE 的 Num 列明显比其他节点高说明后续建表时的分桶策略可能有问题需要在建模阶段解决。3. 表模型设计明细、聚合、分区与分桶的取舍细节3.1 三种数据模型的适用边界Doris 建模时第一件事就是选模型。我这边业务相对复杂三类表用了三种思路。传感器明细表用 Duplicate 模型因为每条采集记录都要保住原始粒度后续无论是积温计算、窗口函数分析还是异常检测都不希望底层数据被提前合并。农事事件表也用 Duplicate 模型保证事件完整可追溯。维度表比如传感器信息表用 Unique 模型主键是传感器ID更新时直接覆盖旧值。聚合模型确实能减少存储量、加快部分查询但它有一个代价聚合是提前发生的如果你后来想做“查询某一分钟原始波动”这类细粒度分析数据已经合并掉了。除非业务特别明确只需要日/月聚合结果否则不建议把明细数据建在聚合模型上。我刚开始还尝试把传感器表建成聚合模型后来发现要反推原始记录几乎不可能只能删表重建。3.2 分区与分桶一天一个分区哈希分桶避开数据倾斜分区和分桶是整个建模里最影响性能的一步。我的经验是逐日分区加哈希分桶分区列用 dt类型为 DATE分桶列用 sensor_id。为什么要按天分区因为物联网数据天然有强时间维度绝大多数的查询都带时间范围。按天分区后查询可以触发分区裁剪只扫描需要的分区。同时删历史数据可以直接删分区比 DELETE 快出一个量级。比如只保留两年第三年的数据用DROP PARTITION一次清理完毕。分桶数量需要算一下。按我们单园区每天 86 万行、全集群 10 个园区合并上报的场景一天约 860 万行原始每行按 150 字节估算一天的原始数据约 1.3GB压缩后约 300MB。分桶数取 24 时每个桶约 12MB虽然偏小但并发查询不错。如果你数据量更大可以把 BUCKETS 升到 48但注意分桶越多元数据越多小文件也越多将来 Compaction 压力会变大。我的实际建议是单桶数据量控制在 50MB-500MB比如每天 300MB24 桶正好落在这个区间。3.3 传感器事实表的建表语句与解释这是我在生产环境实际使用的建表语句做了脱敏简化。动态分区参数配合 WHEN NOT EXISTS 的加载方式省去手工建分区的麻烦CREATE TABLE crop_db.sensor_reading ( ts DATETIME NOT NULL COMMENT 采集时间, dt DATE NOT NULL COMMENT 分区日期, sensor_id VARCHAR(32) NOT NULL COMMENT 设备SN, plot_id VARCHAR(32) NOT NULL COMMENT 大棚编号, sensor_type VARCHAR(16) NOT NULL COMMENT 读数类型: temperature/humidity/light/soil_moisture/co2, value DECIMAL(10,2) NOT NULL COMMENT 读数, unit VARCHAR(8) NOT NULL COMMENT 单位, etl_time DATETIME DEFAULT NULL COMMENT 入库时间 ) DUPLICATE KEY(ts, sensor_id) PARTITION BY RANGE(dt) () DISTRIBUTED BY HASH(sensor_id) BUCKETS 24 PROPERTIES ( replication_num 2, dynamic_partition.enable true, dynamic_partition.time_unit DAY, dynamic_partition.start -730, dynamic_partition.end 3, dynamic_partition.prefix p, dynamic_partition.buckets 24 );注意几点。分桶键不一定非得是主键的前缀Doris 允许桶列不在排序列里我用 sensor_id 是为了保证单个设备的连续读集中在同一台 BE 上方便按设备分析。DDL 里动态分区的 buckets 需要和 DISTRIBUTED 里的 BUCKETS 保持一致否则动态创建出来的分区会用自身配置覆盖原有分桶数两种桶数并存时看似无害实际上会把元数据搞乱。3.4 维度表和其他辅助表的模型选择传感器维度表我单独建了sensor_info使用主键模型CREATE TABLE crop_db.sensor_info ( sensor_id VARCHAR(32) NOT NULL, plot_id VARCHAR(32) NOT NULL, sensor_type VARCHAR(16) NOT NULL, crop_type VARCHAR(32) NOT NULL, planted_at DATE NOT NULL, install_date DATE NOT NULL, status TINYINT NOT NULL DEFAULT 1 ) UNIQUE KEY(sensor_id) DISTRIBUTED BY HASH(sensor_id) BUCKETS 12 PROPERTIES (replication_num 2);维度表查询频率高但数据量小分桶可以少一些12 个桶足够。这里我踩过另一个坑不要在维度表上使用动态分区维度表如果没有时间分区必要每天自动建分区纯粹是浪费磁盘和元数据。4. 数据导入Kafka 实时流水线和历史 CSV 批量灌库实录4.1 实时链路Routine Load 从 Kafka 消费传感器数据设备数据从网关进入 Kafka我用 Doris 的 Routine Load 直接消费。创建作业的 SQL 非常直观CREATE ROUTINE LOAD crop_db.rl_sensor ON crop_db.sensor_reading COLUMNS(ts, sensor_id, plot_id, sensor_type, value, unit), COLUMNS TERMINATED BY | PROPERTIES ( desired_concurrent_number 3, max_batch_interval 10, max_batch_rows 500000 ) FROM KAFKA ( kafka_broker_list 192.168.10.21:9092,192.168.10.22:9092, kafka_topic sensor_raw, kafka_group_id doris_sensor_group );注意我已经在导入列里把 etl_time 排除了让 Doris 自动填充当前时间。如果 Kafka 消息里还有一个字段是 dt那 dt 也要在 COLUMNS 里列出来直接用消息里的日期字符串匹配分区列类型。创建 Routine Load 之后用SHOW ROUTINE LOAD\G;查看状态关键看 State 是否为 RUNNING以及 Progress 是否持续向前滚动。如果卡住不动多数时候是 JSON/CSV 列数和表对不上去 BE 日志里的be.INFO搜索 ErrorRow 就能看到具体是哪一行、哪个字段解析失败。4.2 离线历史数据Stream Load 灌入三年 CSV历史数据是三年 CSV 文件我分目录按天存放用 Stream Load 批量灌入。命令如下curl --location-trusted -u root: \ -H label:hist_20250101_120000 \ -H column_separator:| \ -H columns:ts,sensor_id,plot_id,sensor_type,value,unit,dtleft(ts,10) \ -T /data/history/2025/sensor_20250101.csv \ http://127.0.0.1:8030/api/crop_db/sensor_reading/_stream_load这里的关键是最后一行dtleft(ts,10)。因为 CSV 里没有 dt 列但表按 dt 分区我通过表达式从 ts 截取日期生成分区字段。这个技巧在 Doris 的 Stream Load 里很好用能省掉预处理 ETL。label 参数是用来做幂等的同一批文件如果因为网络中断重试必须用同一个 label否则会产生重复数据。4.3 幂等与去重label 机制和主键更新模型实时链路里 Routine Load 自带消费位点管理Doris 会把 Kafka 的 offset 记录在元数据里重启作业后能接续消费不会丢数据。但 Kafka 消息本身可能重复比如生产端重试导致同一条消息出现两次。对于 Duplicate 模型这就意味着表中会出现重复行。我们的处理方式是在 Kafka 生产端保证消息内有全局唯一 ID 字段消费导入时把它写进表的一个 extra 字段然后用周期性的SELECT ... GROUP BY uuid HAVING COUNT(*) 1做检查发现重复再按时间窗口清理。这种事后清理方式比在导入阶段做全局去重轻量得多。如果真的必须在导入阶段精确去重就用 Unique 模型主键设为(ts, sensor_id, message_id)Doris 会自动把重复主键覆盖。代价是写入吞吐略降且不能再存储同主键的多版本明细。应用要根据业务定不要盲目去重。4.4 导入质量监控怎么发现丢数据和错数据导入之后必须做质量校验。我每次批量灌完一天的数据都会跑一条统计 SQL和源文件行数对账SELECT dt, COUNT(*) FROM crop_db.sensor_reading WHERE dt 2025-01-01 GROUP BY dt;如果对不上再执行SHOW LOAD WHERE Label hist_20250101_120000;查看 ErrorRows 字段。Doris 默认允许一定比例的错误行超过阈值任务会失败即便任务成功ErrorRows 也可能非零。这些错误行会进入日志需要在导入阶段尽早发现否则等到分析阶段才发现某天数据少了几万行再回补成本就高了。实时链路的监控稍微复杂一点。我写了一个轻量脚本每隔五分钟查一次SHOW ROUTINE LOAD的输出解析统计信息里的 receivedBytes 是否在增长同时对比 Kafka 消费组 Lag两个指标都归零或者长期停滞说明链路堵住了要人工介入。5. 从 SQL 到分析结果生长积温、特征宽表和异常检测的实战5.1 基础聚合每个大棚的日均温湿光数据入库后第一类高频查询是大棚维度的基础统计。典型 SQL 如下SELECT plot_id, dt, ROUND(AVG(CASE WHEN sensor_type temperature THEN value END), 2) AS avg_temp_c, ROUND(AVG(CASE WHEN sensor_type humidity THEN value END), 2) AS avg_humidity, ROUND(MAX(CASE WHEN sensor_type light THEN value END), 2) AS max_light_lux, COUNT(*) AS total_points FROM crop_db.sensor_reading WHERE dt 2025-01-01 AND dt 2025-06-30 GROUP BY plot_id, dt;这个查询在原来的 MySQL 里要跑两三分钟换了 Doris 之后分区裁剪加并行扫描几秒就出结果。这里有一个经验尽量不要在分区列上写函数比如WHERE dt DATE(2025-01-01)这种写法会让分区裁剪失效导致 Doris 扫描全表。直接使用dt 2025-01-01 AND dt 2025-01-02是最稳妥的写法。5.2 GDD 积温计算农业数据分析最有代表性的 SQL作物生长分析中积温Growing Degree DaysGDD是绕不开的指标。它表示作物在某个生长周期内积累的热量通常以每日最高温度和最低温度的平均值减去基础温度来计算。基础温度因作物而异比如番茄常用 10 摄氏度。我们传感器表是 5 分钟粒度理论上可以更精细地用逐分钟温度计算但实际业务用每日最高最低就足够WITH daily_temp AS ( SELECT plot_id, dt, MAX(CASE WHEN sensor_type temperature THEN value END) AS tmax, MIN(CASE WHEN sensor_type temperature THEN value END) AS tmin FROM crop_db.sensor_reading WHERE dt 2025-01-01 AND dt 2025-12-31 GROUP BY plot_id, dt ) SELECT plot_id, ROUND(SUM( CASE WHEN (tmax tmin) / 2.0 - 10 0 THEN (tmax tmin) / 2.0 - 10 ELSE 0 END ), 1) AS season_gdd FROM daily_temp GROUP BY plot_id ORDER BY season_gdd DESC;这个查询的价值在于如果某个大棚的 GDD 比其他同类大棚低不少说明温控策略出了问题要么保温设备故障要么通风窗口设置偏差。它能直接指导农艺师调整管理动作这就是数据分析落到一线业务的样子。5.3 窗口函数构造特征宽表给后续AI模型的数据准备我们后续用 Python 做产量预测但 Python 不直接连大表跑全量而是先从 Doris 出一张特征宽表。用窗口函数在 Doris 内算好变化量再导出给建模脚本。下面是一个典型 SQL按传感器ID计算一小时前后的温度变化和 24 小时滑动平均CREATE TABLE crop_db.crop_features AS SELECT dt, sensor_id, plot_id, value AS temp_c, LAG(value, 12) OVER (PARTITION BY sensor_id ORDER BY ts) AS temp_1h_ago, value - LAG(value, 12) OVER (PARTITION BY sensor_id ORDER BY ts) AS temp_change_1h, AVG(value) OVER ( PARTITION BY plot_id ORDER BY ts ROWS BETWEEN 287 PRECEDING AND CURRENT ROW ) AS temp_24h_avg FROM crop_db.sensor_reading WHERE sensor_type temperature AND dt BETWEEN 2025-01-01 AND 2025-06-30;这里 LAG(value, 12) 是基于 5 分钟一条数据来取的12 条就是 60 分钟前。如果后面采集频率改成 1 分钟这里的 12 就要改成 60建议把这类参数在注释里写清楚否则换个同事接手就是一笔糊涂账。5.4 传感器异常检测连续越界的快速定位SQL传感器本身会坏读数会漂移业务上需要快速发现“连续 3 次越界”的设备。用窗口函数做这个检测很顺WITH ranked AS ( SELECT sensor_id, ts, value, LAG(value, 1) OVER (PARTITION BY sensor_id ORDER BY ts) AS prev_val, LAG(value, 2) OVER (PARTITION BY sensor_id ORDER BY ts) AS prev2_val FROM crop_db.sensor_reading WHERE dt 2025-01-15 AND sensor_type temperature ) SELECT * FROM ranked WHERE value 45 AND prev_val 45 AND prev2_val 45 ORDER BY sensor_id, ts;在 MySQL 里这种查询需要配合自连接或临时表写起来很绕Doris 的窗口函数支持得比较完整直接一次扫描搞定。这里要留意窗口函数在 Doris 上会占用较多内存如果数据量特别大尽量先用分区条件缩小范围避免整个 BE 内存被打满。5.5 实时大屏Doris 到前端可视化的最短链路数据大屏是领导最爱看的东西但也是架构上最容易翻车的地方。Doris 兼容 MySQL 协议前端可以用各种 BI 工具直连 9030但我不建议在大屏服务里让前端直接拼 SQL。更稳的做法是用 Python FastAPI 包一层固定 SQL把维度、时间范围作为参数传入后端只返回聚合后的 JSON。比如页面的“当前园区实时温度”卡片后端 SQL 就是SELECT plot_id, value, ts FROM ( SELECT plot_id, value, ts, ROW_NUMBER() OVER (PARTITION BY plot_id ORDER BY ts DESC) AS rn FROM crop_db.sensor_reading WHERE dt 2025-01-15 AND sensor_type temperature ) t WHERE rn 1;这样前端每 5 秒请求一次后端每次只跑一个极小的聚合查询Doris 的负载完全可接受。大屏卡顿的根因从来不是 Doris而是有人写了全表扫描 SQL 挂在前端上。6. 运行半年后踩过的坑与调优记录6.1 慢查询排查分区裁剪被函数破坏的教训上线后我第一次接到的慢查询反馈是一条按天过滤统计的 SQL 跑了 40 秒。打开EXPLAIN SELECT ...一看扫描分区数居然高达全部几百个。原因就是同事把过滤条件写成了WHERE DATE(ts) 2025-01-10分区列是 dt但查询过滤列却是 ts 的函数结果Doris 无法做裁剪。修复很简单把过滤条件改成WHERE dt 2025-01-10或者同时加上ts 2025-01-10 00:00:00 AND ts 2025-01-11 00:00:00。执行时间瞬间落到 1 秒以内。这条经验后来写进了团队规范所有查询必须显式携带 dt 作为分区条件索引列上禁止套函数。排查慢查询的时候第一步永远是看EXPLAIN里的分区扫描数而不是急着调资源。6.2 BE 内存与查询并发OOM 的那次事故有一段时间每天早上 9 点大屏和报表任务同时跑BE 节点内存告警。现象是高并发查询时部分 SQL 报出内存不足错误点开监控发现 BE 进程的 RSS 已经顶到 60G。原因有两层一是exec_mem_limit没有限制大查询可以无节制吃内存二是报表任务里有人写了跨多分区的大 GROUP BY和实时查询抢内存。最终配置调整如下在be.conf里保持memory_limit为 80%但把单个查询的内存上限通过会话变量收紧SET exec_mem_limit 8G;报表类任务统一放到凌晨低峰期跑避免和大屏实时查询高峰正面冲突遇到超大分析任务用SET enable_mem_tablet false;强制走磁盘临时文件路径宁可慢一点也不要拖垮集群。Doris 的查询内存管理比较细默认行为偏向“能跑就跑”但生产环境必须给它套上缰绳。用过大数据组件的人都知道最怕的不是查询慢而是查询把进程搞挂。6.3 compaction 跟不上磁盘告警和碎片化上线三个月后BE 数据目录开始出现磁盘增长过快的问题。Doris 的 Compaction 机制会在后台把小文件合并成大文件但如果导入过于频繁生成的可视化版本数量太多合并速度跟不上就会表现为“明明删了数据磁盘却不释放”。排查后发现我的实时导入任务desired_concurrent_number设得太高四个并发同时写加上每 10 秒一批产生了大量 10MB 级别的小文件。优化方式把 Routine Load 的max_batch_interval从 10 秒改成 30 秒让单批数据量更大把desired_concurrent_number从 4 降到 2手动触发一次ALTER TABLE sensor_reading COMPACT把历史碎片合并掉。压缩完成后磁盘占用下降了约 35%。这是 Doris 运维里最容易被忽视的一环导入频率和文件粒度必须和 Compaction 能力匹配否则数据量不大但小文件能把集群拖死。6.4 数据倾斜现场按传感器ID分桶的隐患分桶用 sensor_id 本来很合理但实际运行中发现某个 BE 节点的数据量明显偏大。查下来是一个特殊水肥一体机的传感器上报频率是普通传感器的 10 倍。因为分桶键是设备 ID这台高频设备的全部数据都落在同一个桶、同一台 BE 上。解决倾斜有两种方案。简单方式是换成复合分桶键(plot_id, sensor_id)让同一个大棚的数据尽量分散如果业务查询经常只带 sensor_id这种切换会影响性能那就在应用层过滤时增加一个 plot_id 条件。另一种方式是改造采集端把高频设备做本地预聚合比如改成 30 秒一条记录从源头削峰。我最终两个方案都做了数据分桶改为(plot_id, sensor_id)同时通知设备组把秒级高精数据单独归档不进入分析主链路。6.5 一批有用的参数调优参考场景配置/语句预期效果慢查询排序EXPLAIN SELECT ...确认是否触发分区裁剪限制单查询内存SET exec_mem_limit 8G;防大查询拖垮并发压缩碎片ALTER TABLE sensor_reading COMPACT;释放磁盘降低读放大导入频率调整max_batch_interval 30减少小文件缓解 compaction并发导入数desired_concurrent_number 2降低写放大判断节点均衡SHOW PROC /cluster_balance;发现数据倾斜迹象这些调优不是一次性做完全部每次只动一个点观察一两周再继续。Doris 的参数体系庞大推荐用“查询变慢就查分区裁剪内存紧张就限制单查询内存磁盘涨太快就调整导入粒度”这个顺序去排查效率最高。最后说点个人体会。做农业科技数据分析难点从来不只是算法而是数据链路稳不稳。Doris 这套组合帮我解决了从采集入库到分析展示的绝大多数问题但真正让平台稳定运行的是那些踩坑之后的纪律分区条件必须带、导入 label 要管好、分桶键要防止倾斜、大查询要限内存。把这些基本功做扎实Doris 就能成为作物生长数据分析里最省心的那一环。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →