资讯动态

Drogon MySQL兼容补丁:解决libmariadb-dev与libmysqlclient-dev冲突

发布时间:2026/10/19 12:40:52 来源:尧图企业网站定制
做 C 后端的人最怕的不是协程写不好而是编译环境里躺着两个“看起来差不多”的开发库。Drogon 项目接 MySQL 的时候我就在 libmariadb-dev 和 libmysqlclient-dev 的冲突上整整折腾了两个晚上。今天把这份 Drogon MySQL 兼容补丁整理出来给所有被类似构建问题卡住的人一个可以直接照抄的答案这两个开发库到底为什么会打架、怎么诊断、补丁怎么写、构建怎么跑。内容不长但每一步都是实打实测过的思路适合刚接触 Drogon 的人也适合在 CI 里被依赖关系折磨的工程老手。1. 冲突到底冲突在哪1.1 两个开发库的“血缘关系”先说清楚这俩库是什么关系。libmysqlclient-dev 是传统 MySQL 客户端的开发包由 Oracle 维护的 MySQL 分支或与其协议兼容的实现演化而来libmariadb-dev 则是 MariaDB 官方提供的客户端开发库。MariaDB 当年从 MySQL 分叉出来API 层面保持了高度兼容连最常见的几个 C API 函数名都完全一致。问题就出在这个“高度兼容”上。两个开发包都向外提供mysql.h、mysql_version.h这些同名头文件也都提供mysql_init、mysql_real_query、mysql_store_result等同名符号。表面上是同一套接口底层实现却各自独立。在 Debian 系发行版里libmariadb-dev和libmysqlclient-dev通常会被标记为互相冲突原因是它们会向系统写入相同路径的文件。一旦因为某些间接依赖导致两个包都被塞进环境里编译期就会陷入“头文件来自一个库二进制库来自另一个库”的混乱局面。下面这个表可以快速帮你理清两者的差异维度libmysqlclient-devlibmariadb-dev常见配置命令mysql_configmariadb_config典型头文件mysql.h, mysql_version.hmysql.h, mariadb_version.h典型动态库libmysqlclient.solibmariadb.so包冲突关系与 libmariadb-dev 冲突与 libmysqlclient-dev 冲突API 风格MySQL C API兼容 MySQL C API不同小版本发行版的头文件路径可能略有差别但核心矛盾是一致的这两个包都想“独占”MySQL C API 资源。1.2 冲突在 Drogon 构建中的具体表现Drogon 是一个基于 C 的异步 Web 框架它内置了数据库后端支持。开启 MySQL 后端时Drogon 的 CMake 构建逻辑会去系统里找一个mysql.h和一套客户端库。正常情况下这没什么问题但一旦环境里同时存在 MariaDB 和 MySQL 两套开发文件Drogon 的查找路径就可能“串线”。最常见的是下面这类编译错误In file included from /usr/include/mysql/mysql.h:70, from /path/to/drogon/orm_lib/src/mysql_impl.cpp:31: /usr/include/mariadb/mysql.h:40:9: error: redefinition of ‘typedef struct st_mysql MYSQL’这种“redefinition of typedef”的报错基本就是头文件被拆成了两半某个模块先引入了 MySQL 的头文件另一个模块又通过不同路径引入了 MariaDB 的头文件。两边都定义了相同结构体编译器直接罢工。更隐蔽的是链接期错误。比如 CMake 找到了 MariaDB 的头文件却在链接阶段找到了libmysqlclient.so。由于两个库的符号版本、内部实现细节、依赖的辅助库不完全一致链接器可能报出“undefined reference to mysql_init”这类诡异问题或者干脆链接成功但程序运行后内存布局对不上出现偶发崩溃。这类问题比编译错误更烦人因为它不是每次都必现。1.3 影响范围远不止 Drogon不要觉得这是 Drogon 独有的毛病。所有使用 MySQL C API 的 C/C 库都可能踩雷包括一些 ORM、连接池甚至手写数据库客户端的项目。但 Drogon 的特殊之处在于它的构建系统会同时拉入头文件、动态库以及额外的线程和 SSL 依赖一旦某个环节被错误库污染后续执行cmake --build时就会反复横跳。所以我把解决方案封装成了一个独立补丁模块目的是让 Drogon 在遇到这种双开发库环境时不再靠 CMake 的“好运”而是明确地告诉构建系统你到底应该用哪一套头文件和库。这套逻辑可以单独放进 Drogon 源码树也可以放进你自己的工程里通过CMAKE_MODULE_PATH注入后面会详细说。2. 动手前先摸清底细2.1 看当前到底装了什么不要急着写补丁先搞清楚机器上实际安装了哪些包。Debian/Ubuntu 系可以用下面的命令快速过滤dpkg -l | grep -E libmysqlclient|libmariadb|default-libmysqlclient|mariadb-client-core如果看到libmysqlclient-dev和libmariadb-dev同时存在就说明环境已经处在冲突状态。有些发行版还会安装default-libmysqlclient-dev它可能是 MySQL 或 MariaDB 的一个过渡包同样需要留意。另外dpkg -l只能看到安装状态不能完整展示文件归属。要确认头文件到底归哪个包管可以继续执行dpkg -S /usr/include/mysql/mysql.h输出会告诉你当前/usr/include/mysql/mysql.h属于libmariadb-dev还是libmysqlclient-dev。如果这条命令没有输出说明头文件不在系统标准路径或者这个路径被手动覆盖过。2.2 看配置命令指向哪里MySQL 和 MariaDB 各自提供了配置命令mysql_config和mariadb_config。它们可以输出该开发库的 include 路径、库路径、编译选项和版本信息是我们将要使用的关键工具。which mysql_config mariadb_config mysql_config --version mysql_config --include mysql_config --libs mariadb_config --version mariadb_config --cc mariadb_config --include mariadb_config --libs注意观察输出。如果mysql_config --include指向/usr/include/mariadb而mysql_config --libs输出的是-lmariadb说明mysql_config这个命令实际上是由 MariaDB 提供的包装脚本系统里所有看似“MySQL”的配置其实都偏向 MariaDB。反之亦然。这里有个经验永远不要凭命令名字猜库来源。一定要看dpkg -S $(which mysql_config)的输出或者看mysql_config --version的返回值。2.3 看 CMake 缓存里残留了什么Drogon 的 CMake 构建过程会把搜到的路径和结果写进build/CMakeCache.txt。如果之前配置过 MySQL 后端后来又换了库缓存里可能残留旧路径。grep -iE MYSQL|MARIADB build/CMakeCache.txt重点看MYSQL_INCLUDE_DIR、MYSQL_LIBRARY、MySQL_*这类变量。如果它们的来源互相矛盾比如 include 路径是/usr/include/mysql但 library 路径里带着libmariadb.so那就太明显了。不要直接在CMakeCache.txt里手动改路径因为 CMake 重新运行时会用新的探测结果覆盖手动改反而容易制造新旧混合状态。正确做法是删除build目录后重新配置或者用cmake -U MYSQL_*清除相关缓存变量。3. 补丁的设计与原理3.1 设计思路不硬刚只统一面对两个开发库的冲突有些人会尝试通过修改 Drogon 源码里的#include mysql.h为#include mariadb/mysql.h来规避问题。我不推荐这个路径。一是 Drogon 是第三方框架直接改源码会让后续升级变得痛苦二是某些头文件的内部依赖链很复杂强行替换 include 名称会把 MySQL 和 MariaDB 的宏混在一起反而制造更多不确定性。我的补丁思路很简单不让 CMake 靠“运气”去搜头文件和库而是强制它通过同一个配置命令同时拿到 include 路径和链接库。这样做的好处是无论你用 MariaDB 还是 MySQL得到的头文件和库一定来自同一个客户端库不会出现“头文件在左、库在右”的分裂局面。如果一定要在物理机上同时保留两个开发包给其他项目用那就别让 Drogon 直接去系统路径里探索。在容器或独立安装前缀里只暴露需要的那个包再通过配置命令指向它。这样既解决了 Drogon 的构建也不影响其他项目。3.2 核心模块DrogonMysqlCompat.cmake下面这个 CMake 模块是整个兼容补丁的核心。它先找mariadb_config或mysql_config然后无条件使用该命令的--include和--libs输出不靠find_path和find_library的猜测逻辑。# cmake/DrogonMysqlCompat.cmake # 处理 libmariadb-dev 与 libmysqlclient-dev 冲突的兼容模块。 # 用法 # include(DrogonMysqlCompat) # drogon_setup_mysql(DROGON_MYSQL_INCLUDE_DIRS DROGON_MYSQL_LIBRARIES) function(drogon_setup_mysql OUT_INCLUDE_DIRS OUT_LIBRARIES) # 优先使用 mariadb_config其次使用 mysql_config。 # 不要同时把两个配置命令都塞进搜索列表 # 务必保证 include 路径和链接库来自同一个客户端库。 find_program(MYSQL_CONFIG_EXECUTABLE NAMES mariadb_config mysql_config DOC Path to mariadb_config or mysql_config ) if(NOT MYSQL_CONFIG_EXECUTABLE) message(FATAL_ERROR 找不到 mariadb_config/mysql_config请先安装 libmariadb-dev 或 libmysqlclient-dev) endif() message(STATUS Drogon MySQL compat: using ${MYSQL_CONFIG_EXECUTABLE}) # 分别取出 include 参数和链接参数 execute_process( COMMAND ${MYSQL_CONFIG_EXECUTABLE} --include OUTPUT_VARIABLE MYSQL_INCLUDE_ARGS OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND_ERROR_IS_FATAL ANY ) execute_process( COMMAND ${MYSQL_CONFIG_EXECUTABLE} --libs OUTPUT_VARIABLE MYSQL_LIB_ARGS OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND_ERROR_IS_FATAL ANY ) # 把字符串按空格拆成 CMake 列表方便后面赋给 target separate_arguments(MYSQL_INCLUDE_ARGS NATIVE_COMMAND ${MYSQL_INCLUDE_ARGS}) separate_arguments(MYSQL_LIB_ARGS NATIVE_COMMAND ${MYSQL_LIB_ARGS}) # 回传给调用方 set(${OUT_INCLUDE_DIRS} ${MYSQL_INCLUDE_ARGS} PARENT_SCOPE) set(${OUT_LIBRARIES} ${MYSQL_LIB_ARGS} PARENT_SCOPE) message(STATUS Drogon MySQL include: ${MYSQL_INCLUDE_ARGS}) message(STATUS Drogon MySQL libs: ${MYSQL_LIB_ARGS}) endfunction()这个函数的关键点是把两个execute_process放在同一个配置命令后面。因为mariadb_config --include和mariadb_config --libs必然来自同一套库所以不会出现跨库混用的问题。如果两个 dev 包共存但路径被某些环境变量干扰了只要你最终选择的命令是对的头文件和库就不会打架。3.3 如何让 Drogon 接上这套逻辑有了模块之后还要让 Drogon 的 CMake 在构建 MySQL 后端时调用它。假设你的 Drogon 源码在/path/to/drogon补丁的核心 diff 大致如下--- a/CMakeLists.txt b/CMakeLists.txt if(BUILD_MYSQL) - find_package(MySQL REQUIRED) list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) include(DrogonMysqlCompat) drogon_setup_mysql(DROGON_MYSQL_INCLUDE_DIRS DROGON_MYSQL_LIBRARIES) target_include_directories(drogon PUBLIC ${DROGON_MYSQL_INCLUDE_DIRS}) target_link_libraries(drogon PUBLIC ${DROGON_MYSQL_LIBRARIES}) endif()不同版本的 Drogon 对 target 名称和 CMake 结构略有差异但思路不变不要再用原来那个模糊的find_package(MySQL)改为显式调用我们的兼容函数。如果你的环境里既有 MariaDB 也有 MySQL还可以通过给 CMake 传参锁定执行路径例如cmake -S . -B build -DBUILD_MYSQLON -DMYSQL_CONFIG_EXECUTABLE/usr/bin/mariadb_configfind_program在有外部变量传入时会优先使用外部指定的路径不会再去 PATH 里找第一个。这个特性非常适合 CI 环境统一固定库来源。3.4 为什么这样有效传统find_path加find_library的方式本质上是分两条独立路径去搜索。如果系统中出现两套同 API 的开发库搜索顺序稍有变化结果就会不一致。补丁改成“配置命令驱动”之后实际上把两个查询合并成了一个原子操作你要么全用 MariaDB要么全用 MySQL不存在中间态。对于 Drogon 这种高度依赖异步 IO 的框架来说头文件版本和库版本不一致不仅影响编译更影响运行期内存布局。比如MYSQL结构体大小不同mysql_store_result返回的字段结构在客户端库内部被分配但你用的是头文件里声明的旧结构体程序可能拿到错误数据。这种问题不像编译错误那么直白排查起来非常痛苦。所以花半小时把构建逻辑做干净比省这几分钟更划算。4. 实操步骤完整跑一遍兼容构建4.1 先清理可能“打架”的环境如果你现在正处于冲突状态不要急着写补丁。先决定你要用哪套库。这一步选错后面所有步骤都白做。我通常建议在干净容器里操作。如果必须在物理机上处理先卸载掉不想用的那个开发包sudo apt purge libmysqlclient-dev libmariadb-dev sudo apt autoremove然后只安装你需要的那个# 如果项目偏向 MariaDB sudo apt install libmariadb-dev # 或者如果偏向 MySQL sudo apt install libmysqlclient-dev有的发行版里安装libmariadb-dev时需要同步安装libmariadb-dev-compat因为它提供了某些应用需要的兼容 symlink。按你的发行版提示操作即可。这里还有一个备选方案如果你两个包都不舍得卸载那就用 Docker 把构建环境隔离起来。容器里只装其中一个 dev 包物理机上的混乱不会影响你。4.2 准备 Drogon 构建依赖Drogon 构建依赖不止 MySQL。你需要一个能编译 C17 的编译器、CMake、OpenSSL 开发库、jsoncpp 开发库等。在 Debian/Ubuntu 上大致是sudo apt install build-essential cmake libssl-dev libjsoncpp-dev zlib1g-dev uuid-dev然后把 Drogon 仓库克隆下来进入源码根目录git clone drogon-repo-url drogon cd drogon4.3 放置兼容模块在源码根目录下创建cmake目录把DrogonMysqlCompat.cmake放进去。mkdir -p cmake然后把 3.2 节的完整 CMake 函数存成cmake/DrogonMysqlCompat.cmake。建议用cat写入避免编辑器自动加空格或转义字符。4.4 修改 CMakeLists.txt 并应用补丁找到CMakeLists.txt里关于 MySQL 后端的段落。不同版本写法不同但通常会看到类似if(BUILD_MYSQL)的结构。把原来的find_package(MySQL REQUIRED)替换成对兼容模块的调用具体 diff 可以参考 3.3 节。如果你不想改动 Drogon 的源码也可以在自己的工程里覆盖CMAKE_MODULE_PATH然后在cmake目录下提供一个同样名为DrogonMysqlCompat.cmake的文件。但要注意Drogon 自身的CMakeLists.txt仍然需要被修改因为你得让它调用这个函数而不是原来的查找逻辑。应用完成后建议用git diff看一遍改动确认没有误改到其他 CMake 配置。4.5 配置并编译回到 Drogon 源码根目录开始配置构建。重点是把 MySQL 后端打开cmake -S . -B build -DBUILD_MYSQLON -DCMAKE_BUILD_TYPERelease如果兼容模块被正确启用你会看到类似这样的输出-- Drogon MySQL compat: using /usr/bin/mariadb_config -- Drogon MySQL include: -I/usr/include/mariadb -- Drogon MySQL libs: -L/usr/lib/x86_64-linux-gnu -lmariadb -lpthread -ldl -lz看到include的前缀和libs中的库名一致就可以确认没有跨库混用。然后执行编译cmake --build build -j$(nproc)如果编译中途报错不要慌回到第 4.1 步重新确认包环境再清掉build目录重来。大部分问题都出在缓存污染而不是代码本身。4.6 编写一个最小验证程序编译通过后最好写一个最小程序验证 Drogon 的数据库连接池真的能连上 MySQL 或 MariaDB。#include drogon/drogon.h #include iostream int main() { drogon::app().addListener(127.0.0.1, 8080); drogon::app().loadConfigFile(./config.json); auto db drogon::app().getDbClient(); auto result db-execSqlSync(select 1 as ok); if (!result.empty()) { std::cout DB connect OK, row count result.size() std::endl; } return 0; }为了跑通你还要在config.json里配置数据库地址、用户名和密码。不过这一步只是验证运行时的库是否一致测试通过就能说明补丁生效了。5. 常见问题与避坑清单5.1 问题速查表下面这个表是我个人在实际操作中整理出来的高频问题按优先级排列现象可能原因排查命令解决办法编译报 redefinition of typedef多个同名头文件被先后引入dpkg -S /usr/include/mysql/mysql.h只保留一套 dev 包或让 include 路径统一链接阶段 undefined reference to mysql_init头文件与库版本不匹配mysql_config --version对比实际库版本用兼容模块强制使用同一个配置命令运行时提示找不到 libmariadb.so 或 libmysqlclient.so动态库不在默认搜索路径ldd ./build/exegrep -E mysqlcmake重新配置后变量没变CMakeCache 残留旧值grep -iE MYSQLMARIADB build/CMakeCache.txt同一个项目在 CI 与本地结果不同两个环境的 mysql_config 指向不同打印mysql_config --version在 CI 中固定MYSQL_CONFIG_EXECUTABLE变量5.2 实操经验与避坑建议第一不要在同一个build目录里反复切换 MySQL 后端和 MariaDB 后端。CMake 缓存对这套东西的“记忆”比你想象中顽固切换前先删除build目录。我试过很多次只改 CMake 变量不删目录最后总会漏掉某个隐藏变量。第二补丁函数里应优先使用mariadb_config还是mysql_config取决于你项目里哪个库是“主”。如果你还有另一个项目用 libmysqlclient-dev不要在这个环境里混用。最干净的做法是在 Drogon 的构建容器里只暴露需要的那套配置命令。如果物理机上的/usr/bin/mysql_config其实是 MariaDB 包装脚本那也没问题关键是保持它在整个构建过程中稳定不变。第三不要试图通过修改mysql.h的 include 宏来绕过冲突。比如给编译器传-Dmysql.hmariadb/mysql.h之类的操作短期可能骗过编译器长期一定会在某个子模块里把两个头文件同时引入造成更奇怪的错误。第四CI 场景下建议每次都打印出mysql_config的路径和版本并把MYSQL_CONFIG_EXECUTABLE写进 CI 变量。少了这一步等哪天 runner 镜像更新了包构建可能会无声无息地切到另一套库。第五链接静态库时也要注意符号冲突。如果你同时链接了某个用 MariaDB 编译的静态库和另一个用 MySQL 编译的静态库最终二进制内可能有重复符号定义。这时候别在 CMake 层面纠结先确认整个依赖树里混用了几套客户端库该换则换。这套补丁和排查方法不只适用于 Drogon。以后你写任何基于 MySQL C API 的 C 组件遇到类似“哪个库来负责”的模糊问题时第一反应都应该是先确认一个配置命令再让 include 和 libs 全部从这一个命令生成最后固定到构建脚本里不留给系统“自由发挥”的空间。我个人现在的规矩是任何 C 项目只要涉及 MySQL/MariaDB 客户端库都会在 CMake 层写一个类似的原子查找函数并且把mysql_config的路径打印在配置日志最显眼的位置。这套 Drogon MySQL 兼容补丁只是这个思路的一个具体落地。只要环境里出现 libmariadb-dev 和 libmysqlclient-dev 的影子就用它把问题掐死在配置阶段。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑