事情是这样:我在 Windows 笔记本上要做一批数据验证,正好想评估 Doris 作为分析型数据库到底合不合适。数据量不大,但我想完整走一遍建表、导入、聚合查询的流程。一开始我直接下载 Doris 的 Linux 二进制包想在本机跑,折腾半天 BE 起不来,各种脚本和路径问题接踵而至。后来换了思路,用 Docker Desktop 在 WSL2 后端里跑 Linux 容器,一套 compose 文件把 FE、BE 拉起来,十分钟不到就进可用的状态。这篇文章把我从零到跑通的完整过程写出来,包括 Docker Desktop 在 Windows 上起不来的排查思路、Doris 的 FE/BE 容器怎么编排、建表导入怎么验证,以及单机跑 Doris 最容易踩的端口和内存坑。如果你也想在 Windows 上快速体验 Doris,或者只是想搭个本地验证环境,这篇可以直接照着抄。
1. 想清楚再动手:为什么是"Doris + Docker + Windows"这套组合
1.1 我到底要解决什么问题
Doris,准确说 Apache Doris,是一个 MPP 架构的分析型数据库。它把节点分成 FE(Frontend)和 BE(Backend)两类角色:FE 负责 SQL 解析、元数据管理、查询规划和协调,BE 负责数据存储和实际计算。对外走的是 MySQL 协议,这意味着你可以用现成的 MySQL 客户端连上去,团队里会 MySQL 的人基本能无缝上手。存储层面是列式存储,典型面向 OLAP 场景,比如十几亿行日志的聚合分析、多表 JOIN、明细查询。
我当时的场景很明确:本地验证 Doris 的明细模型和聚合模型,确认分桶策略、Stream Load 导入方式,再跑几条常见的 GROUP BY 查询,确认表现符合预期后再部署到服务器。这个需求不需要很大的数据量,但必须有一个真实的、可操作的 Doris 环境,而不是只看文档猜结论。
1.2 为什么不在 Windows 上直接跑 Doris 二进制包
这是我最开始的方案,试过之后才决定放弃。Doris 官方发布的是 Linux 二进制包,Windows 上没有现成的启动脚本。FE 是 Java 进程,理论上还能跑,但 BE 依赖大量 Linux 环境能力,比如 mmap、文件描述符限制、shell 脚本执行环境,在 Windows 上要么脚本直接报错,要么路径里带盘符和反斜杠把配置解析搞崩。我碰到的问题是启动脚本无法执行、日志路径异常、进程崩溃后没有明确报错,排起来非常消耗时间。
更关键的是,Doris 的部署文档、社区讨论、配置默认值全部是围绕 Linux 写的。与其在 Windows 上给 Doris 打补丁,不如给它一个完整的 Linux 运行环境。Docker 容器正好干这个事:镜像里自带运行环境、目录结构、启动脚本,Windows 这边只负责跑容器,不需要理解 Doris 内部对系统调用的依赖。
1.3 Docker 方案带来的额外好处
除了绕开环境兼容问题,Docker 方案在日常使用上也更舒服。第一,可复现。Docker Compose 文件就是部署文档,换一台机器,把 compose 文件和 data/log 目录搬过去,docker compose up -d就能起同样的环境。第二,可销毁。验证完了想清理,docker compose down全部删掉,不污染 Windows 系统环境,也不会在注册表里留一堆东西。第三,端口和资源可以随时调整,比如 9030 端口被占用,改一下映射关系就行,不影响容器内部结构。
另外我要说一句选型上的考虑。也有人问我为什么不上 ClickHouse,毕竟单机场景 ClickHouse 也很能打。我承认 ClickHouse 单节点性能很强,但我要验证的方案后续要复用 Doris 的聚合模型、Replace 模型这类功能,而且团队里 MySQL 协议接受度更高。既然目标是 Doris,本地就用 Doris 验证,避免“本地用 A,线上用 B”这种验证失真。
2. 在 Windows 上把 Docker Desktop 跑通是第一道坎:虚拟化与 WSL2
2.1 安装前先确认三件事
很多人卡在 Docker Desktop 启动失败,其实不是软件问题,是前置条件没满足。Docker Desktop 在 Windows 上默认走 WSL2 后端,要求 Windows 10 64 位以上,Win11 更稳。装之前先确认三件事。
第一,BIOS 里的虚拟化必须打开。Intel 平台是 VT-x,AMD 平台是 AMD-V。怎么确认?打开任务管理器,切到“性能”页,看“虚拟化”这一项是不是“已启用”。如果显示未启用,就得重启进 BIOS,在 CPU 配置里找到 Intel Virtualization Technology 或 SVM Mode,打开保存退出。
第二,Windows 功能里要启用“虚拟机平台”和“适用于 Linux 的 Windows 子系统”。最简单的方式是管理员权限运行 PowerShell,执行下面两条命令,然后重启系统:
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart第三,WSL2 内核要装好。老系统可能默认 WSL1,需要手动升级。管理员 PowerShell 里跑:
wsl --update wsl --set-default-version 2装完之后用wsl --status可以看当前默认版本,确认是 2 再装 Docker Desktop。注意 Windows 10 Home 版没有 Hyper-V,但完全不影响,WSL2 不依赖 Hyper-V 的图形界面组件,Docker Desktop 在 Home 版上照常跑。
2.2 Docker Desktop 启动失败的处理路线
Docker Desktop 最典型的报错就是这句:Docker Desktop failed to start because virtualization support wasn't detected,中文环境可能显示“未检测到虚拟化支持”。这个报错信息很误导,它不是说你的 CPU 不支持虚拟化,而是说 Docker Desktop 没有检测到可用的虚拟化环境。
按我排障的顺序来:
- 先回任务管理器确认虚拟化是否真的开启。如果显示“已启用”,跳第 2 步;如果显示“未启用”,去 BIOS 开。
- 确认 Windows 功能里“虚拟机平台”和“适用于 Linux 的 Windows 子系统”都勾上了。只装 Docker Desktop 但没开这两个功能,是最常见的情况。
- 运行
wsl --status看子系统状态。如果有报错提示 WSL 内核太旧,跑wsl --update。 - 如果以上都正常,打开 Docker Desktop 的 Settings -> General,确认勾选了
Use the WSL 2 based engine,然后重启 Docker Desktop。 - 最后一步,如果还是起不来,退出 Docker Desktop,管理员 PowerShell 执行
wsl --shutdown,把所有 WSL 实例彻底停掉再重新启动 Docker Desktop。
我在这步踩过一次坑:Windows 功能里子系统已经开了,但 WSL 内核版本太老,Docker Desktop 卡在启动界面半天,日志里反复出现 WSL 初始化失败的记录。执行wsl --update重启后就好了。
2.3 WSL2 的资源上限必须主动设置
这一步很多人忽略,但单机跑 Doris 必须处理。WSL2 默认内存占用策略是取 Windows 物理内存的 50%(过去是 80%),Doris 的 FE 和 BE 都是吃内存大户,如果不限制,WSL2 可能把整机的内存吃掉大半,Windows 自己开始卡。
WSL2 的资源限制通过用户目录下的.wslconfig文件配置。在C:\Users\你的用户名\.wslconfig里写入:
[wsl2] memory=8GB processors=4 swap=2GB这里的 memory 是 WSL2 虚拟机能用的最大内存,不是固定占用的内存。我建议根据你机器实际内存来设:16GB 内存设 8GB,8GB 内存设 5GB 左右,留足够空间给 Windows 自己。改完配置后,在 PowerShell 执行wsl --shutdown,再重新启动 Docker Desktop,配置才会生效。
另外提醒一句,Doris 的镜像不算小,FE、BE 两个镜像加起来可能要下载几个 GB。如果拉镜像速度很慢,在 Docker Desktop 的 Settings -> Docker Engine 里加一段 registry-mirrors 配置,用国内公共镜像加速地址,比如:
{ "registry-mirrors": [ "https://docker.m.daocloud.io" ] }注意这些公共加速地址随时可能变化,以能拉下来为准。镜像加速我只在纯本机拉取公共镜像这个场景下讨论,不涉及任何其他用途。
3. Docker Compose 部署 Doris:FE 和 BE 的配置细节与启动流程
3.1 镜像选型和目录规划
Doris 官方在 Docker Hub 上的仓库是apache/doris,镜像 tag 一般按版本切成-fe和-be两个后缀,比如我用的apache/doris:2.1.4-fe和apache/doris:2.1.4-be。不同版本的 tag 命名可能略有差异,以 Docker Hub 仓库实际展示为准。如果 apache 官方仓库拉取慢或找不到,可以试一下selectdb/apache-doris,SelectDB 维护的镜像也提供 FE/BE 双镜像,部署参数基本一致。
动手前先把目录结构规划好,我本地建了这么一套:
doris-deploy/ ├─ docker-compose.yml ├─ fe/ │ ├─ data/ │ └─ log/ └─ be/ ├─ data/ └─ log/FE 元数据、BE 数据文件都在挂载目录里,容器删了数据还在。有一个特别要注意的点:我没有挂载 conf 目录。Doris 官方很多示例会建议把 FE、BE 的 conf 目录也挂出来,方便改配置。但在 Windows 上这么做风险很大,Windows NTFS 文件系统与容器内 Linux 用户权限映射经常出问题,容器内 doris 用户写日志时遇到 Permission denied,BE 直接起不来。所以我用最省事的方案:只挂 data 和 log,配置改动用容器内操作完成,后面第 5 节详细讲。
3.2 compose 文件实际写法
直接在doris-deploy目录下创建docker-compose.yml:
services: fe: image: apache/doris:2.1.4-fe container_name: doris-fe hostname: doris-fe environment: FE_MASTER: "true" ports: - "8030:8030" - "9010:9010" - "9030:9030" volumes: - ./fe/data:/opt/apache-doris/fe/data - ./fe/log:/opt/apache-doris/fe/log networks: - doris-net be: image: apache/doris:2.1.4-be container_name: doris-be hostname: doris-be environment: FE_MASTER: "true" ports: - "8040:8040" - "9050:9050" - "9060:9060" - "9070:9070" volumes: - ./be/data:/opt/apache-doris/be/data - ./be/log:/opt/apache-doris/be/log depends_on: - fe networks: - doris-net networks: doris-net: driver: bridge这里有几个关键点要解释清楚。
FE 暴露了三个端口:8030 是 Web UI 和 Stream Load 的 HTTP 端口;9030 是 MySQL 协议端口,客户端连这个;9010 是 FE 之间选举和元数据同步用的 RPC 端口。BE 暴露的端口稍微多些:9050 是 BE 向 FE 上报心跳的端口,8040 是 BE 的 HTTP 服务端口,9060 是 BE 的 Thrift RPC 端口,9070 是 BE 的 BRPC 端口。单机部署时这些端口都映射出来,方便排查问题。
网络层面,我用了自定义 bridge 网络doris-net,而不是默认网络。自定义网络的好处是 Docker 内置 DNS 解析,FE 容器里可以直接通过服务名be解析到 BE 容器的 IP。如果用默认网络,容器间只能靠 IP 通信,容器重建后 IP 会变,后面注册 BE 的地址会失效,维护起来很恼火。
depends_on只能保证 BE 容器在 FE 容器之后创建,不能保证 FE 元数据已经初始化完。所以启动后不要急着加 BE,先确认 FE 真起来了再说。
3.3 启动、注册 BE、检查状态
先启动服务:
docker compose up -d然后观察 FE 日志,等 FE 完成初始化:
docker logs -f doris-fe看到 FE 进程正常监听端口的记录后,打开浏览器访问http://127.0.0.1:8030,能出现 Doris 的 Web 登录页就说明 FE 已经活了。
接下来用 MySQL 客户端连 FE:
mysql -h 127.0.0.1 -P 9030 -uroot如果你电脑上没装 MySQL 客户端也不用急,FE 镜像自带了 mysql 客户端,直接进容器执行:
docker exec -it doris-fe bash -c "mysql -uroot -P9030"默认 root 密码为空,直接回车就能进去。执行下面这条命令注册 BE 节点:
ALTER SYSTEM ADD BACKEND "doris-be:9050";这里必须用doris-be:9050,而不是localhost:9050或127.0.0.1:9050。原因很简单:执行这条 SQL 的是 FE 容器,FE 容器里的localhost指向它自己,它去localhost:9050找 BE 当然找不到,BE 就会一直显示 offline。用doris-be:9050,FE 通过 docker 网络解析到真实的 BE 容器地址,心跳才能建立起来。
注册完后执行:
SHOW BACKENDS;看结果里的Aliveness字段是否为 true,Status是否正常。如果 Aliveness 是 false,多半就是地址写错了,用ALTER SYSTEM DROP BACKEND "错误地址:9050"删掉再重新加。这一步过了,Doris 集群就算真正跑起来了。
4. 建库导入查数:顺带把"几 MB 数据要不要分桶"说透
4.1 MySQL 协议连上 FE,建一张测试表
Doris 对外是 MySQL 协议,所以建库建表的习惯和 MySQL 很像。连上 FE 之后,我建了一个测试库和一张日志表:
CREATE DATABASE demo; USE demo; CREATE TABLE event_log ( day DATE, user_id INT, event VARCHAR(32), cnt INT ) DUPLICATE KEY(day, user_id) DISTRIBUTED BY HASH(user_id) BUCKETS 1 PROPERTIES ("replication_num" = "1");DUPLICATE KEY表示明细模型,适合日志类数据,相同 key 可以多行存在,不丢数据。DISTRIBUTED BY HASH(user_id) BUCKETS 1是数据分布策略,按 user_id 的哈希值分散到 1 个桶里。replication_num设为 1,因为当前只有一个 BE,副本数大于 1 也没有别的节点可以放。
4.2 只有几 MB 数据,到底要不要分桶
这是实际使用 Doris 时很多人纠结的问题,包括我自己。先说结论:如果数据只有几 MB,甚至几十 MB,分桶数直接设 1 就行,不需要为了“看起来标准”去分 16 个或 32 个桶。
为什么?Doris 里一个 bucket 对应一个 tablet,tablet 是数据管理和调度的最小单位。几 MB 的数据如果分成 32 个桶,每个桶里只有几百 KB,结果就是元数据膨胀、导入时产生大量小文件、查询时 Doris 要分别调度 32 个 tablet 再合并结果,纯属给自己加负担。数据量小到一定程度,一个桶反而最快最干净。
但也要考虑一件事:后期扩容。Doris 新增 BE 节点后,负载均衡会把已有的 tablet 在多个 BE 之间搬迁。如果你的表只有 1 个桶,哪怕以后扩到 3 个 BE,这张表的数据也只会落在 1 个 BE 上,多节点并行能力完全用不上。所以如果预判数据量以后会涨到 GB 级,初始 bucket 数建议至少设成未来 BE 数量的倍数,比如预计 3 台 BE,就设 6 或 9 个桶。如果你只是本地验证,今天用完明天可能就删了,BUCKETS 1 就是最优解。
顺便提一句,Doris 导入时对单表 tablet 数量有默认上限限制,如果某张表非要建 1000 个桶,可能报max_tablet_num_per_table超限。小数据场景完全不需要碰这个参数,真碰到了先想想是不是桶建多了。
4.3 导入数据和查询验证
建完表先插几条数据做冒烟测试:
INSERT INTO event_log VALUES ('2025-01-01', 1, 'click', 3), ('2025-01-01', 2, 'view', 1), ('2025-01-02', 1, 'click', 2), ('2025-01-02', 3, 'view', 5);然后跑一条聚合查询:
SELECT day, COUNT(*) AS pv, SUM(cnt) AS total_cnt FROM event_log GROUP BY day ORDER BY day;能正常返回分组结果,说明 FE、BE 和存储链路都是通的。如果只想验证功能,到这一步就够了。不过 Doris 的日常导入很少用 INSERT,更常用 Stream Load,这里也演示一下。准备一个event_log.csv文件,内容就是带表头的逗号分隔数据,然后执行:
curl.exe -X PUT -u root: \ -H "Expect: 100-continue" \ -H "ColumnSeparator: ," \ -T event_log.csv \ http://127.0.0.1:8030/api/demo/event_log/_stream_load这里的127.0.0.1:8030是 FE 的 HTTP 端口,/api/demo/event_log/_stream_load路径里的 demo 是库名,event_log 是表名。-u root:表示用户名 root、密码为空。返回的 JSON 里看Status字段是否为Success,如果是Publish Timeout通常数据已经写入成功,只是事务确认超时,可以查表确认;如果是Label Already Exists,说明这个导入 label 之前用过了,需要换 label 或者清掉旧的。
导入完成后,再用 4.1 的 SELECT 查一遍,确认聚合结果和预期一致。整个验证流程到这里就闭环了。
5. Windows 本机运行的坑:端口冲突、内存限制与容器重启
5.1 端口被占用的定位和解决
Doris 用的端口很多,最常见的是 9030 和 8030 被占用。Windows 上 MySQL 如果之前装过,9030 被 MySQL 占掉的情况非常多;8030 也可能被其他 Web 服务占用。
我的排查习惯是两步走。先用 netstat 看端口对应进程:
netstat -ano | findstr 9030回显里最后一列是 PID。再用 tasklist 查这个 PID 是哪个程序:
tasklist /FI "PID eq 12345"如果确认是多余进程,可以在任务管理器里结束它,或者命令行taskkill /F /PID 12345。
如果你不想动现有进程,更安全的方法是改 compose 文件里的端口映射,把宿主机端口换掉,容器内部端口不变。比如:
ports: - "9031:9030"这样外部访问就用-h 127.0.0.1 -P 9031。改完执行docker compose up -d重建对应容器,Doris 内部元数据不受影响。这个思路同样适用于 8030 被占用的情况。
5.2 FE/BE 内存设置:不调会 OOM
这是 Windows 单机跑 Doris 最隐蔽的一个坑。Doris 默认参数是按服务器设计的,在 Windows + WSL2 环境里,内存是共享的,FE 是 Java 进程,默认堆可能给到 8GB,BE 的mem_limit默认按物理内存的 80% 算,如果 WSL2 没限制,Docker Desktop 就会疯狂吃内存,然后 Windows 开始卡,随后容器被 OOM Kill。
我的处理思路是把 FE 和 BE 的内存都压到适合小内存机器。进入 BE 容器:
docker exec -it doris-be bash sed -i 's/^mem_limit.*/mem_limit = 4GB/' conf/be.conf然后进入 FE 容器调整 Java 堆:
docker exec -it doris-fe bash sed -i 's/^JAVA_OPTS.*/JAVA_OPTS="-Xmx2048m -Xms2048m"/' conf/fe.conf如果 sed 命令在容器里不好用,也可以用docker cp把配置文件拷出来编辑,再拷回去。改完分别重启容器:
docker restart doris-fe docker restart doris-be注意一个细节:因为我们没有挂载 conf 目录,改的配置是写在容器可写层的,只要容器不删除,配置就一直在。一旦docker compose down再up,容器会重建,这些改动就丢了。一个简单的备份方法是把改好的两个 conf 文件用docker cp拉到宿主机保存,下次重建后再传回去。
还有一个思路:如果镜像本身支持环境变量传配置,可以优先用环境变量。但不同版本支持情况不一致,我为了统一,直接用改 conf 文件的方式,效果最确定。
5.3 重启电脑后 Doris 如何自动恢复
本地验证环境的另一个问题是 Windows 经常要重启,每次手动起容器很烦。我做了两个设置之后就省事了。
第一步,Docker Desktop 的 Settings 里勾选Start Docker Desktop when you sign in,让 Windows 登录后 Docker Desktop 自动启动。
第二步,给 compose 文件里的服务加上自动重启策略:
services: fe: restart: unless-stopped be: restart: unless-stopped之后 Windows 重启,Docker Desktop 起来,WSL2 里的容器会按重启策略自动拉起。由于 FE 的元数据存储在挂载目录里,BE 也可以重新向 FE 注册,集群基本能恢复到之前的状态。如果重启后发现SHOW BACKENDS里 BE 短暂 offline,等几十秒再查一次,或者手动docker restart doris-be触发重连,这是正常的启动时序问题,不是集群坏了。
最后分享一个使用习惯:日常验证完数据,不需要容器在后台跑的时候,用docker compose stop停掉而不是docker compose down。stop 只停止容器,再docker compose start就能继续用,数据目录和容器配置都保留;down 会把容器删掉,下次 up 是全新创建。对于本地频繁开关机的验证环境,stop 显然更合适。这套组合我用了挺长一段时间,整体稳定,唯一的建议就是先保证 WSL2 稳定,再谈 Doris 的配置优化。