1. 问题现象

  • 访问 Halo 站点时返回 502 Bad Gateway(由 OpenResty 代理抛出)。

  • 查看容器日志,发现 Halo 启动过程中抛出异常:

    text

    Caused by: io.r2dbc.spi.R2dbcBadGrammarException: [1061] [42000] Duplicate key name 'idx_customer_coupons_customer_status'

    随后还有其他类似索引(如 idx_customer_coupons_coupon_customer)的重复错误,导致应用无法启动。

2. 环境信息

  • Halo 镜像:halohub/halo-pro:2.25.0

  • MySQL 版本:8.4.5(Docker 容器)

  • 部署工具:Docker Compose

  • 网络:自定义外部网络 1panel-network

  • 宿主机:Ubuntu(1Panel 管理)

3. 根本原因分析

Halo 2.25.0 的数据库初始化脚本 schema-mysql.sql 中,多个 CREATE INDEX 语句未包含 IF NOT EXISTS 判断。如果数据库中已存在同名索引(可能源自之前失败的安装或残留),再次执行初始化脚本时就会报 Duplicate key name 错误,导致启动失败。

注:此问题在 Halo 社区中时有发生,官方未在 2.25.0 中修复,推荐使用启动参数 --spring.sql.init.continue-on-error=true 绕过。

4. 尝试过的错误方案(供记录)

  • 手动修改 schema-mysql.sql,添加 IF NOT EXISTS
    虽然文件被挂载到容器内,但 Spring Boot 默认从 jar 包内部加载初始化脚本,外部挂载未生效。即使通过环境变量强制指定外部文件,也因 MySQL 驱动解析问题导致语法错误。

  • 删除重复索引
    手动删除 customer_coupons 表上的冲突索引(以及相关外键)虽然有效,但过程繁琐,且如果其他表也有类似索引,需反复操作,不具通用性。

5. 最终解决方案(推荐)

在 Halo 容器的启动命令中添加两个参数:

  • --spring.sql.init.continue-on-error=true:使 SQL 初始化脚本在遇到错误(如重复索引)时跳过并继续执行,而不是中断启动。

  • --spring.r2dbc.properties.sslMode=disabled禁用 SSL 连接,防止未来可能出现的 MySQL 连接卡死(特别针对旧版 MySQL 的兼容性问题)。

同时,移除任何外部挂载的 schema-mysql.sql 文件,避免干扰默认初始化流程。

修改后的 docker-compose.yml 核心片段

yaml

services:
  halo:
    image: halohub/halo-pro:2.25.0
    container_name: halo-pro
    restart: always
    command:
      - --spring.r2dbc.url=r2dbc:pool:mysql://mysql:3306/halo_85n664?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
      - --spring.r2dbc.username=XXXXX
      - --spring.r2dbc.password=XXXXxXXX
      - --spring.sql.init.platform=mysql
      - --spring.sql.init.continue-on-error=true          # ← 关键
      - --spring.r2dbc.properties.sslMode=disabled        # ← 推荐
    environment:
      - JVM_OPTS=-Xms224m -Xmx384m
    volumes:
      - ./data:/root/.halo2
      # 注意:不再挂载 schema-mysql.sql
    ports:
      - "8090:8090"
    networks:
      - 1panel-network
    deploy:
      resources:
        limits:
          cpus: 0.5
          memory: 448M
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8090/actuator/health"]
      interval: 30s
      timeout: 5s
      retries: 5
      start_period: 30s

6. 操作步骤回顾

  1. 清理冲突索引(可选)

    • 进入 MySQL 容器,删除 customer_coupons 表上的重复索引(如有外键依赖,先删外键)。

    • 但最终解决方案中,continue-on-error 可以忽略这些索引,因此此步并非必需。

  2. 修改配置文件

    • 编辑 docker-compose.yml,添加两个 command 参数,移除外部挂载和环境变量。

  3. 重启容器

    bash

    docker compose down
    docker compose up -d
  4. 验证启动日志

    • 执行 docker logs -f halo-pro,观察是否出现 Started Application in ...,且无 ERROR

  5. 访问站点

    • 浏览器访问 http://服务器IP:8090,正常显示 Halo 页面。