🎯 功能简介
这是一个通用的 Python 脚本,用于将 MySQL 导出的 SQL 文件转换为 MariaDB 兼容的格式。
✨ 功能特性
自动处理的兼容性问题
- ✅ 移除 GTID_PURGED 语句
- MySQL 的全局事务标识符,MariaDB 不支持
- ✅ 移除 SQL_LOG_BIN 设置
- MySQL 特有的二进制日志设置
- ✅ 转换排序规则(Collation)
utf8mb4_0900_ai_ci→utf8mb4_unicode_ciutf8mb4_0900_as_ci→utf8mb4_unicode_ciutf8mb4_0900_as_cs→utf8mb4_binutf8mb4_0900_bin→utf8mb4_bin- 其他 MySQL 8.0 特有的排序规则
- ✅ 移除不兼容的优化器提示
- MySQL 8.0 的
/*+ ... */优化器提示
- MySQL 8.0 的
- ✅ 处理 INVISIBLE 列
- MySQL 8.0 的隐藏列特性
- ✅ 处理版本特定的注释
- MySQL 8.0 特有的条件注释
📋 系统要求
- Python 3.6 或更高版本
- Windows / Linux / macOS
🚀 使用方法
基本用法
# 自动生成输出文件名(输入文件名 + _mariadb 后缀)
python mysql_to_mariadb_converter.py input.sql
# 指定输出文件名
python mysql_to_mariadb_converter.py input.sql output.sql
示例
# 转换单个数据库
python mysql_to_mariadb_converter.py database.sql
# 输出:database_mariadb.sql
# 转换并指定输出文件名
python mysql_to_mariadb_converter.py mysql_backup.sql mariadb_import.sql
📊 转换统计
脚本会自动显示详细的转换统计信息:
Conversion Statistics:
Total Lines: 195
Removed Lines: 5
Modified Lines: 39
Output Lines: 190
Detailed Changes:
- Removed GTID_PURGED: 1
- Removed SQL_LOG_BIN: 3
- Collation Changes: 39
- Other Fixes: 0
🔍 转换示例
原始 MySQL SQL(不兼容)
-- GTID 设置
SET @@GLOBAL.GTID_PURGED='8559a6bb-978c-11f0-be00-fa163e8e9a1c:1-18264955';
-- SQL_LOG_BIN 设置
SET @@SESSION.SQL_LOG_BIN= 0;
-- MySQL 8.0 排序规则
CREATE TABLE `users` (
`name` varchar(100) COLLATE utf8mb4_0900_ai_ci NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
转换后的 MariaDB SQL(兼容)
-- 已移除 GTID_PURGED 和 SQL_LOG_BIN 语句
-- 转换为 MariaDB 兼容的排序规则
CREATE TABLE `users` (
`name` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
📝 完整工作流程
步骤 1:从 MySQL 导出数据
# 方法 A:使用 mysqldump(推荐)
mysqldump -u root -p --databases your_database > mysql_dump.sql
# 方法 B:DBeaver 导出
# 右键数据库 → 工具 → 备份数据库 → 选择保存位置
步骤 2:转换 SQL 文件
python mysql_to_mariadb_converter.py mysql_dump.sql
步骤 3:导入到 MariaDB
方法 A:命令行导入
# 创建数据库(如果不存在)
mysql -h 122.9.39.110 -u appuser -p -e "CREATE DATABASE IF NOT EXISTS your_database CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
# 导入数据
mysql -h 122.9.39.110 -u appuser -p your_database < mysql_dump_mariadb.sql
方法 B:DBeaver 导入
- 连接到 MariaDB 服务器
- 右键数据库 → SQL 编辑器 → 打开 SQL 脚本
- 选择转换后的
_mariadb.sql文件 - 点击执行(▶️ 按钮)
⚠️ 注意事项
1. 备份原始文件
转换前请务必备份原始 SQL 文件:
cp original.sql original.sql.backup
2. 数据类型兼容性
大多数数据类型是兼容的,但注意:
| MySQL 8.0 | MariaDB | 说明 |
|---|---|---|
| JSON | JSON | MariaDB 10.2+ 支持,但实现略有不同 |
| INVISIBLE columns | ❌ 不支持 | 脚本会自动移除 |
| SPATIAL 数据类型 | ✅ 支持 | 通常兼容 |
3. 存储过程和函数
如果 SQL 包含存储过程或函数,可能需要手动检查:
- MySQL 8.0 特有的语法可能不兼容
- 建议导入后测试所有存储过程
4. 触发器和事件
触发器和事件通常兼容,但建议验证:
-- 查看触发器
SHOW TRIGGERS;
-- 查看事件
SHOW EVENTS;
🛠 高级用法
批量转换多个文件
Linux/Mac
#!/bin/bash
for file in *.sql; do
python mysql_to_mariadb_converter.py "$file"
done
Windows PowerShell
Get-ChildItem *.sql | ForEach-Object {
python mysql_to_mariadb_converter.py $_.Name
}
转换并直接导入
# 转换后立即导入
python mysql_to_mariadb_converter.py dump.sql && \
mysql -h 122.9.39.110 -u appuser -p your_database < dump_mariadb.sql
🐛 常见问题
问题 1:字符编码错误
错误:UnicodeDecodeError: 'utf-8' codec can't decode byte
解决:脚本已设置 errors='ignore',会自动跳过无效字符
问题 2:文件太大
问题:SQL 文件过大,内存不足
解决:拆分大文件
# Linux/Mac
split -l 10000 large_file.sql part_
# Windows - 使用 DBeaver 导出时选择"分卷"选项
问题 3:导入时仍然报错
可能原因:
- 存储过程包含不兼容的语法
- 使用了 MySQL 8.0 特有的函数
解决:
- 检查错误日志
- 手动修改不兼容的部分
- 逐表导入,定位问题表
📚 排序规则对照表
| MySQL 8.0 | MariaDB | 说明 |
|---|---|---|
| utf8mb4_0900_ai_ci | utf8mb4_unicode_ci | 不区分重音,不区分大小写 |
| utf8mb4_0900_as_ci | utf8mb4_unicode_ci | 区分重音,不区分大小写 |
| utf8mb4_0900_as_cs | utf8mb4_bin | 区分重音,区分大小写 |
| utf8mb4_0900_bin | utf8mb4_bin | 二进制比较 |
| utf8_0900_ai_ci | utf8_unicode_ci | UTF-8(3字节)版本 |
🔄 版本历史
v1.0.0 (2024-11-06)
- ✅ 初始版本
- ✅ 支持移除 GTID_PURGED
- ✅ 支持移除 SQL_LOG_BIN
- ✅ 支持排序规则转换
- ✅ 支持 INVISIBLE 列处理
- ✅ 详细的转换统计
- ✅ Windows 兼容性
💡 最佳实践
1. 完整的迁移流程
# 1. 导出 MySQL 数据
mysqldump -u root -p --single-transaction --routines --triggers --events \
--databases your_database > mysql_backup.sql
# 2. 转换为 MariaDB 格式
python mysql_to_mariadb_converter.py mysql_backup.sql
# 3. 在 MariaDB 上创建数据库
mysql -h mariadb_host -u appuser -p -e \
"CREATE DATABASE your_database CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
# 4. 导入数据
mysql -h mariadb_host -u appuser -p your_database < mysql_backup_mariadb.sql
# 5. 验证数据
mysql -h mariadb_host -u appuser -p your_database -e "SHOW TABLES;"
2. 数据验证
-- 比对表数量
SELECT COUNT(*) FROM information_schema.tables
WHERE table_schema = 'your_database';
-- 比对行数
SELECT table_name, table_rows
FROM information_schema.tables
WHERE table_schema = 'your_database';
-- 检查字符集
SHOW VARIABLES LIKE 'character%';
SHOW VARIABLES LIKE 'collation%';
3. 性能测试
导入后建议进行性能测试:
-- 重建索引统计信息
ANALYZE TABLE table_name;
-- 优化表
OPTIMIZE TABLE table_name;
📞 技术支持
如果遇到问题:
- 查看转换统计:检查哪些内容被修改
- 对比转换前后:使用
diff或文本编辑器对比 - 逐步导入:先导入表结构,再导入数据
- 查看日志:检查 MariaDB 错误日志
🎓 相关文档
📄 许可证
MIT License - 可自由使用和修改
完整脚本代码
将以下代码保存为 mysql_to_mariadb_converter.py,即可使用:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
MySQL 到 MariaDB SQL 文件转换工具
功能:
- 移除 GTID_PURGED 语句
- 移除 SQL_LOG_BIN 设置
- 转换 MySQL 8.0 特有的排序规则
- 移除不兼容的表空间引用
- 处理其他兼容性问题
使用方法:
python3 mysql_to_mariadb_converter.py input.sql [output.sql]
示例:
python3 mysql_to_mariadb_converter.py database.sql database_mariadb.sql
python3 mysql_to_mariadb_converter.py database.sql # 自动生成输出文件名
"""
import os
import sys
import re
from datetime import datetime
# 颜色输出
class Colors:
GREEN = '\033[0;32m'
YELLOW = '\033[1;33m'
RED = '\033[0;31m'
BLUE = '\033[0;34m'
CYAN = '\033[0;36m'
NC = '\033[0m' # No Color
BOLD = '\033[1m'
def print_success(message):
"""打印成功信息"""
print(f"{Colors.GREEN}[OK] {message}{Colors.NC}")
def print_error(message):
"""打印错误信息"""
print(f"{Colors.RED}[ERROR] {message}{Colors.NC}")
def print_warning(message):
"""打印警告信息"""
print(f"{Colors.YELLOW}[WARNING] {message}{Colors.NC}")
def print_info(message):
"""打印信息"""
print(f"{Colors.BLUE}[INFO] {message}{Colors.NC}")
def print_header():
"""打印脚本标题"""
print(f"\n{Colors.CYAN}{'='*70}{Colors.NC}")
print(f"{Colors.BOLD}{Colors.CYAN} MySQL to MariaDB SQL Converter{Colors.NC}")
print(f"{Colors.CYAN}{'='*70}{Colors.NC}\n")
class MySQLToMariaDBConverter:
"""MySQL 到 MariaDB 转换器"""
def __init__(self):
self.stats = {
'total_lines': 0,
'removed_lines': 0,
'modified_lines': 0,
'gtid_purged': 0,
'sql_log_bin': 0,
'collation_changes': 0,
'other_changes': 0
}
# MySQL 8.0 排序规则映射到 MariaDB 兼容的排序规则
self.collation_map = {
'utf8mb4_0900_ai_ci': 'utf8mb4_unicode_ci',
'utf8mb4_0900_as_ci': 'utf8mb4_unicode_ci',
'utf8mb4_0900_as_cs': 'utf8mb4_bin',
'utf8mb4_0900_bin': 'utf8mb4_bin',
'utf8_0900_ai_ci': 'utf8_unicode_ci',
'utf8_0900_as_ci': 'utf8_unicode_ci',
'utf8_0900_as_cs': 'utf8_bin',
}
def should_remove_line(self, line):
"""判断是否应该移除该行"""
line_stripped = line.strip()
# 移除 GTID_PURGED 设置
if 'GTID_PURGED' in line:
self.stats['gtid_purged'] += 1
self.stats['removed_lines'] += 1
return True
# 移除 SQL_LOG_BIN 设置
if 'SQL_LOG_BIN' in line and ('SET' in line or 'SESSION' in line):
self.stats['sql_log_bin'] += 1
self.stats['removed_lines'] += 1
return True
# 移除 MYSQLDUMP_TEMP_LOG_BIN
if 'MYSQLDUMP_TEMP_LOG_BIN' in line:
self.stats['removed_lines'] += 1
return True
# 移除 GTID state 注释块
if line_stripped.startswith('-- GTID state'):
self.stats['removed_lines'] += 1
return True
return False
def convert_collation(self, line):
"""转换 MySQL 8.0 特有的排序规则"""
original_line = line
# 替换排序规则
for mysql_collation, mariadb_collation in self.collation_map.items():
if mysql_collation in line:
line = line.replace(mysql_collation, mariadb_collation)
self.stats['collation_changes'] += 1
self.stats['modified_lines'] += 1
return line
def process_line(self, line):
"""处理单行 SQL"""
# 检查是否应该移除
if self.should_remove_line(line):
return None
# 转换排序规则
line = self.convert_collation(line)
# 处理其他兼容性问题
# 1. 移除 MySQL 8.0 特有的优化器提示
if '/*+ ' in line:
line = re.sub(r'/\*\+.*?\*/', '', line)
self.stats['other_changes'] += 1
self.stats['modified_lines'] += 1
# 2. 转换 INVISIBLE 列(MySQL 8.0 特性)
if ' INVISIBLE' in line and 'COLUMN' in line:
line = line.replace(' INVISIBLE', '')
self.stats['other_changes'] += 1
self.stats['modified_lines'] += 1
# 3. 处理不兼容的注释版本号
# MariaDB 可能不识别 MySQL 8.0 的版本号
line = re.sub(r'/\*!80\d{3}\s+(.*?)\s*\*/', r'\1', line)
return line
def convert_file(self, input_file, output_file):
"""转换 SQL 文件"""
try:
print_info(f"Reading file: {input_file}")
# 检查输入文件是否存在
if not os.path.exists(input_file):
print_error(f"File not found: {input_file}")
return False
# 读取文件
with open(input_file, 'r', encoding='utf-8', errors='ignore') as f:
lines = f.readlines()
self.stats['total_lines'] = len(lines)
print_success(f"Read {self.stats['total_lines']} lines successfully")
# 处理每一行
print_info("Converting...")
output_lines = []
for line in lines:
processed_line = self.process_line(line)
if processed_line is not None:
output_lines.append(processed_line)
# 在文件开头添加转换信息注释
header_comment = f"""-- ============================================================
-- MySQL to MariaDB Conversion
-- Converted: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
-- Original file: {os.path.basename(input_file)}
-- Total lines: {self.stats['total_lines']}
-- Removed lines: {self.stats['removed_lines']}
-- Modified lines: {self.stats['modified_lines']}
-- ============================================================
"""
output_lines.insert(0, header_comment)
# 写入输出文件
print_info(f"Writing file: {output_file}")
with open(output_file, 'w', encoding='utf-8') as f:
f.writelines(output_lines)
print_success(f"Conversion complete! Output: {output_file}")
return True
except Exception as e:
print_error(f"Conversion failed: {e}")
import traceback
traceback.print_exc()
return False
def print_stats(self):
"""打印转换统计信息"""
print(f"\n{Colors.CYAN}{'='*70}{Colors.NC}")
print(f"{Colors.BOLD}Conversion Statistics:{Colors.NC}\n")
print(f" Total Lines: {Colors.YELLOW}{self.stats['total_lines']:,}{Colors.NC}")
print(f" Removed Lines: {Colors.RED}{self.stats['removed_lines']:,}{Colors.NC}")
print(f" Modified Lines: {Colors.BLUE}{self.stats['modified_lines']:,}{Colors.NC}")
print(f" Output Lines: {Colors.GREEN}{self.stats['total_lines'] - self.stats['removed_lines']:,}{Colors.NC}")
print(f"\n{Colors.BOLD}Detailed Changes:{Colors.NC}\n")
print(f" - Removed GTID_PURGED: {self.stats['gtid_purged']}")
print(f" - Removed SQL_LOG_BIN: {self.stats['sql_log_bin']}")
print(f" - Collation Changes: {self.stats['collation_changes']}")
print(f" - Other Fixes: {self.stats['other_changes']}")
print(f"{Colors.CYAN}{'='*70}{Colors.NC}\n")
def main():
"""主函数"""
print_header()
# 解析命令行参数
if len(sys.argv) < 2:
print_error("Missing input file parameter!")
print(f"\n{Colors.BOLD}Usage:{Colors.NC}")
print(f" {Colors.CYAN}python3 {sys.argv[0]} <input.sql> [output.sql]{Colors.NC}")
print(f"\n{Colors.BOLD}Examples:{Colors.NC}")
print(f" {Colors.CYAN}python3 {sys.argv[0]} database.sql{Colors.NC}")
print(f" {Colors.CYAN}python3 {sys.argv[0]} database.sql database_mariadb.sql{Colors.NC}")
sys.exit(1)
input_file = sys.argv[1]
# 确定输出文件名
if len(sys.argv) > 2:
output_file = sys.argv[2]
else:
# 自动生成输出文件名
base_name = os.path.splitext(input_file)[0]
extension = os.path.splitext(input_file)[1]
output_file = f"{base_name}_mariadb{extension}"
print_info(f"Input file: {input_file}")
print_info(f"Output file: {output_file}")
# 检查输出文件是否存在
if os.path.exists(output_file):
print_warning(f"Output file exists: {output_file}")
response = input("Overwrite? (y/N): ")
if response.lower() != 'y':
print_info("Conversion cancelled")
sys.exit(0)
# 创建转换器并执行转换
converter = MySQLToMariaDBConverter()
if converter.convert_file(input_file, output_file):
converter.print_stats()
# 打印后续步骤
print(f"{Colors.BOLD}Next Steps:{Colors.NC}\n")
print(f"1. Verify the converted SQL file")
print(f"2. Import to MariaDB:")
print(f" {Colors.CYAN}mysql -h <host> -u <user> -p <database> < {output_file}{Colors.NC}")
print(f"3. Or use DBeaver GUI to import\n")
print_success("Conversion completed successfully!")
else:
print_error("Conversion failed!")
sys.exit(1)
if __name__ == "__main__":
main()
如何使用
-
复制上面的代码,保存为
mysql_to_mariadb_converter.py -
运行转换:
python mysql_to_mariadb_converter.py your_database.sql -
导入到 MariaDB:
mysql -h your_host -u your_user -p your_database < your_database_mariadb.sql
祝您迁移顺利! 🎉