You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多线程连接时MariaDB C++ Connector崩溃错误求助

问题:MariaDB C++ Connector并发调用connect方法导致服务器崩溃

在Ubuntu 20.04系统中使用gcc编译器及MariaDB C++ Connector时,Web服务器负载测试发现,只要存在多个并发连接,服务器就会崩溃。问题定位到MariaDB驱动的connect方法,报错为以下两种异常之一:

terminate called after throwing an instance of 'std::logic_error'
what(): basic_string::_M_construct null not valid
Aborted (core dumped)

terminate called after throwing an instance of 'std::invalid_argument'
what(): stoi
Aborted (core dumped)

最小复现代码(仅依赖标准库与mariadb/conncpp.hpp):

#include <mariadb/conncpp.hpp>
#include <iostream>
#include <thread>
#include <string>

sql::Driver* driver = sql::mariadb::get_driver_instance();
sql::SQLString connectionUrl = "jdbc:mariadb://localhost:3306/the_database_name";
sql::Properties properties = {{"user", "the_username"}, {"password", "the_password"}};

int main()
{
    // simulate 2 concurrent connections each making 500 requests
    std::vector<std::function<void()>> t1cbs;
    std::vector<std::function<void()>> t2cbs;
    for(int i = 0; i < 500; i++) // if does not fail for you at 500 up the number until it does
    {
        t1cbs.push_back([&]{
            std::unique_ptr<sql::Connection> conn(driver->connect(connectionUrl, properties));
            std::cout << "t1:" << conn->getHostname().c_str() << std::endl;
        });

        // comment out this block to keep the second thread from executing, and you will see 
        // that no errors occur
        t2cbs.push_back([&]{
            std::unique_ptr<sql::Connection> conn(driver->connect(connectionUrl, properties));
            std::cout << "t2:" << conn->getHostname().c_str() << std::endl;
        });
    }

    std::thread t1([&]{
        for(auto& cb : t1cbs)
            cb();
    });

    std::thread t2([&]{
        for(auto& cb : t2cbs)
            cb();
    });

    t1.join();
    t2.join();

    return 0;
}

经排查,driver->connect方法存在线程安全问题,官方文档也提及多线程环境下直接调用可能引发异常,以下是可行的规避方案:

解决思路
  • 对connect调用加全局互斥锁
    用互斥锁保护所有并发的connect调用,确保同一时间只有一个线程执行连接操作,直接解决线程冲突问题:

    #include <mutex>
    
    std::mutex connect_mutex;
    
    // 在每个线程的连接逻辑中加锁
    auto conn_func = [&]{
        std::lock_guard<std::mutex> lock(connect_mutex);
        std::unique_ptr<sql::Connection> conn(driver->connect(connectionUrl, properties));
        std::cout << "t1:" << conn->getHostname().c_str() << std::endl;
    };
    

    缺点是会牺牲部分并发性能,适合连接频率较低的场景。

  • 每个线程独立获取Driver实例
    避免多个线程共享同一个Driver实例,改为每个线程单独获取Driver:

    // 不在全局定义driver,而是在每个线程的连接逻辑中初始化
    t1cbs.push_back([&]{
        sql::Driver* local_driver = sql::mariadb::get_driver_instance();
        std::unique_ptr<sql::Connection> conn(local_driver->connect(connectionUrl, properties));
        std::cout << "t1:" << conn->getHostname().c_str() << std::endl;
    });
    

    每个线程使用独立的Driver实例,消除共享状态带来的线程安全隐患。

  • 使用连接池替代直接创建连接
    生产环境优先选择连接池,它不仅能管理连接生命周期、降低频繁创建连接的开销,还会内部处理线程安全问题。MariaDB C++ Connector原生支持连接池:

    #include <mariadb/conncpp/ConnectionPool.hpp>
    
    // 初始化连接池(建议全局或单例模式)
    sql::ConnectionPool* pool = sql::mariadb::ConnectionPool::create(
        driver, connectionUrl, properties, 10, 20); // 最小10个连接,最大20个
    
    // 线程中获取连接
    auto conn_func = [&]{
        std::unique_ptr<sql::Connection> conn(pool->getConnection());
        std::cout << "t1:" << conn->getHostname().c_str() << std::endl;
        // 连接使用后自动归还到池,无需手动关闭
    };
    

    这是高并发场景下的最优解决方案。

  • 升级MariaDB C++ Connector版本
    部分旧版本的Connector存在线程安全bug,升级到最新稳定版可能已修复相关问题。Ubuntu下可通过以下命令升级:

    sudo apt update && sudo apt install --only-upgrade libmariadb-dev libmariadbcpp-dev
    

内容的提问来源于stack exchange,提问作者whitwhoa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 18:24:26