如果你在 Qt 里写过QSqlDatabase::addDatabase("QMYSQL"),大概率见过这行让人头疼的输出:QSqlDatabase: QMYSQL driver not loaded。即便把官方 SDK 完整装了一遍,插件目录里也只有 qsqlite、qsqlodbc 那几个 dll,唯独没有 MySQL 驱动。这不是你漏装了组件,而是 Qt 官方分发版里刻意不带 MySQL 插件,具体原因下文会说清楚。这篇就围绕“从零编译 MySQL 驱动”的完整过程展开,覆盖 Windows 的 MinGW 路线、Linux 的常见做法,以及我踩过的坑和沉淀下来的排查清单。适合遇到编译报错、运行时驱动加载失败,或者准备把 Qt 应用部署到新机器上的朋友。
1. 为什么官方SDK里没有MySQL驱动
1.1 一个许可证引发的“标准问题”
Qt 使用的核心许可证是 LGPL,而 MySQL 客户端库以及连接协议本身绑定的是 GPL 体系。LGPL 允许动态链接闭源程序,但 GPL 对“派生作品”有严格约束。Qt 官方很清楚:如果直接把 MySQL 驱动编译好打包进安装包,会让整个发行版卷入两种许可证条款的拉扯,用户的合规成本也会明显上升。
所以你可以看到 Qt 源码仓库里一直保留着QSqlDriverPlugin和QMYSQL的实现,但官方安装器不给你产出物。这套策略其实很有意思:他们不是不让你用 MySQL,而是把“编译驱动”这一步留给你自己完成,许可证边界也就自然划清了。
刚开始我也觉得这设计很“反人类”,但后来想明白了:MySQL 驱动作为一个独立的插件模块,谁编译谁负责。个人开发者在本地编译没有任何问题,商业软件发布时只要注意客户端库的许可证,同样可以合法使用。理解了这个背景,遇到任何编译错误都不会太慌,因为编译这个行为本身就是 Qt 生态下的日常流程。
1.2 要不要自己编?替代方案对比
很多人的第一反应是上网找一个编译好的 dll。确实能搜到不少资源,但我建议在动手前先横向对比一下:
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 自己编译 | 版本完全可控,与 Qt 编译器匹配,干净无污染 | 需要搭环境、花时间去踩坑 | 正规项目、长期维护 |
| 第三方预编译 dll | 快,下载即用 | 编译器版本、Qt 版本、MySQL 客户库版本三者很难全部匹配,还可能染毒 | 临时验证一下能不能连上 |
| ODBC 驱动 | Qt 自带,不用编译 | 性能一般,多一层配置,服务器端还得支持 ODBC | 内网工具类软件可以凑合 |
| 直接用 MySQL 官方 C API 封装 | 不受 Qt 插件机制制约 | 丢失 QSqlDatabase 的统一抽象,代码量明显变大 | 你本身不依赖 Qt SQL 层 |
我的建议很简单:只要你的项目还要继续演进,就老老实实自己编一次。我第一次编译花了大半个下午,第二次五分钟就完事。这种一次性成本很值得投入。
2. 环境准备与版本匹配原则
2.1 一次把版本矩阵说清楚
在动手前,先花两分钟确认版本匹配,这能避免 80% 的莫名其妙错误。
Qt 的 SQL 驱动是一个编译好的动态库插件,它加载进 Qt 后,运行时的一切符号都跟 Qt 本体绑定。所以核心原则是:驱动使用的编译器必须和 Qt 本体保持一致。MSVC 编译出来的 Qt,绝不能用 MinGW 编译的插件去顶替,反之亦然。位数也必须对齐,64 位 Qt 配 64 位驱动,32 位 Qt 配 32 位驱动。
常见组合大概是这样的:
| Qt 版本 | 编译器套件 | MySQL 客户端 | 编译方式 |
|---|---|---|---|
| Qt 5.15.2 | MinGW 8.1 64-bit | MySQL Server 8.0 或 Connector/C 8.0 | qmake + mingw32-make |
| Qt 5.15.2 | MSVC2019 64-bit | MySQL Server 8.0 或 Connector/C 8.0 | qmake + nmake |
| Qt 6.5 / 6.8 | MinGW 11.2 64-bit | MySQL Server 8.0 | CMake + ninja |
| Qt 6.5 / 6.8 | MSVC2022 64-bit | MySQL Server 8.0 | CMake + ninja |
还有一个隐藏坑:MySQL 客户端库的位数也必须一致。如果你下载的是 x86 版本 Connector/C,却在 64 位插件里链接,链接阶段可能不报错,但运行时一定崩溃或者找不到入口点。
2.2 你得先找到 Qt 源码
编译 MySQL 驱动需要 Qt 源码树中的qtbase/src/plugins/sqldrivers目录。很多人在这一步卡住,因为他们安装 Qt 时根本没有勾选 Sources 组件。
打开 Qt Maintenance Tool,选择“添加或移除组件”,展开你自己安装的 Qt 版本号,下面有一个子项叫 Sources,勾选后更新组件。下载完成后,目录通常是这样的:
C:\Qt\5.15.2\Src\qtbase\src\plugins\sqldrivers如果你用的是 Qt 6 系列,目录结构类似,也是在qtbase/src/plugins/sqldrivers。
还有一种方式是从官方仓库拉取对应分支的 qtbase 和 qtsql 源码,版本 tag 必须和你的 Qt 版本一致。这个方法适合老版本或者无法通过安装器补源码的朋友。我不建议在编译完整 Qt 源码这件事上花时间,真没必要,你只需要 sqldrivers 这一小块。
3. Windows下用MinGW编译MySQL驱动(主流程)
3.1 准备MySQL头文件与库文件
编译驱动本质上就是:让编译器找到mysql.h,让链接器找到libmysql.lib,Qt 里对应的是MYSQL_INCDIR和MYSQL_LIBDIR两个变量。
我推荐直接下载 MySQL Connector/C,而不是用 MySQL Server 安装目录里的头文件。原因很简单:Connector/C 体积小、目录结构清晰,而且版本独立管理,方便后续换版本重新编译。
下载时注意选择与你的 Qt 位数一致的版本。解压后,典型的目录结构是:
D:\MySQL\mysql-connector-c-6.1.11-winx64\include D:\MySQL\mysql-connector-c-6.1.11-winx64\lib这两个目录就是你要用的。include里能看到mysql.h、mysql_com.h等文件,lib目录里会有libmysql.lib和libmysql.dll。
如果你手头只有 MySQL Server 安装目录,也可以。路径一般是:
C:\Program Files\MySQL\MySQL Server 8.0\include C:\Program Files\MySQL\MySQL Server 8.0\lib注意路径里的空格,后面在 qmake 命令行中必须用引号包住。
3.2 使用qmake、make、make install走完三步
这部分我以 Qt 5.15.2 + MinGW 8.1 + MySQL Server 8.0 为例,完整走一遍。
第一步,打开“Qt 5.15.2 (MinGW 8.1 64-bit)”这个命令行环境。这个入口在开始菜单里有过,它已经帮你把编译器 bin 目录和 qmake 加进 PATH 了。如果你自己用普通 cmd,那还得手动设置一堆环境变量,容易出错。
第二步,进入源码目录:
cd /d C:\Qt\5.15.2\Src\qtbase\src\plugins\sqldrivers先检查一下这个目录下是否有qsqldriverbase.pri和mysql子目录。如果mysql目录不存在,说明你的源码包不完整,重装 Sources 组件。
第三步,我习惯直接通过命令行传参数,这样不修改源码文件,方便以后保留参数记录:
qmake -- MYSQL_INCDIR="C:/Program Files/MySQL/MySQL Server 8.0/include" MYSQL_LIBDIR="C:/Program Files/MySQL/MySQL Server 8.0/lib"注意,路径里的分隔符建议统一用正斜杠/,Windows 下反斜杠在 qmake 环境里偶尔会被转义出问题,正斜杠虽然看起来不“Windows”,但兼容性反而更好。
第四步,编译:
mingw32-make如果你用的是 MSVC 版本的 Qt,对应命令是nmake。MinGW 环境里其实make也指向了 mingw32-make,但为了明确,我还是写了完整名字。
编译过程大概一到两分钟,最后会生成一个目录plugins\sqldrivers。里面可以看到:
qsqlmysql.dll qsqlmysqld.dll带d的是 debug 版本,不带的是 release 版本。如果你的 Qt 是 release 版且在 release 模式下编译,那直接得到qsqlmysql.dll就够用。
第五步,安装。qmake 生成的 Makefile 自带 install 目标:
mingw32-make install它会自动把 dll 拷贝到 Qt 安装目录的plugins\sqldrivers下。如果你没有写权限,或者不想污染原版 Qt,可以先指定INSTALL_ROOT:
mingw32-make install INSTALL_ROOT=D:\temp\qt-sql-plugins然后把最后生成的qsqlmysql.dll手动复制到自己的项目目录或 Qt 插件目录。这一步比直接覆盖安装更可控。
3.3 部署时最容易漏掉的DLL
驱动编译成功了,程序还是有可能报driver not loaded,这就要说到 Windows 下的动态库依赖问题。
qsqlmysql.dll本身并不包含 MySQL 协议实现,它链接了libmysql.dll。编译完成后,libmysql.dll保存在你本机,但在目标机器上不一定存在。所以部署 Qt 程序时,记得把这个 dll 一并带上,放在 exe 同目录即可。
还有一个细节:如果你把程序拷贝到别的机器测试,最方便的办法是先用依赖查看工具打开qsqlmysql.dll看一眼,确认它依赖哪些运行库。常见依赖工具是 Dependencies 或者老牌的 Dependency Walker。看完你会明白,有些机器上提示“找不到 libmysql.dll”,完全不关 Qt 的事,纯粹是部署不全。
3.4 Qt 6.x版的CMake编译变化
Qt 6 开始,构建系统从 qmake 全面转向 CMake,MySQL 驱动的编译方式也跟着变了。源码位置还是在qtbase/src/plugins/sqldrivers,但你不再直接运行qmake,而是走 CMake 流程。
大致过程是先单独配置 qtbase 源码树:
cmake -S C:\Qt\6.5.0\Src\qtbase -B C:\Qt\6.5.0\build-qtbase -DCMAKE_PREFIX_PATH=C:\Qt\6.5.0\mingw_64 -DMySQL_INCLUDE_DIR=D:\MySQL\include -DMySQL_LIBRARY=D:\MySQL\lib\libmysql.lib -DCMAKE_BUILD_TYPE=Release配置成功后,只需要构建目标:
cmake --build C:\Qt\6.5.0\build-qtbase --target qsqlmysql最终的 dll 会生成在plugins/sqldrivers下,手动复制到 Qt 的插件目录即可。Qt 6 的整体流程变化不大,核心思路依然是“指定 MySQL 头文件目录和库目录”,只是换了个语法。
4. Linux环境下编译MySQL驱动(换个思路完成部署)
4.1 先装好MySQL客户端开发包
Linux 下没有 Windows 那么多“可视化安装器”,一切靠包管理器。
以 Debian/Ubuntu 系列为例:
sudo apt install libmysqlclient-dev如果你的发行版默认用 MariaDB 兼容库,那么:
sudo apt install libmariadb-dev这两个包都自带mysql.h和链接库,编译出来的驱动在功能上没差别。安装完成后可以用mysql_config --include和mysql_config --libs确认路径,比如:
mysql_config --include # -I/usr/include/mysql mysql_config --libs # -L/usr/lib/x86_64-linux-gnu -lmysqlclient4.2 在qtbase源码目录里直接编译
我推荐的方式还是走 Qt 自身的插件编译流程。首先确认你的 Qt 安装在哪个目录,比如:
~/Qt/5.15.2/gcc_64使用这个 Qt 对应的 qmake:
cd ~/Qt/5.15.2/Src/qtbase/src/plugins/sqldrivers ~/Qt/5.15.2/gcc_64/bin/qmake -- MYSQL_INCDIR=/usr/include/mysql MYSQL_LIBDIR=/usr/lib/x86_64-linux-gnu make make install注意,这里千万不要用/usr/bin/qmake,那是系统自带的 Qt5 或 Qt6,不一定匹配你的目标 Qt。我见过一位同事就在这里折腾了很久,编译的是系统 Qt,运行用的却是自装 Qt,插件放进去当然加载不了。
编译完成后,最终产物是libqsqlmysql.so。make install通常会直接把它放到 Qt 安装目录的plugins/sqldrivers下。如果因为权限原因无法写入,自己复制过去也一样:
cp libqsqlmysql.so ~/Qt/5.15.2/gcc_64/plugins/sqldrivers/4.3 检查加载日志的实用技巧
Linux 下驱动加载失败,不像 Windows 那样有明显的“找不到 dll”弹窗,可能只是程序日志里给一句QSqlDatabase: QMYSQL driver not loaded。
这时可以借助 Qt 自己的调试开关,运行程序前设置:
export QT_DEBUG_PLUGINS=1然后启动程序,控制台会打印 QFactoryLoader 相关的插件搜索日志,你能看到 Qt 实际搜索了哪些目录、尝试加载哪些文件。这比瞎猜效率高得多。
我之前遇到过一个问题:编译出来的驱动被放到自定义插件目录后,程序始终找不到。通过日志才发现 Qt 默认的插件目录是QTDIR/plugins,自定义目录需要调用QCoreApplication::addLibraryPath()或设置QT_PLUGIN_PATH环境变量。这类问题在 Windows 上同样存在,但在 Linux 上更容易被忽视。
5. 常见问题与排查技巧实录
5.1 一眼识别“Driver not loaded”的真正原因
这个问题基本成了 Qt 社区里的高频通病。原因就那几类,但表现完全一样。我把它整理成三个检查顺序:
先看plugins/sqldrivers目录下有没有驱动文件。没有,那就是没编译成功或没复制过去。
再看驱动文件位数和编译器。64 位 Qt 配 32 位驱动,加载时通常直接失败,Windows 下还可能弹%1 不是有效的 Win32 应用程序。
最后看依赖库。Windows 下缺libmysql.dll,Linux 下缺libmysqlclient.so,即使驱动文件和 Qt 完全匹配,动态库加载阶段还是会挂掉。
排查思路其实很简单:把“驱动加载”想象成三层嵌套——Qt 找到插件文件、系统加载 DLL/SO、插件初始化时链接 mysql 客户端库。哪一层断了,报错都会落到driver not loaded。
5.2 编译期报错速查表
编译阶段最常见的错误我整理成了表格,基本覆盖我见过的所有情况:
| 报错内容 | 可能原因 | 解决办法 |
|---|---|---|
mysql.h: No such file or directory | MYSQL_INCDIR 指错了 | 检查路径是否真实存在,一定包含 mysql.h |
Cannot open include file: 'qsql_mysql.h' | qtbase 源码残缺 | 重装 Sources 组件,确保 sqldrivers/mysql 目录完整 |
cannot find -lmysqlclient | MYSQL_LIBDIR 不对,或库未安装 | Linux 下确认已装 libmysqlclient-dev |
LNK1181: cannot open input file 'libmysql.lib' | Windows 下 lib 路径不对 | 检查 lib 目录,区分 libmysql.lib 与 libmysql.dll |
cannot find -lqsqlmysql | 编译顺序出错 | 确认在 sqldrivers 根目录执行 qmake,而不是 mysql 子目录 |
QMAKE_MOC / QMAKE_QMAKE 未定义 | 用了错误版本的 qmake | 使用 Qt 安装目录下的 qmake |
编译 MySQL 驱动的最关键动作是:一定要在sqldrivers这个根目录执行 qmake,而不是进入mysql子目录单独编。目录里的 qsqldriverbase.pri 会把多个驱动的构建逻辑串起来,你单独进入子目录会遇到一堆相互依赖的抽象变量,反而更难排查。
5.3 MySQL 8.0的认证与编码坑
驱动编译成功、加载成功,也不代表连接就一帆风顺。MySQL 8.0 相比 5.7 的最大变化是默认身份认证插件换成了caching_sha2_password,而这可能导致较旧版本的客户端库报认证失败,错误信息类似:
Authentication plugin 'caching_sha2_password' cannot be loaded:这个时候 Qt 驱动本身没有锅,锅在你链接的 MySQL 客户端库版本太旧。解决办法有两个:
一是换用较新版本的 MySQL Connector/C 重新编译驱动,让客户端库支持新认证插件。
二是如果你暂时不能更换依赖库,只能在数据库层把用户认证插件改回mysql_native_password,例如:
ALTER USER 'user'@'host' IDENTIFIED WITH mysql_native_password BY 'password';这个方法在内部工具里可行,但生产环境不建议激进修改认证策略。更合理的做法是升级客户端库。另外,连接信息里的字符集也要留意,我习惯在连接后立即执行:
SET NAMES utf8mb4否则中文数据可能出现乱码,这跟驱动无关,纯粹是连接字符集没有统一。
5.4 万能排查清单:从日志到代码验证
遇到加载失败,我会用一套固定的验证流程,你照着做基本能定位问题:
- 设置
QT_DEBUG_PLUGINS=1运行程序,看日志里是否出现加载驱动路径。 - 确认驱动文件名是否带
d后缀。debug 版程序需要qsqlmysqld.dll,release 版需要qsqlmysql.dll,两者不能互换。 - 用依赖分析工具看驱动文件的动态依赖,确认 mysql 客户端库存在且位数匹配。
- 写一个最小连接程序,排除业务代码干扰:
#include <QCoreApplication> #include <QSqlDatabase> #include <QSqlQuery> #include <QDebug> int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); QSqlDatabase db = QSqlDatabase::addDatabase("QMYSQL"); db.setHostName("127.0.0.1"); db.setPort(3306); db.setDatabaseName("test"); db.setUserName("root"); db.setPassword("your_password"); if (!db.open()) { qWarning() << db.lastError().text(); return 1; } QSqlQuery query(db); query.exec("SELECT VERSION()"); while (query.next()) { qDebug() << "MySQL version:" << query.value(0).toString(); } return 0; }在 .pro 文件里加:
QT += sql编译运行。能打出 MySQL 版本号,说明驱动和连接都通了,问题在业务代码;打不出,就按上面三步逐层排查。这套方法我用了很多年,比翻文档管用。
最后分享一个我个人的习惯:每台机器上编译驱动时,我会把 qmake 参数和路径记录到项目根目录的一个文本文件里。下次换电脑、升级 Qt、或者其他同事要在他自己机器上编译,直接按记录跑一次就好,不用重新研究。以后你还会遇到证书、加密、SSL 连接之类的问题,但只要你掌握了“驱动编译”这个底层链路,再偏门的问题也能迎刃而解。如果你在编译过程中遇到了我上面没列到的问题,可以先看看是不是版本匹配、路径配置、依赖库这三板斧里的某一把没挥好。