MariaDB 部署方式、兼容边界与提效工具手册
从一次兼容误判开始
MariaDB 最容易被写错的地方,不是它难装,而是它太像 MySQL。端口像、客户端像、SQL 大部分像、很多旧项目甚至直接用 mysql 命令连接,于是团队很容易把 MariaDB 写成“免费版 MySQL”或“MySQL 换个镜像名”。这会在 collation、JSON、GTID、复制、驱动、备份工具、系统表、SQL_MODE 和高可用方案上慢慢爆雷。
一个典型事故是:开发环境用 MariaDB,测试环境跑 MySQL,JDBC URL 仍是 jdbc:mysql:,迁移脚本又带着 MySQL 8 的 utf8mb4_0900_ai_ci。接口冒烟看似通过,直到新表上线、JSON 查询走到冷门分支或复制链路接入,兼容假设才一起破裂。正确的起点不是争论两个产品谁更好,而是先确认项目真实连接的产品,再把版本、驱动、DDL、备份和复制当成一套不可拆开的契约。
第一次启动实验实例时,需要一台能运行 Docker Engine 或 Docker Desktop 的机器、一个与真实数据隔离的项目目录,以及至少一种客户端。示例把宿主机端口放在 3308,是为了避开常见的本机 3306 和 MySQL 对照实例 3307;端口不是标准,团队可以换,但连接名必须带上产品与环境。
动手前准备:
Docker Engine 或 Docker Desktop,并确认 docker compose version 可用。一个只用于实验的项目目录,例如 your-project/。本机端口 3308 预留给 MariaDB,避免和 MySQL 的 3307 或本机 3306 冲突。
至少一种客户端:mariadb CLI、mysql CLI、DBeaver、DataGrip 或 MariaDB Connector/J。一份 .env.example,只写占位符,不提交真实密码。明确项目真实依赖是 MariaDB 还是 MySQL。开发环境用 MariaDB、生产用 MySQL,或者反过来,都必须把兼容验证写进项目文档。
先看本机端口和已有容器:
docker version
docker compose version
docker ps --format "table {{.Names}}\t{{.Ports}}\t{{.Status}}"Windows:
netstat -ano | findstr ":3306"
netstat -ano | findstr ":3307"
netstat -ano | findstr ":3308"Linux / macOS:
lsof -i :3306
lsof -i :3307
lsof -i :3308如果 MySQL 和 MariaDB 同时跑在本机,连接名称必须写清产品和环境,例如 mysql-local-3307、mariadb-local-3308。只写 local-db 很容易让人误连。
把版本与工具链锁成一份契约
MariaDB 的 Community Server、Enterprise Server、MaxScale、ColumnStore、Operator 和 Connectors 有各自的发布节奏、支持周期与许可条件。开发模板首先要在 Community Server Release Notes 和 全部版本记录 中选择团队验证过的维护线,再把 server、Connector/J、备份工具和容器镜像分别锁定。不要因为网页上出现了更大的版本号,就把 preview、RC、滚动发布或尚未做恢复演练的版本直接升为共享环境基线。
后面的容器示例锁定 mariadb:12.3.2,用于保证命令和预期结果可以复现。项目模板应在升级评审中替换为团队验证过的具体 tag,供应链要求更高时继续锁 digest;latest 和 lts 适合观察发布入口,不适合长期承担可重复构建。
Docker Official Image 的环境变量说明 有两个会直接改变实验结果的规则:
MARIADB_RANDOM_ROOT_PASSWORD、MARIADB_ROOT_PASSWORD_HASH、MARIADB_ROOT_PASSWORD、MARIADB_ALLOW_EMPTY_ROOT_PASSWORD 及其 _FILE 形式至少选择一种;共享环境禁止空 root 密码。除 MARIADB_AUTO_UPGRADE 外,初始化环境变量不会改写已有数据库目录。旧 volume 会保留旧库、旧用户和旧密码,修改 .env 后重启并不等于重新初始化。
10.6 及以上镜像同时出现 MARIADB_* 与兼容的 MYSQL_* 变量时,优先使用 MARIADB_*。新模板统一使用前者,避免项目身份继续模糊。/docker-entrypoint-initdb.d 中受支持的 shell、SQL 和压缩 SQL 文件按文件名顺序处理;/etc/mysql/conf.d 可挂载只读 .cnf。初始化脚本同样只对空数据目录生效。
应用侧也要单独锁版本。MariaDB Connector/J 从 3.0 起默认接受 jdbc:mariadb:;复用 jdbc:mysql: 需要显式兼容参数。数据库 URL、driver class、JDK 基线和连接池版本应进入依赖锁定与升级测试,不要依靠“协议看起来一样”。
跨 MySQL / MariaDB 复制尤其不能套用同产品升级经验。两者 GTID 格式不同,版本、复制方向、binlog 事件、认证插件、JSON 与 collation 都会改变可行性。准备混合复制或迁移窗口时,先按 复制兼容矩阵 核对源端和目标端的精确版本,再用恢复环境重放真实 DDL 与关键事务。
MariaDB Licensing FAQ 给出的产品边界不能混写:Community Server 是 GPL v2,Connector/J 等新连接器采用 LGPL v2.1 或更高版本;连接远程 MariaDB 服务与把 server 或连接库随产品分发,也不是同一种合规场景。MaxScale 还要按版本单独判断:MaxScale 许可说明 明确 25.01 及以后采用商业许可,生产使用需要有效的 Enterprise 订阅;更早版本可能处于 BSL 或已经转换为 GPL,不能拿旧版本结论套到新版本。ColumnStore、Enterprise Operator 与商业支持同样会引入订阅、云费用和运维能力差异。选型表中应分别记录组件版本、软件许可、分发方式、支持期限、实例与存储成本、故障责任方和退出路径,不能把“能下载”推导成“可以无成本用于任何生产形态”。
| 入口 | 适合 | 不适合 | 必须确认 |
|---|---|---|---|
| 本机安装 | 学习 MariaDB CLI、验证服务、与系统包集成 | 团队统一开发入口 | 版本、服务名、端口、数据目录、卸载方式 |
| Docker 单容器 | 快速验证 MariaDB 行为和 MySQL 差异 | 团队长期模板、生产 | tag、root 密码、端口、volume、初始化 |
| Docker Compose | 项目本地联调、CI 临时依赖、团队模板 | 生产 HA | .env、init、healthcheck、旧 volume、清理 |
| 单机多实例 | 多版本兼容、MySQL / MariaDB 对照测试 | 高可用 | 端口、datadir、server_id、日志隔离 |
| 主从复制 | 读扩展、基础冗余、升级迁移 | 强一致高可用幻觉 | binlog、GTID、延迟、切换、旧主隔离 |
| Galera Cluster | 同步复制、高可用、低延迟内网 | 跨地域、写扩展幻想、大事务 | quorum、flow control、主键、网络延迟 |
| MaxScale | 读写分离、路由、故障入口 | 业务一致性替代品 | 路由规则、长事务、延迟读、许可证 |
| 云托管 MariaDB | 降低运维复杂度 | 成本和权限边界不明 | 版本、备份、恢复、参数、审计、退出 |
本机安装
本机安装适合学习系统服务、客户端工具和数据目录,不适合作为团队唯一的可重复环境。安装完成后先用 mariadb --version 和服务端查询确认真实版本,不能把包名当成运行证据。
Windows 使用 MariaDB 下载页提供的 x64 MSI。安装器可以创建 Windows 服务并设置端口、数据目录和 root 密码;不要沿用默认服务名后又同时安装 MySQL,否则脚本很难判断启动的是哪个服务。安装后验证:
Get-Service | Where-Object { $_.Name -match 'MariaDB|MySQL' }
mariadb --version
mariadb -h 127.0.0.1 -P 3308 -uroot -p -e "SELECT VERSION(), @@version_comment;"Debian / Ubuntu 和 RHEL 系发行版可以使用系统仓库,也可以按 MariaDB Package Repository 选择明确的发行线。系统仓库版本可能滞后,MariaDB 仓库默认的 rolling 入口又会随新稳定线移动,因此共享环境必须固定仓库系列,并保留仓库配置和 GPG key 变更记录。最小安装与验证是:
# Debian / Ubuntu
sudo apt update
sudo apt install mariadb-server mariadb-client
# RHEL / Rocky / AlmaLinux
sudo dnf install mariadb-server mariadb
sudo systemctl enable --now mariadb
sudo systemctl status mariadb --no-pager
mariadb --version
sudo mariadb -e "SELECT VERSION(), @@version_comment;"macOS 可按 Homebrew 安装说明 使用 bottle:
brew install mariadb
brew services start mariadb
mariadb --version
mariadb -uroot -e "SELECT VERSION(), @@version_comment;"本机卸载前先导出需要保留的数据,并分别记录服务、配置文件和 data directory。卸载软件包不会自动等价于安全删除数据,删除数据目录也不能替代备份恢复验证。
Docker 单容器
单容器用于个人最小验证:
docker volume create te-mariadb-data
docker run -d \
--name te-mariadb-single \
-p 127.0.0.1:3308:3306 \
-e MARIADB_ROOT_PASSWORD=YOUR_STRONG_MARIADB_ROOT_PASSWORD \
-e MARIADB_DATABASE=te_mariadb \
-e MARIADB_USER=te_app \
-e MARIADB_PASSWORD=YOUR_STRONG_MARIADB_APP_PASSWORD \
-v te-mariadb-data:/var/lib/mysql \
mariadb:12.3.2验证:
docker exec -it te-mariadb-single mariadb \
-ute_app \
-pYOUR_STRONG_MARIADB_APP_PASSWORD \
te_mariadb \
-e "SELECT VERSION(), @@version_comment, CURRENT_USER();"清理:
docker rm -f te-mariadb-single
docker volume rm te-mariadb-data注意:示例密码只表达变量位置。真实团队模板使用 .env、Docker secrets 或密码库,不把真实值写进文档。
Docker Compose
项目模板建议:
dev-dependencies/
mariadb/
compose.yaml
.env.example
conf.d/dev.cnf
init/01-schema.sql
init/02-seed.sql
scripts/verify.sh
scripts/clean-dry-run.sh
scripts/clean-confirmed.sh.env.example:
MARIADB_IMAGE=mariadb:12.3.2
MARIADB_HOST_PORT=3308
MARIADB_ROOT_PASSWORD=YOUR_STRONG_MARIADB_ROOT_PASSWORD
MARIADB_DATABASE=te_mariadb
MARIADB_APP_USER=te_app
MARIADB_APP_PASSWORD=YOUR_STRONG_MARIADB_APP_PASSWORD
MARIADB_ENV=localcompose.yaml:
services:
mariadb:
image: ${MARIADB_IMAGE:-mariadb:12.3.2}
container_name: your-project-mariadb
ports:
- "127.0.0.1:${MARIADB_HOST_PORT:-3308}:3306"
environment:
MARIADB_ROOT_PASSWORD: ${MARIADB_ROOT_PASSWORD}
MARIADB_DATABASE: ${MARIADB_DATABASE:-te_mariadb}
MARIADB_USER: ${MARIADB_APP_USER:-te_app}
MARIADB_PASSWORD: ${MARIADB_APP_PASSWORD}
volumes:
- mariadb-data:/var/lib/mysql
- ./conf.d:/etc/mysql/conf.d:ro
- ./init:/docker-entrypoint-initdb.d:ro
healthcheck:
test:
[
"CMD-SHELL",
"healthcheck.sh --connect --innodb_initialized"
]
interval: 10s
timeout: 5s
retries: 30
restart: unless-stopped
volumes:
mariadb-data:
name: your-project-mariadb-dataconf.d/dev.cnf:
[mariadb]
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
default-time-zone=+00:00
skip-name-resolve=ON
max_connections=200
slow_query_log=ON
long_query_time=1
[client]
default-character-set=utf8mb4不要从 MySQL 8 模板复制 utf8mb4_0900_ai_ci。MariaDB 和 MySQL 的 collation 不完全等价,兼容验证要作为最小验证的一部分。
启动:
docker compose --env-file dev-dependencies/mariadb/.env \
-f dev-dependencies/mariadb/compose.yaml \
up -d停止保留数据:
docker compose --env-file dev-dependencies/mariadb/.env \
-f dev-dependencies/mariadb/compose.yaml \
down个人本地重置:
docker compose --env-file dev-dependencies/mariadb/.env \
-f dev-dependencies/mariadb/compose.yaml \
down -v共享环境禁止无审查 down -v。旧 volume 是 MariaDB 开发环境里最常见的“配置不生效”原因。
初始化脚本
init/01-schema.sql:
CREATE TABLE IF NOT EXISTS tool_verify (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
marker VARCHAR(64) NOT NULL,
payload_json LONGTEXT CHECK (JSON_VALID(payload_json)),
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
KEY idx_tool_verify_marker_created_at (marker, created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;为什么这样写:
使用 utf8mb4_unicode_ci,避免复制 MySQL 8 的 0900 collation。JSON 示例用 LONGTEXT + JSON_VALID,提醒团队不要误以为 MariaDB 与 MySQL binary JSON 完全一致。建一个联合索引,验证执行计划和索引是否可用。
权限
Docker 初始化变量创建的 MARIADB_USER 会对 MARIADB_DATABASE 拿到完整权限。共享环境建议更细分:
CREATE USER IF NOT EXISTS 'te_reader'@'%' IDENTIFIED BY 'YOUR_READER_PASSWORD';
GRANT SELECT ON te_mariadb.* TO 'te_reader'@'%';
CREATE USER IF NOT EXISTS 'te_migration'@'%' IDENTIFIED BY 'YOUR_MIGRATION_PASSWORD';
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, INDEX, DROP
ON te_mariadb.* TO 'te_migration'@'%';生产或共享环境不要把 root 当应用账号。MARIADB_ROOT_HOST=% 也要谨慎,它会扩大 root 远程访问半径。
连接配置
Spring Boot 示例:
spring:
datasource:
url: jdbc:mariadb://${MARIADB_HOST:127.0.0.1}:${MARIADB_PORT:3308}/${MARIADB_DATABASE:te_mariadb}?connectionTimeZone=UTC
username: ${MARIADB_APP_USER:te_app}
password: ${MARIADB_APP_PASSWORD}
driver-class-name: org.mariadb.jdbc.Driver
hikari:
maximum-pool-size: 10
minimum-idle: 2
connection-timeout: 3000不要在新项目里写:
jdbc:mysql://127.0.0.1:3308/te_mariadbMariaDB Connector/J 3.x 默认使用 jdbc:mariadb:。如果团队强行用 jdbc:mysql:,必须显式解释 permitMysqlScheme、类路径里驱动选择和兼容风险。
最小验证必须证明:版本、产品、字符集、collation、JSON、写读清理、执行计划和驱动边界都清楚。
连接:
mariadb -h 127.0.0.1 -P 3308 -ute_app -p te_mariadb确认产品和版本:
SELECT VERSION() AS version, @@version_comment AS comment;
SELECT @@character_set_server, @@collation_server, @@time_zone;
SELECT CURRENT_USER(), DATABASE();写入 JSON:
INSERT INTO tool_verify(marker, payload_json)
VALUES ('mariadb-ready', '{"engine":"mariadb","compatible_with_mysql":false}');
SELECT id, marker, JSON_VALUE(payload_json, '$.engine') AS engine
FROM tool_verify
WHERE marker = 'mariadb-ready'
ORDER BY id DESC
LIMIT 1;预期能看到 marker=mariadb-ready、engine=mariadb。接着用临时表做一次反向验证,证明 MariaDB 的 JSON 列不是任意文本袋子:
CREATE TEMPORARY TABLE json_reject_probe(payload JSON);
INSERT INTO json_reject_probe(payload) VALUES ('not-json');第二条语句应失败并返回约束错误,常见证据为 ERROR 4025 (23000);如果非法文本写入成功,说明实际 schema、server 版本或 SQL_MODE 与预期契约不一致。临时表随会话关闭自动清理,不会污染业务 schema。
执行计划:
EXPLAIN
SELECT id, marker, created_at
FROM tool_verify
WHERE marker = 'mariadb-ready'
ORDER BY created_at DESC
LIMIT 1;清理:
DELETE FROM tool_verify WHERE marker = 'mariadb-ready';
SELECT COUNT(*) AS remaining FROM tool_verify WHERE marker = 'mariadb-ready';remaining 应为 0。这一步把“命令执行过”升级为“测试记录确实被清理”。
兼容验证:
SELECT JSON_VALID('{"a":1}') AS json_valid;
SELECT @@sql_mode;
SHOW VARIABLES LIKE 'collation_server';
SHOW VARIABLES LIKE 'gtid%';如果团队从 MySQL 迁移到 MariaDB,最小验证还要增加:
使用迁移脚本建表。跑核心查询。验证 JSON、collation、generated column、DDL、索引和时间函数。
用 MariaDB Connector/J 跑应用冒烟。用目标备份工具做一次导出和恢复。
项目里建议保留:
dev-dependencies/mariadb/
compose.yaml
.env.example
conf.d/dev.cnf
init/01-schema.sql
verify/verify.sql
scripts/verify-cli.sh
scripts/clean-dry-run.sh
docs/mariadb-contract.mddocs/mariadb-contract.md 至少写:
项目真实数据库产品:MariaDB,不是 MySQL。server、镜像、驱动与备份工具的版本基线和升级窗口。JDBC driver 和 URL scheme。
collation 和字符集。JSON 使用口径。迁移工具和 DDL 审查规则。
备份恢复入口。与 MySQL 的兼容测试清单。
Flyway / Liquibase 接入原则:
migration 脚本按 MariaDB 实跑,不只在 MySQL 上通过。DDL 不复制 MySQL 8 独有 collation。JSON、sequence、generated column、window function、CTE、RETURNING 等语法要按 MariaDB 版本验证。
使用 mariadb profile,不要复用 mysql profile 改端口。
MariaDB 解决的核心架构问题
MariaDB 适合:
团队明确选择 MariaDB Server 作为 MySQL 协议兼容的关系型数据库。需要开源 GPL 口径的社区数据库,并能接受 MariaDB 与 MySQL 差异。现有 Linux 发行版、托管平台或客户现场默认 MariaDB。
需要 Galera、MaxScale、ColumnStore、sequence、temporal table 等 MariaDB 生态能力,并愿意为对应的兼容验证、许可和运维成本负责。
MariaDB 不适合:
只因为“看起来和 MySQL 一样”就替代生产 MySQL。项目大量依赖 MySQL 8 binary JSON、utf8mb4_0900_* collation、MySQL GTID 或特定系统表工具。团队没有能力维护 MariaDB / MySQL 差异,却让本地、测试、生产混用不同产品。
用 Galera 解决所有写扩展问题。用 MaxScale 替代业务一致性设计。
架构师判断重点:MariaDB 不是 MySQL 的影子环境。选它,就要把版本、驱动、备份、复制、语法、权限和工具链按 MariaDB 独立治理。
主流生产架构
单机架构
组成:一个 MariaDB Server,一个数据目录,一个或多个 database。
适合:本地开发、小型内部系统、非核心业务、低并发业务。
问题:单点故障、容量和并发受限。
工具侧最小验证:版本、普通账号、字符集、collation、写读清理、备份恢复。
主从复制
组成:一个 primary,多个 replica,基于 binlog 复制。
能力:
读扩展。数据冗余。升级和迁移缓冲。
风险:
MariaDB GTID 与 MySQL GTID 不兼容。MySQL 到 MariaDB 复制要特别验证 JSON、认证、collation 和 binlog 事件。读写分离仍有延迟读风险。
Galera Cluster
Galera 提供同步复制和多节点高可用能力,但它不是“写扩展神器”。所有节点都可以接收写入,并不等于所有写入都能线性扩展。网络延迟、写冲突、大事务、flow control、最弱节点性能都会影响整个集群。
适合:低延迟内网、强治理团队、需要同步复制和快速故障恢复的场景。
不适合:跨地域高延迟、海量写入、大事务、团队不理解 quorum 的场景。
进入生产前,Galera 运行手册至少要固化引导首节点、加入新节点、quorum 丢失、节点隔离、SST / IST、flow control、备份恢复和整集群重启步骤,并在与生产同拓扑的环境演练。缺少这些动作时,即使三节点都显示在线,也不能把集群判定为可接管。
MaxScale / 代理
MaxScale 可以做 readwritesplit、路由、故障入口和安全层。但代理不懂业务语义:
支付、库存、权限、订单状态等写后读不能盲目走 replica。事务中的读写必须保持会话和路由一致。代理本身要高可用。
MaxScale 许可证、订阅、版本和能力要在采购与升级评审中逐项核对,不能沿用旧环境结论。
云托管
AWS RDS、云厂商 MariaDB 兼容服务或 MariaDB Cloud 可以降低运维成本,但架构师仍要确认:
版本和升级策略。备份、PITR、跨区恢复和下载权限。参数组、插件、权限和审计。
连接方式、白名单、VPC、安全组。成本、SLA 和退出方案。
核心兼容机制
客户端协议像 MySQL,不代表产品等价
MariaDB 使用 MySQL 协议,mysql CLI 也常能连接。但驱动、系统变量、collation、JSON、GTID、information_schema、性能视图和 DDL 行为都可能不同。项目文档必须写 MariaDB,而不是隐藏在 MySQL 名称里。
MARIADB_* 变量优先
Docker 镜像支持 MYSQL_* 兼容变量,是为了迁移方便,不是让新模板继续混用。新项目统一 MARIADB_ROOT_PASSWORD、MARIADB_DATABASE、MARIADB_USER、MARIADB_PASSWORD。否则排障时没人知道这个容器到底按 MySQL 还是 MariaDB 口径维护。
JSON 不是 MySQL binary JSON
MariaDB 有 JSON 函数和兼容语法,但底层实现和 MySQL binary JSON 不等价。核心风险是迁移脚本和 ORM 以为 JSON 类型完全一致。关键业务要验证 JSON 函数、索引、大小、比较和导出导入。
Collation 是高发兼容点
MySQL 8 常见 utf8mb4_0900_ai_ci 不应直接复制到 MariaDB。MariaDB 从 MySQL 复制或迁移时,collation 不兼容会导致 DDL 失败、复制中断或排序结果差异。
GTID 和复制不是通用兼容层
MariaDB 不支持 MySQL 的 GTID 实现。跨 MySQL / MariaDB 复制要依据源端、目标端精确版本选择 binlog file / position 或相应兼容设置,并对 DDL、JSON、collation、认证和事件格式做验证。
Connector/J 要明确 driver
MariaDB Connector/J 的 driver class 是 org.mariadb.jdbc.Driver,新版本默认使用 jdbc:mariadb:。如果类路径同时有 MySQL Connector/J 和 MariaDB Connector/J,URL scheme 会影响实际选中的驱动。
查看版本:
SELECT VERSION(), @@version_comment;查看字符集和 collation:
SHOW VARIABLES LIKE 'character_set_server';
SHOW VARIABLES LIKE 'collation_server';查看当前用户:
SELECT CURRENT_USER(), USER();查看库表:
SHOW DATABASES;
SHOW TABLES;
SHOW CREATE TABLE tool_verify\G导出开发库:
mariadb-dump -h 127.0.0.1 -P 3308 -ute_app -p te_mariadb > te_mariadb.sql恢复到本地临时库:
mariadb -h 127.0.0.1 -P 3308 -uroot -p -e "CREATE DATABASE te_restore;"
mariadb -h 127.0.0.1 -P 3308 -uroot -p te_restore < te_mariadb.sql查看慢日志是否开启:
SHOW VARIABLES LIKE 'slow_query_log';
SHOW VARIABLES LIKE 'long_query_time';查看复制相关变量:
SHOW VARIABLES LIKE 'log_bin';
SHOW VARIABLES LIKE 'gtid%';把容量和成本变成趋势
MariaDB 的容量不能只看“磁盘还有多少”。数据页、索引、binlog、临时表、慢日志、备份副本和 replica 都会消耗空间,连接与 buffer 配置还会把内存成本放大。先建立同一负载下可重复采集的基线:
SELECT table_schema,
ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS used_mb,
ROUND(SUM(data_free) / 1024 / 1024, 2) AS free_mb
FROM information_schema.tables
WHERE table_schema = 'te_mariadb'
GROUP BY table_schema;
SHOW GLOBAL STATUS LIKE 'Threads_connected';
SHOW GLOBAL STATUS LIKE 'Max_used_connections';
SHOW GLOBAL STATUS LIKE 'Created_tmp_disk_tables';
SHOW GLOBAL STATUS LIKE 'Slow_queries';
SHOW VARIABLES LIKE 'max_connections';
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
SHOW VARIABLES LIKE 'binlog_expire_logs_seconds';这些值不能脱离时间窗和业务负载单独下结论。团队应比较每轮发布后的数据与索引增长率、峰值连接占比、落盘临时表趋势、binlog 日增量、全量备份大小和实际恢复耗时。单机成本是计算、主数据、日志和备份;主从至少再乘 replica 数量并增加网络传输;Galera 需要每个节点保存完整数据集,还要为 SST / IST 和流控预留带宽、I/O 与临时空间;MaxScale 与云托管再叠加代理、订阅、跨区流量和备份保留费用。
扩容前先回答瓶颈属于 CPU、buffer pool 命中、磁盘 IOPS、锁等待、连接风暴还是 SQL 与索引。不能用“再加一个 replica”解决写入锁,也不能用“增大 max_connections”掩盖连接池泄漏。升级或扩容验收以同一回放负载下错误率不升、延迟与资源趋势回到预算、备份能在恢复目标内还原为准;演示环境里的绝对数值不能直接成为生产阈值。
MySQL 客户端连到 MariaDB 后误判产品
现象:连接成功,团队以为连的是 MySQL,后续 collation、JSON 或 GTID 才出问题。
判断:
SELECT VERSION(), @@version_comment;修复:连接名称、文档、JDBC URL、驱动 class 都写 MariaDB。不要用 mysql-local 命名 MariaDB 连接。
MARIADB_ROOT_PASSWORD 不生效
现象:改了 .env,root 密码仍是旧值。
判断:
docker volume ls | grep mariadb
docker logs your-project-mariadb --tail 120原因:旧 volume 已经初始化过。Docker 初始化变量只在空数据目录生效。
修复:个人本地确认后 down -v;共享环境走密码轮换 SQL 和变更流程。
初始化脚本没执行
现象:容器 healthy,但表不存在。
判断:看 /docker-entrypoint-initdb.d 挂载路径、文件后缀、日志和旧 volume。
修复:确保脚本文件名有顺序,后缀为官方支持格式。旧 volume 环境不要期待脚本自动重放。
MARIADB_AUTO_UPGRADE 不可控
现象:换镜像 tag 后,容器启动时自动执行系统表升级,随后回退旧镜像变复杂,或者数据目录里出现升级备份文件。
判断:查看容器启动日志、SELECT VERSION();、数据目录里的系统表升级备份和团队是否有升级演练记录。
修复:团队模板不要默认打开 MARIADB_AUTO_UPGRADE。升级前先克隆 volume 或恢复到临时实例,用目标镜像跑升级、应用冒烟、备份恢复和回滚验证。生产运行手册还要固定变更窗口、停止写入条件、备份保留、失败判定、回退镜像与数据恢复责任人。
MySQL collation 报错
现象:Unknown collation: 'utf8mb4_0900_ai_ci' 或复制 / migration 失败。
判断:
SHOW VARIABLES LIKE 'collation_server';
SHOW COLLATION LIKE 'utf8mb4%';修复:按 MariaDB 支持的 collation 重写初始化脚本,并用核心查询验证排序和唯一约束。
JSON 行为不同
现象:MySQL 里的 JSON 字段、函数、索引或 ORM 生成 SQL 在 MariaDB 报错或语义不同。
判断:用 MariaDB 实跑 JSON 插入、查询、校验和导出导入,不看 MySQL 结果替代。
修复:明确 JSON 存储口径、函数清单、索引策略和迁移兼容层。复杂 JSON 查询可能需要改 SQL 或转应用层处理。
Connector/J URL 选错驱动
现象:应用依赖里同时有 MySQL 和 MariaDB 驱动,连接行为不稳定或报协议错误。
判断:看 JDBC URL、driver-class-name、依赖树和启动日志。
修复:使用 jdbc:mariadb: 和 org.mariadb.jdbc.Driver。如果必须 jdbc:mysql:,显式说明 permitMysqlScheme 和风险。
Galera 集群写入变慢
现象:节点都在线,但写入延迟上升、flow control、事务冲突或全局卡顿。
判断:查网络延迟、节点负载、大事务、热点表、主键和 Galera 状态。
修复:减少大事务和热点写,控制节点数量和网络延迟。Galera 解决高可用,不是无限写扩展。
MaxScale 读写分离读到旧数据
现象:写入后立即查询不到最新数据。
判断:看 SQL 路由、事务状态、replica 延迟和 MaxScale readwritesplit 配置。
修复:核心写后读走 primary,或做会话一致性和延迟兜底。不要把“SELECT 自动走 replica”当通用规则。
备份工具版本不匹配
现象:mariadb-backup 失败或恢复后启动异常。
判断:确认备份工具版本、server 版本、存储引擎、加密和压缩选项。
修复:MariaDB Backup 与 server 版本强相关,使用明确版本工具并恢复到临时实例验证。
账号模型:
| 账号 | 用途 | 权限边界 |
|---|---|---|
| root | 本地初始化、紧急维护 | 不给应用长期使用 |
| te_app | 应用运行 | 目标库 DML |
| te_migration | schema 变更 | 目标库 DDL,走审查 |
| te_reader | 排障查询 | SELECT,只读 |
| backup | 备份 | 备份所需最小权限 |
凭证规则:
.env 不提交,.env.example 只放占位符。支持 Docker secrets 时优先使用 *_FILE。GUI 客户端不保存生产 root 密码。
MARIADB_ROOT_HOST=% 必须审查。共享环境账号按项目拆分,不共用 root。导出的 SQL dump 可能包含真实数据和账号信息,必须脱敏和受控保存。
敏感扫描:
rg -n "MARIADB_.*PASSWORD|MYSQL_.*PASSWORD|jdbc:mariadb://|jdbc:mysql://|BEGIN .*PRIVATE KEY|ACCESS_KEY|SECRET" .团队模板至少沉淀:
dev-dependencies/mariadb/compose.yaml。.env.example,只写占位符。conf.d/dev.cnf,写明字符集、collation、时区、慢日志。
init/01-schema.sql,只放开发环境最小对象。verify/verify.sql,验证版本、产品、collation、JSON、写读清理、执行计划。scripts/clean-dry-run.sh 和 scripts/clean-confirmed.sh。
docs/mariadb-contract.md,写清和 MySQL 的兼容边界。
团队职责:
| 角色 | 负责 |
|---|---|
| 架构 / 数据 owner | MariaDB 选型、MySQL 兼容差异、复制 / Galera / MaxScale 边界 |
| 后端 owner | JDBC driver、连接池、migration、JSON / collation 兼容 |
| 测试 owner | MySQL / MariaDB 差异用例、备份恢复冒烟、旧 volume 清理 |
| 平台 / 运维 owner | 共享实例、备份、复制、MaxScale、账号和升级 |
| 安全 owner | root、dump、GUI 连接、生产只读和凭证轮换 |
MariaDB 不是 MySQL 的影子环境
现象:本地 MariaDB、测试 MySQL、生产又是 MariaDB,问题只在某个环境出现。判断入口是 SELECT VERSION(), @@version_comment、JDBC URL、driver class 和镜像 tag。取舍结论:项目必须选择一个主数据库产品,另一个只能作为显式兼容目标,不能让环境随机混用。
旧 volume 会让所有初始化变量失效
现象:密码、库名、用户、初始化 SQL 改了都不生效。判断入口是 Docker volume、entrypoint 日志和数据目录是否已有系统库。个人本地可以重建 volume,共享环境必须走 SQL 变更和备份确认。
MYSQL_* 变量让模板长期混乱
MariaDB 镜像兼容部分 MYSQL_* 变量,但团队新模板应该使用 MARIADB_*。如果同一项目 MySQL、MariaDB 都存在,混用变量会让排障人员判断错误。检查项是 Compose、.env.example、CI secret 和文档里的变量名。
MySQL 8 collation 会在 MariaDB 上失败
现象:migration 报 unknown collation,或者复制 DDL 中断。判断入口是 SHOW COLLATION LIKE 'utf8mb4%'; 和失败 DDL。修复不是随手替换成另一个 collation,而是确认排序、唯一约束和业务查询是否受影响。
JSON 兼容是高风险假设
MariaDB 支持 JSON 函数和兼容语法,但它不是 MySQL binary JSON。判断入口是 JSON_VALID、核心 JSON 查询、ORM 生成 SQL、导出导入和索引方案。只要业务重度依赖 JSON 查询,就必须把 MySQL / MariaDB 差异用例纳入回归。
Connector/J URL 会决定真实驱动
类路径同时存在 MySQL 和 MariaDB 驱动时,jdbc:mysql: 和 jdbc:mariadb: 不是文案差别。判断入口是依赖树、启动日志和 driver-class-name。团队模板使用 jdbc:mariadb:,只有兼容遗留项目才说明 permitMysqlScheme。
GTID 跨产品不兼容会影响迁移和复制
MariaDB 不支持 MySQL 的 GTID 实现。跨产品复制不能只说“都是 binlog”。判断入口是 GTID 变量、binlog 设置、复制状态和官方兼容矩阵。迁移前要做完整复制演练和 DDL / JSON / collation 回归。
Galera 不是写入扩容器
多节点可写会让人误判为写吞吐线性扩展。实际冲突检测、flow control、网络延迟、大事务和最弱节点都会反过来限制集群。上线前要压测热点写、大事务、节点隔离和重加入,不只看三节点能启动。
MaxScale 不能替代业务一致性
readwritesplit 能识别读写,但不知道业务刚完成了支付、扣库存或改权限。核心写后读走 primary,或者引入延迟兜底。路由规则要有业务 owner,代理层还要有高可用和绕过路径。
MariaDB Backup 和 server 版本强相关
备份命令能跑不代表可恢复。判断入口是备份工具版本、server 版本、恢复日志和临时实例验证。生产备份验收以恢复成功为准,不以备份文件存在为准。
root 远程访问半径容易被放大
MARIADB_ROOT_HOST=% 或 GUI 保存 root 密码,会把本地便利变成共享环境风险。检查项是用户列表、host、权限和客户端连接。共享环境 root 只给 owner,应用用普通账号。
自动升级变量会放大回滚风险
MARIADB_AUTO_UPGRADE 适合受控升级流程,不适合开发模板默认开启。判断入口是容器启动日志、系统表状态和数据目录备份文件。升级必须先在副本或临时恢复环境验证,确认应用兼容、备份可恢复、回滚路径存在,再进入共享环境。
从 MySQL 迁移到 MariaDB 不能只跑冒烟接口
冒烟接口覆盖不了 DDL、索引、JSON、collation、SQL_MODE、备份、复制和性能视图差异。迁移验证至少包括 schema diff、核心 SQL、慢查询、备份恢复、驱动连接和回滚计划。没有这些,迁移风险会在上线后以小概率事故出现。
上线与升级放行门禁
部署方式:
明确项目使用 MariaDB,不是把 MySQL 文档改名。镜像 tag 固定,不使用 latest。默认使用 MARIADB_* 变量。
root 密码不为空。端口 3308 未和 MySQL / 本机服务冲突。conf.d/dev.cnf 使用 MariaDB 支持的 collation。
旧 volume 清理路径明确。
项目接入:
JDBC URL 使用 jdbc:mariadb:。driver class 为 org.mariadb.jdbc.Driver。.env.example 只有占位符。
migration 在 MariaDB 上实跑。JSON、collation、SQL_MODE 差异已验证。普通账号、迁移账号、只读账号分开。
架构判断:
主从复制、Galera、MaxScale、云托管的故障模型、成本和接管责任已有选型结论。跨 MySQL / MariaDB 复制已按源端和目标端精确版本验证 GTID、binlog、JSON、collation 和认证。Galera 不被当成写扩展方案。
MaxScale 读写分离有业务一致性兜底。备份恢复在临时实例演练。
安全治理:
root 不给应用。MARIADB_ROOT_HOST=% 已审查。GUI 生产连接默认只读。
SQL dump 和备份文件有脱敏和访问控制。密码使用 .env、secret 或密码库,不进仓库。
可恢复性验证:
SELECT VERSION(), @@version_comment 证明产品和版本。写入、读取、删除测试数据成功。EXPLAIN 能走预期索引。
JSON_VALID 和核心 JSON 查询通过。collation 检查通过。导出和恢复到临时库通过。
