C++使用libmysqlclient.a连接MySQL偶现初始化失败问题咨询
问题解答
1. 代码存在的问题及资源泄漏情况
你的代码存在多处明显缺陷,也是导致偶发初始化失败的核心原因:
- 指针未初始化:构造函数没有对
sock、my_sql两个私有指针做初始化,野指针可能导致后续资源判断、释放逻辑出现不可预期的异常。 - 资源泄漏严重:
executeSql方法中获取的MYSQL_RES结果集用完后没有调用mysql_free_result释放,每次查询都会泄漏内存,累计到一定程度会导致mysql_init申请内存失败,就是你遇到的偶发Init failed问题。connInit中如果mysql_real_connect连接失败,已经通过mysql_init创建的my_sql对象没有释放,直接返回会造成内存泄漏。- 析构函数为空,如果业务代码忘记手动调用
close方法,连接资源和MYSQL对象会直接泄漏。
- 逻辑判断错误:
mysql_real_connect失败时返回的是NULL,你用sock < 0判断指针有效性属于逻辑错误,不同平台下指针比较规则不同,可能导致正常连接被误判为失败,也可能漏判连接失败的情况。connInit方法执行成功后没有写return 0,属于未定义行为,函数返回值可能是随机值,导致业务逻辑误判初始化失败。executeSql方法没有判断mysql_query的返回值,SQL执行失败时后续取结果的逻辑会直接报错。
- 类设计缺陷:没有禁用拷贝构造、拷贝赋值函数,一旦意外复制了
ConnMysql对象,会导致两个对象持有同一份MySQL连接资源,最终出现双重释放的问题。
2. 获取MySQL初始化/连接失败具体原因的方法
你可以直接调用MySQL客户端库提供的错误查询函数获取详情:
- 如果
mysql_init返回NULL,调用mysql_error(NULL)就能获取初始化失败的错误信息,调用mysql_errno(NULL)可以拿到对应的错误码。 - 如果
mysql_real_connect返回NULL,传入mysql_init返回的MYSQL对象作为参数,调用mysql_error(my_sql)、mysql_errno(my_sql)就能拿到连接失败的具体原因,比如账号密码错误、连接数超限、网络超时、服务端不可用等。
修改后的核心逻辑参考:
// 构造函数初始化指针 ConnMysql::ConnMysql():sock(nullptr), my_sql(nullptr){ } // 析构函数自动释放资源,避免漏调用close泄漏 ConnMysql::~ConnMysql(){ close(); } int ConnMysql::connInit(const char* host,int port,const char* user,const char* password,const char* database){ // 先释放之前的资源,避免重复调用connInit导致泄漏 close(); my_sql = mysql_init(NULL); if (my_sql == NULL) { fprintf(stderr, "Init failed, err: %s, errno: %d\n", mysql_error(NULL), mysql_errno(NULL)); return -1; } sock = mysql_real_connect(my_sql, host, user, password, database, port, NULL, CLIENT_MULTI_STATEMENTS); if (sock == NULL) { fprintf(stderr, "Connect failed, err: %s, errno: %d\n", mysql_error(my_sql), mysql_errno(my_sql)); // 连接失败要释放已经创建的my_sql对象 mysql_close(my_sql); my_sql = nullptr; return -1; } // 补充成功返回值 return 0; } int ConnMysql::executeSql(const char* sql, std::vector<std::string> &v) { if (sock == nullptr) { return -1; } int ret = mysql_query(sock, sql); if (ret != 0) { fprintf(stderr, "Execute sql failed, err: %s\n", mysql_error(sock)); return -1; } MYSQL_RES *res_ptr = mysql_store_result(sock); if(res_ptr == nullptr) { printf("Get data from mysql failed, err: %s\n", mysql_error(sock)); return -1; } MYSQL_ROW sqlrow; while((sqlrow = mysql_fetch_row(res_ptr))) { if (sqlrow[0] != nullptr) { v.push_back(std::string(sqlrow[0])); } } // 补充结果集释放,避免内存泄漏 mysql_free_result(res_ptr); return 0; } void ConnMysql::close() { if (sock != nullptr) { mysql_close(sock); sock = nullptr; } my_sql = nullptr; }
内容的提问来源于stack exchange,提问作者Yongqi Z
相关产品推荐
相关产品推荐

