资讯详情

资讯详情

Windows下C/C++直连MySQL实战:VS2022环境配置与生产级避坑指南

1. 项目概述为什么在Windows上用C/C直连MySQL不是“炫技”而是刚需在Windows平台做嵌入式设备管理后台、工业数据采集终端、本地化部署的轻量级桌面应用或者给某高校实验室开发一套离线运行的数据分析工具时你几乎一定会遇到这个场景程序需要稳定、低延迟、不依赖浏览器或额外服务地读写结构化数据。这时候用JSON文件存配置撑不过三天用SQLite当并发写入超过5个线程、日志表单日插入超10万条时就开始卡顿而用HTTP调用远程API网络一抖整个采集流程就断链——这些都不是理论风险是我在给某跨平台系统做边缘侧数据缓存模块时连续踩了三周坑后亲手验证过的现实。标题里那句“亲测有效一遍过”不是营销话术而是对WindowsC/CMySQL组合长期被妖魔化的直接反击。很多人一看到“C连接MySQL”就自动脑补出编译报错满屏、libmysql.dll找不到、字符集乱码、查询结果指针悬空、多线程下崩溃……其实问题根本不在技术本身而在于绝大多数教程把“环境配置”和“代码逻辑”混为一谈用Linux思维硬套Windows路径规则拿MinGW的链接方式去配Visual Studio甚至把MySQL Connector/C的x86/x64版本混着用——这就像让一个开惯手动挡的人突然去开F1赛车不是车不行是离合器踏板位置和换挡逻辑完全没对齐。我这次实操用的是Windows 10 22H2 Visual Studio 2022v17.8 MySQL 8.0.33社区版全程未装任何第三方包管理器如vcpkg、conan所有依赖均来自MySQL官方二进制分发包。核心目标很朴素写一个能稳定执行SELECT/INSERT/UPDATE的控制台程序支持中文字段名与UTF8MB4内容可编译为x64 Release版本双击即运行不弹任何DLL缺失警告。它不追求高大上的ORM封装也不模拟Web框架的路由逻辑就是最原始的“打开连接→执行SQL→取结果→关连接”四步闭环。但正是这个闭环暴露了Windows下C/C数据库编程最常被忽略的五个断点头文件包含路径的绝对优先级、运行时DLL加载顺序的隐式依赖、字符集初始化时机的不可逆性、结果集内存生命周期的显式管理、以及VS项目属性页中“附加依赖项”与“附加库目录”的非对称生效逻辑。接下来每一节我都将用真实编译日志、调试器内存快照和Wireshark抓包截图文字描述还原来拆解这些断点而不是告诉你“把xxx加到include目录就行”。2. 环境准备与依赖解析为什么MySQL Connector/C的“官方包”反而最容易翻车2.1 官方安装包的隐藏陷阱x86/x64/VC版本必须三重对齐MySQL官网提供的Connector/C下载页mysql-connector-c-6.1.11-winx64-vs14.zip看似清晰实则埋了三道坎第一重坎架构位数VS2022默认新建项目为x64平台但很多教程仍沿用旧版x86包。若你误下mysql-connector-c-6.1.11-win32-vs14.zip并强行链接到x64项目链接器会直接报错LNK2001: unresolved external symbol _mysql_init4。这不是函数没定义而是4后缀暴露了这是32位调用约定__stdcall而x64下所有函数调用均无修饰后缀。实测对比x86包的libmysql.lib导出符号含数字x64包则全为裸函数名如mysql_init。解决方案只有唯一路径去MySQL官网下载页严格筛选“Windows x64” “Visual Studio 2022”对应版本实际需选vs17因MySQL尚未更新命名但vs17兼容包可用。第二重坎VC运行时版本vs14代表Visual Studio 2015vs17代表VS2022。若你用VS2022编译却链接vs14版libmysql.lib即使架构匹配运行时也会弹窗“无法启动此程序因为计算机中丢失MSVCP140.dll”。这是因为libmysql.lib内部静态链接了VC14运行时而VS2022默认使用VC17运行时MSVCP140.dll是VC14的VC17对应MSVCP140_1.dll。验证方法用Dependency Walker打开libmysql.dll查看其导入表中的DLL名称。正确做法是下载mysql-connector-c-8.0.33-winx64-vs17.zip注意8.0.33起官方已提供vs17包6.1.x系列需手动找兼容包。第三重坎安装路径空格与特殊字符很多人习惯把MySQL装在C:\Program Files\MySQL\但VS的“附加包含目录”若填入C:\Program Files\MySQL\Connector C 8.0\include编译器会因空格截断为C:\Program导致头文件找不到。更隐蔽的是某些企业镜像站打包时在路径中加入版本号如Connector C 8.0.33其中的.会被VS误判为路径分隔符。实测解决方案创建硬链接绕过空格——以管理员身份运行mklink /J C:\mysqlc C:\Program Files\MySQL\Connector C 8.0后续所有路径均用C:\mysqlc。提示不要试图用引号包裹带空格路径。VS项目属性中“附加包含目录”字段不支持引号转义引号会被当作路径一部分导致#include mysql.h时搜索C:\Program Files\MySQL\Connector C 8.0\include含引号而非真实路径。2.2 头文件与库文件的物理定位不是“复制粘贴”而是“路径映射”MySQL Connector/C解压后目录结构如下C:\mysqlc\ ├── include\ ← mysql.h, mysql_version.h等头文件 ├── lib\ ← libmysql.lib静态库、libmysql.dll动态库 └── bin\ ← libmysql.dll运行时必需关键认知include和lib是编译期依赖bin下的DLL是运行期依赖二者不可互换。常见错误是只配置了include路径却忘了bin目录——编译成功运行时报“找不到libmysql.dll”。正确做法是编译期在VS项目属性 → C/C → 常规 → 附加包含目录填入C:\mysqlc\include链接期在链接器 → 常规 → 附加库目录填入C:\mysqlc\lib运行期将C:\mysqlc\bin\libmysql.dll复制到你的.exe同目录或添加C:\mysqlc\bin到系统PATH不推荐污染全局环境。注意libmysql.lib是导入库Import Library不是静态链接库。它不包含实际代码只包含DLL函数地址跳转表。因此即使你勾选了“使用静态库链接CRT”仍必须提供libmysql.dll。这是Windows DLL机制的本质决定的与Linux下.a和.so的混合使用逻辑完全不同。2.3 MySQL服务端配置三个被90%教程忽略的启动参数客户端能连上不代表能正常工作。我在某次实测中发现程序能成功mysql_real_connect()但执行SELECT * FROM test_table时返回空结果集且mysql_error()为空字符串。抓包发现TCP连接建立后MySQL服务端立即发送FIN包断开。根源在于MySQL 8.0默认启用了caching_sha2_password认证插件而旧版Connector/C8.0仅支持mysql_native_password。解决方案有二方案A推荐服务端降级认证修改MySQL配置文件my.ini通常在C:\ProgramData\MySQL\MySQL Server 8.0\在[mysqld]段添加default_authentication_pluginmysql_native_password然后重启MySQL服务net stop mysql80 net start mysql80。验证命令mysql -u root -p -e SELECT plugin FROM mysql.user WHERE Userroot;输出应为mysql_native_password。方案B客户端升级Connector直接使用MySQL 8.0.33官方Connector/C它原生支持caching_sha2_password。但需额外调用mysql_options()设置SSL模式否则仍会失败mysql_options(mysql, MYSQL_OPT_SSL_MODE, (const char*)SSL_MODE_DISABLED);另一个致命配置是max_allowed_packet。当插入含长文本如JSON日志的记录时若该值小于数据长度MySQL会静默截断并返回成功。默认值为4MB但工业传感器日志单条可达10MB。修改方法my.ini中添加max_allowed_packet64M。3. 核心代码实现与原理剖析从mysql_init()到mysql_free_result()的每一步意图3.1 连接初始化mysql_init()不是“分配内存”而是“构造状态机”很多初学者认为MYSQL* mysql mysql_init(NULL);只是申请一个结构体指针实则这是MySQL客户端状态机的启动入口。mysql_init()内部做了三件事分配MYSQL结构体内存含连接缓冲区、字符集映射表、错误码存储区初始化MYSQL::charset字段为系统默认Windows下通常是gbk非UTF8设置MYSQL::server_version为空等待mysql_real_connect()填充。关键陷阱若传入非NULL指针如mysql_init(mysql)则MYSQL结构体必须由调用者保证内存有效且未初始化。但VS默认栈变量未清零mysql.net.vio等指针域可能为随机值导致后续mysql_real_connect()访问野指针崩溃。实测代码必须写成MYSQL* mysql mysql_init(NULL); // 让库内部malloc安全可控 if (!mysql) { fprintf(stderr, mysql_init() failed\n); return -1; }实操心得永远不要手动malloc一个MYSQL结构体。MySQL官方文档明确警告“The structure must be initialized by calling mysql_init(). Do not allocate the structure yourself.” 这不是建议是强制契约。3.2 字符集设置mysql_set_character_set()必须在mysql_real_connect()之后这是中文乱码问题的终极答案。几乎所有教程都把字符集设置放在连接前例如// ❌ 错误示范连接前设置 mysql_init(mysql); mysql_set_character_set(mysql, utf8mb4); // 此时mysql.net.vio未初始化 mysql_real_connect(...);此时mysql_set_character_set()会因mysql-net.vio NULL直接返回CR_COMMANDS_OUT_OF_SYNC错误但多数人忽略返回值检查导致后续所有SQL执行都用默认gbk编码中文存入变成????。正确时序是// ✅ 正确流程连接成功后立即设置 if (!mysql_real_connect(mysql, localhost, user, pass, db, 3306, NULL, 0)) { fprintf(stderr, Connection failed: %s\n, mysql_error(mysql)); return -1; } // 连接成功后vio已初始化此时设置才有效 if (mysql_set_character_set(mysql, utf8mb4)) { fprintf(stderr, Set charset failed: %s\n, mysql_error(mysql)); return -1; }原理mysql_set_character_set()本质是向MySQL服务器发送SET NAMES utf8mb4指令并同步更新客户端内部的字符集转换表。该操作依赖已建立的TCP连接即mysql-net.vio故必须在mysql_real_connect()之后。3.3 查询执行与结果处理mysql_store_result()与mysql_use_result()的本质区别执行SELECT后必须调用结果获取函数但选哪个网上答案混乱这里用一张表说清本质对比维度mysql_store_result()mysql_use_result()内存占用高将全部结果集缓存在客户端内存极低仅缓存当前行逐行从socket读取适用场景结果集小1万行、需多次遍历、需mysql_data_seek()结果集大10万行、只遍历一次、内存受限线程安全安全结果集独立于连接状态危险同一连接不能并发执行其他查询典型错误大结果集OOM崩溃mysql_fetch_row()中途调用mysql_query()导致连接阻塞实测案例某传感器日志表单次查询返回12万行用store_result导致程序占用内存飙升至2.3GB后被系统终止改用use_result后内存恒定在15MB但必须确保while (row mysql_fetch_row(res))循环内不调用任何其他mysql_*函数。注意无论用哪个函数mysql_free_result()都必须调用否则内存泄漏。store_result释放的是结果集内存use_result释放的是socket缓冲区资源。漏掉任一者下次查询都会失败。3.4 错误处理mysql_error()返回空字符串的三种真相当mysql_query()返回0成功但业务逻辑异常时mysql_error()常返回空字符串让人误以为“没错误”。实则有三种情况情况1SQL语法正确但逻辑失败如INSERT INTO t(id) VALUES(1)但表中id为UNIQUE且1已存在。此时mysql_errno()返回1062ER_DUP_ENTRYmysql_error()返回Duplicate entry 1 for key PRIMARY。必须检查mysql_errno()而非仅看mysql_error()是否为空。情况2连接已断开但未检测网络抖动导致TCP连接中断mysql_query()仍返回0因写入socket缓冲区成功但后续mysql_store_result()返回NULL且mysql_error()为空。正确检测方式if (!res mysql_errno(mysql)) { ... }。情况3字符集不匹配导致静默截断客户端设utf8mb4服务端表用latin1插入含emoji的字符串时MySQL自动截断并返回成功mysql_error()为空。验证方法SELECT LENGTH(col), CHAR_LENGTH(col)若前者为后者2倍说明发生多字节截断。4. 完整可运行示例一个带防错机制的生产级连接模板4.1 项目配置清单VS2022实测通过项目属性项值说明平台工具集Visual Studio 2022 (v143)确保与Connector/C的vc17包匹配Windows SDK版本10.0 (最新)避免旧SDK导致的API兼容问题配置类型应用程序(.exe)不要选DLL或静态库附加包含目录C:\mysqlc\include注意无引号无尾部反斜杠附加库目录C:\mysqlc\lib指向libmysql.lib所在目录附加依赖项libmysql.lib注意不是libmysql.dll也不是mysqlclient.lib运行时库多线程DLL (/MD)与Connector/C的vc17运行时一致生成事件-后期xcopy /Y C:\mysqlc\bin\libmysql.dll $(OutDir)自动复制DLL到输出目录避免手动操作4.2 核心源码test_mysql.c每行注释直指痛点#include stdio.h #include stdlib.h #include string.h #include mysql.h // 必须在所有标准头文件之后避免宏冲突 // 全局错误处理宏打印文件名、行号、错误信息 #define CHECK_MYSQL_ERROR(mysql, msg) do { \ if (mysql_errno(mysql)) { \ fprintf(stderr, [%s:%d] %s: %s (Errno: %d)\n, \ __FILE__, __LINE__, msg, mysql_error(mysql), mysql_errno(mysql)); \ exit(EXIT_FAILURE); \ } \ } while(0) int main(int argc, char** argv) { MYSQL* mysql NULL; MYSQL_RES* res NULL; MYSQL_ROW row; // 1. 初始化连接对象必须用NULL参数 mysql mysql_init(NULL); if (!mysql) { fprintf(stderr, mysql_init() failed\n); return EXIT_FAILURE; } // 2. 连接MySQL服务器注意host用127.0.0.1而非localhost // 原因localhost触发Windows命名管道而Connector/C默认禁用管道 if (!mysql_real_connect(mysql, 127.0.0.1, testuser, testpass, testdb, 3306, NULL, 0)) { fprintf(stderr, mysql_real_connect() failed: %s\n, mysql_error(mysql)); goto cleanup; } // 3. 关键连接成功后立即设置字符集utf8mb4支持emoji if (mysql_set_character_set(mysql, utf8mb4)) { fprintf(stderr, mysql_set_character_set() failed: %s\n, mysql_error(mysql)); goto cleanup; } // 4. 创建测试表自动建库避免权限问题 const char* create_sql CREATE DATABASE IF NOT EXISTS testdb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;; if (mysql_query(mysql, create_sql)) { CHECK_MYSQL_ERROR(mysql, CREATE DATABASE); } // 切换数据库 if (mysql_select_db(mysql, testdb)) { CHECK_MYSQL_ERROR(mysql, mysql_select_db); } // 创建表 const char* create_table CREATE TABLE IF NOT EXISTS sensor_log ( id INT AUTO_INCREMENT PRIMARY KEY, device_id VARCHAR(20) NOT NULL, log_time DATETIME DEFAULT CURRENT_TIMESTAMP, data_json JSON, note TEXT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;; if (mysql_query(mysql, create_table)) { CHECK_MYSQL_ERROR(mysql, CREATE TABLE); } // 5. 插入含中文和emoji的数据验证字符集 const char* insert_sql INSERT INTO sensor_log (device_id, data_json, note) VALUES (DEV-001, {\temp\:25.3,\unit\:\℃\}, 传感器启动✅);; if (mysql_query(mysql, insert_sql)) { CHECK_MYSQL_ERROR(mysql, INSERT); } printf(Inserted 1 row.\n); // 6. 查询并打印结果使用mysql_store_result适合小结果集 const char* select_sql SELECT id, device_id, note FROM sensor_log ORDER BY id DESC LIMIT 5;; if (mysql_query(mysql, select_sql)) { CHECK_MYSQL_ERROR(mysql, SELECT); } res mysql_store_result(mysql); if (!res) { CHECK_MYSQL_ERROR(mysql, mysql_store_result); } printf(\nQuery result:\n); printf(%-5s %-10s %-20s\n, ID, Device, Note); printf(----------------------------------------\n); while ((row mysql_fetch_row(res))) { // mysql_fetch_row返回char**需手动转换为字符串注意NULL值 printf(%-5s %-10s %-20s\n, row[0] ? row[0] : NULL, row[1] ? row[1] : NULL, row[2] ? row[2] : NULL); } // 7. 清理资源顺序不能错 cleanup: if (res) mysql_free_result(res); // 先释放结果集 if (mysql) mysql_close(mysql); // 再关闭连接 return EXIT_SUCCESS; }4.3 编译与运行验证步骤编译命令VS开发者命令提示符cl /EHsc /MD test_mysql.c /IC:\mysqlc\include /link C:\mysqlc\lib\libmysql.lib若出现LNK2019: unresolved external symbol检查/I路径是否拼写错误或libmysql.lib是否为x64版本。运行前检查DLL执行dumpbin /dependents test_mysql.exe确认输出中包含libmysql.dll。若无说明链接失败需检查/link参数。首次运行必做三件事确认MySQL服务正在运行services.msc中检查MySQL80状态确认testuser用户存在且有testdb库权限mysql -u root -p -e GRANT ALL ON testdb.* TO testuserlocalhost IDENTIFIED BY testpass; FLUSH PRIVILEGES;确认防火墙放行3306端口若用IP而非127.0.0.1。预期输出Inserted 1 row. Query result: ID Device Note ---------------------------------------- 1 DEV-001 传感器启动✅5. 常见问题与排查技巧实录那些让老手也皱眉的“幽灵错误”5.1 问题速查表按现象归类精准定位现象描述最可能原因排查命令/操作解决方案编译报错mysql.h: No such file附加包含目录路径错误或未生效在VS中右键项目→属性→C/C→常规→附加包含目录确认路径存在且无拼写错误用dir C:\mysqlc\include\mysql.h验证文件存在链接报错LNK2019: _mysql_init4x86库链接到x64项目dumpbin /headers C:\mysqlc\lib\libmysql.lib | findstr machine输出x64才正确下载x64版Connector/C或新建x86项目运行报错“找不到libmysql.dll”DLL未复制到exe同目录depends.exe test_mysql.exeDependency Walker查看红色标记的DLL将C:\mysqlc\bin\libmysql.dll复制到test_mysql.exe所在文件夹连接成功但查询返回空结果字符集未设置或服务端不匹配mysql -u testuser -p -e SELECT character_set_client, collation_connection;确保客户端和服务端均为utf8mb4且mysql_set_character_set()在connect后调用中文显示为???或乱码客户端字符集设错或字段定义为latin1SHOW CREATE TABLE sensor_log;查看note字段的CHARACTER SET修改表ALTER TABLE sensor_log MODIFY note TEXT CHARACTER SET utf8mb4;程序运行几秒后崩溃无错误提示mysql_free_result()未调用或调用两次用Visual Studio调试器在mysql_free_result()处设断点观察调用次数确保每个mysql_store_result()/mysql_use_result()后有且仅有一次mysql_free_result()5.2 独家避坑技巧来自三年现场调试的血泪总结技巧1用mysql_ping()代替心跳包长连接场景下网络中断后mysql_query()可能不报错因数据发到socket缓冲区。每执行N次查询后调用if (mysql_ping(mysql)) { /* 重连 */ }mysql_ping()会真实发送TCP探测包并等待响应比自建心跳逻辑可靠十倍。技巧2mysql_real_escape_string()的缓冲区溢出防护该函数要求目标缓冲区大小为2*src_len1但很多人写成strlen(src)1导致溢出。安全写法size_t src_len strlen(user_input); char* escaped malloc(2 * src_len 1); size_t escaped_len mysql_real_escape_string(mysql, escaped, user_input, src_len);技巧3多线程下MYSQL*指针不可共享即使加锁MYSQL结构体内部有非线程安全的缓冲区如net.read_pos。正确做法每个线程mysql_init(NULL)独立连接或使用连接池如mysql_thread_safe()返回1才可池化。技巧4mysql_stmt_*预处理语句的隐形性能陷阱预处理语句虽防SQL注入但mysql_stmt_prepare()每次调用都向服务器发送完整SQL文本。高频查询场景下应缓存MYSQL_STMT*指针复用mysql_stmt_execute()而非每次prepare→execute→close。5.3 调试工具链实测推荐Wireshark过滤规则tcp.port 3306 mysql可直观看到客户端发送的COM_QUERY包内容及服务端返回的OK_Packet验证SQL是否被截断或编码错误Process MonitorSysinternals监控test_mysql.exe对libmysql.dll的CreateFile调用若路径显示C:\mysqlc\bin\libmysql.dll但结果为NAME NOT FOUND说明DLL路径错误Visual Studio内存诊断启用/MTd编译运行时开启诊断堆_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF)可捕获mysql_init()分配的内存未释放问题。我在给某工业网关开发数据同步模块时曾用Process Monitor发现程序在mysql_close()后仍在尝试加载libmysql.dll最终定位到第三方日志库静态链接了旧版MySQL库造成DLL冲突。这种问题没有工具链支撑靠猜十年也解决不了。6. 生产环境加固建议从“能跑”到“稳跑”的最后五步6.1 连接池设计为什么mysql_init(NULL)不能滥用单次mysql_init(NULL)创建的连接其内部缓冲区大小固定默认1MB。当执行SELECT * FROM huge_table时mysql_store_result()会尝试分配远超1MB的内存导致malloc失败返回NULL。生产环境必须实现连接池但池化不是简单数组缓存MYSQL*需考虑连接健康检查每次从池取出连接前执行mysql_ping()失败则丢弃并新建空闲连接回收设置max_idle_time300秒超时连接自动mysql_close()最大连接数限制MySQL服务端max_connections默认151客户端池大小不应超过100避免耗尽服务端资源线程安全销毁池销毁时必须确保所有线程已退出再逐个调用mysql_close()否则mysql_close()可能访问已被其他线程释放的net.vio。6.2 SQL注入防御mysql_real_escape_string()的局限性与替代方案mysql_real_escape_string()只能处理字符串对数字、表名、列名无效。例如动态排序ORDER BY ?中的?不能用escape处理。正确方案是白名单校验const char* valid_sort_fields[] {id, device_id, log_time}; int is_valid_field(const char* field) { for (int i 0; i sizeof(valid_sort_fields)/sizeof(valid_sort_fields[0]); i) { if (strcmp(field, valid_sort_fields[i]) 0) return 1; } return 0; } // 使用前校验if (!is_valid_field(user_sort)) { /* 拒绝 */ }6.3 日志与监控在mysql_query()前后埋点生产环境必须记录每次查询的耗时与SQL文本脱敏clock_t start clock(); if (mysql_query(mysql, sql)) { log_error(Query failed: %s, SQL: %s, mysql_error(mysql), mask_sensitive(sql)); } else { double ms ((double)(clock() - start)) / CLOCKS_PER_SEC * 1000; if (ms 1000) { // 超1秒告警 log_warn(Slow query: %.2fms, SQL: %s, ms, mask_sensitive(sql)); } }mask_sensitive()函数需替换passwordxxx、tokenyyy等敏感词为***避免日志泄露。6.4 故障自愈连接断开后的优雅重试网络抖动时mysql_query()可能返回错误但连接对象仍有效。应实现指数退避重试int retry_count 0; const int max_retries 3; while (retry_count max_retries) { if (mysql_query(mysql, sql) 0) break; if (mysql_errno(mysql) CR_SERVER_LOST || mysql_errno(mysql) CR_CONN_HOST_ERROR) { // 重连逻辑 mysql_close(mysql); mysql mysql_init(NULL); if (!mysql_real_connect(/*...*/)) continue; retry_count; Sleep(100 * (1 retry_count)); // 100ms, 200ms, 400ms } else { break; // 其他错误不重试 } }6.5 性能基准你的连接到底有多快用以下SQL测试基础性能排除网络影响本地连接-- 测试纯连接开销 SELECT BENCHMARK(10000, SLEEP(0.0001)); -- 测试简单查询延迟 SELECT SQL_NO_CACHE id FROM sensor_log LIMIT 1;实测数据i7-10750H, NVMe SSDmysql_real_connect()平均耗时8.2msmysql_query(SELECT 1)平均耗时0.3msmysql_store_result()处理1000行结果1.7msmysql_free_result()释放内存0.1ms。若你的实测值偏离超50%需检查杀毒软件实时扫描、MySQLinnodb_buffer_pool_size过小、或Windows电源计划为“节能模式”降频CPU。我在交付某高校实验室数据采集系统时客户反馈“查询慢”实测发现是Windows电源计划设为“节能”CPU频率被锁在0.8GHz调回“高性能”后查询速度提升3.2倍。这种问题不跑真实基准永远发现不了。最后再分享一个小技巧如果项目需要打包分发不要把libmysql.dll直接扔进安装包而是用Dependencies工具github.com/lucasg/Dependencies扫描其依赖的VC运行时DLL如VCRUNTIME140_1.dll一并打包避免目标机器缺少运行时。这比让用户自己装Visual C Redistributable靠谱得多。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →