C++ MySQL异步?

C++ MySQL异步?,c++,mysql,boost,asynchronous,boost-asio,C++,Mysql,Boost,Asynchronous,Boost Asio,我基本上面临着阻塞问题。 我的服务器基于C++的BooSt.asIO编码,使用8个线程,因为服务器有8个逻辑内核。 我的问题是一个线程可能面临0.2到1.5秒的MySQL查询阻塞,我真的不知道该如何绕过这个问题,因为MySQL C++连接器不支持异步查询,我不知道如何正确地设计服务器来使用多个线程来执行查询。 这就是我要征求意见的地方,在这种情况下该怎么办。 为异步查询sql创建100个线程? 我可以从专家那里得到一个意见吗?我不是MySQL专家,但下面是通用的多线程建议 当没有任何线程阻塞并

我基本上面临着阻塞问题。 我的服务器基于C++的BooSt.asIO编码,使用8个线程,因为服务器有8个逻辑内核。 我的问题是一个线程可能面临0.2到1.5秒的MySQL查询阻塞,我真的不知道该如何绕过这个问题,因为MySQL C++连接器不支持异步查询,我不知道如何正确地设计服务器来使用多个线程来执行查询。 这就是我要征求意见的地方,在这种情况下该怎么办。 为异步查询sql创建100个线程?
我可以从专家那里得到一个意见吗?

我不是MySQL专家,但下面是通用的多线程建议

  • 当没有任何线程阻塞并且您只是在所有CPU上分配负载时,使用
    NumberOfThreads==NumberOfCores
    是合适的

  • 一种常见的模式是每个CPU有多个线程,因此一个线程在执行,另一个线程在等待

  • 在您的情况下,我倾向于设置
    NumberOfThreads=n*NumberOfCores
    ,其中“n”是从配置文件、注册表项或其他用户可设置的值读取的。您可以使用不同的“n”值测试系统,以获得最佳结果。我建议在3点左右先猜一猜


好的,正确的解决方案是扩展Asio并编写一个
mysql\u服务
实现来集成它。我几乎要去,但我想开始使用“模拟”

我们的想法是

  • 使用
    io\u服务的业务流程
  • 数据库“facade”接口,将异步查询分派到不同的队列(io_服务)中,并将完成处理程序发回业务_进程
    io_服务
这里需要一个微妙的调整,您需要保持业务流程端的io_服务不会在其作业队列为空时立即关闭,因为它可能仍在等待来自数据库层的响应

因此,将其建模为一个快速演示:

namespace database
{
    // data types
    struct sql_statement { std::string dml; };
    struct sql_response { std::string echo_dml; }; // TODO cover response codes, resultset data etc.
我希望你能原谅我的过分简化:/

struct service
{
    service(unsigned max_concurrent_requests = 10)
        : work(io_service::work(service_)),
        latency(mt19937(), uniform_int<int>(200, 1500)) // random 0.2 ~ 1.5s
    {
        for (unsigned i = 0; i < max_concurrent_requests; ++i)
            svc_threads.create_thread(boost::bind(&io_service::run, &service_));
    }

    friend struct connection;

private:
    void async_query(io_service& external, sql_statement query, boost::function<void(sql_response response)> completion_handler)
    {
        service_.post(bind(&service::do_async_query, this, ref(external), std::move(query), completion_handler));
    }

    void do_async_query(io_service& external, sql_statement q, boost::function<void(sql_response response)> completion_handler)
    {
        this_thread::sleep_for(chrono::milliseconds(latency())); // simulate the latency of a db-roundtrip

        external.post(bind(completion_handler, sql_response { q.dml }));
    }

    io_service service_;
    thread_group svc_threads; // note the order of declaration
    optional<io_service::work> work;

    // for random delay
    random::variate_generator<mt19937, uniform_int<int> > latency;
};
此业务流程从在应用程序服务上发布自身开始。然后,它会连续执行许多db查询,并最终退出(通过执行
in_progress.reset()
应用程序服务会意识到这一点)

主演示,在单个线程上启动10个业务流程:

int main()
{
    io_service app;
    database::service db;

    ptr_vector<domain::business_process> bps;
    for (int i = 0; i < 10; ++i)
    {
        bps.push_back(new domain::business_process(app, db));
    }

    app.run();
}

在我看来,您似乎对Boost Asio(或类似的异步IO库,如libuv)不太熟悉。对于IO争用应用程序,最好的建议通常是在尽可能少的线程上多路传输IO,通常为1。创建许多线程来“补偿”阻塞实际上是在堆积更多的瓶颈(线程消耗资源并造成不断增长的调度开销)@sehe-你是对的,我不熟悉boost::asio,但是我使用过许多多线程通信系统。如果asio/mysql有一个线程池,并进行适当的作业管理,这样等待的作业就不会占用线程,那么额外的线程是没有帮助的。如果它让线程休眠,等待i/o(如问题所示),那么额外的线程保持CPU繁忙通常会提高性能。你知道asio/mysql是如何管理作业的吗?关键是asio/mysql不管理作业。Asio服务在等待时不会“占用”线程,但Asio尚未被告知Mysql接口。几分钟后看我的答案。我刚刚根据我目前对Asio的理解发布了一个答案。我认为写一个合适的扩展服务是一个更干净的方法。这应该不会太复杂(它做的是相同的事情),但它会更好地集成(例如与和)。它确实显示了Asio的能力;请注意,所有业务流程都在一个线程上,但可以自由复用。您预计会有多少并发SQL查询?每秒需要支持多少查询?如果没有要求,我会说:只需将查询排队,完成后再发回即可。正如我之前所说,我对asio了解不多(尽管我曾与编写asio的Chris Kohlhoff合作过,他是一个非常聪明的人),因此我无法为您的解决方案添加太多内容。看起来您正在使用异步完成处理程序,因此线程不会等待数据库完成。我可能应该避免这种情况,因为asio中的“a”表示“异步”。我要责备老年人:-要澄清:我期待客户端和服务器之间的异步通信连接,我没有意识到服务器监听器和数据库之间也是异步的。除非你考虑重写,否则,我们的控制系统几乎无法控制。一些库使其具有可扩展性。LibCurl在这方面非常酷(请参阅)。
namespace domain
{
    struct business_process : id_generator
    {
        business_process(io_service& app_service, database::service& db_service_) 
            : id(generate_id()), phase(0), 
            in_progress(io_service::work(app_service)),
            db(id, app_service, db_service_)
        { 
            app_service.post([=] { start_select(); });
        }

    private:
        int id, phase;
        optional<io_service::work> in_progress;

        database::connection db;

        void start_select() {
            db.async_query({ "select * from tasks where completed = false" }, [=] (database::sql_response r) { handle_db_response(r); });
        }

        void handle_db_response(database::sql_response r) {
            if (phase++ < 4)
            {
                if ((id + phase) % 3 == 0) // vary the behaviour slightly
                {
                    db.async_query({ "insert into tasks (text, completed) values ('hello', false)" }, [=] (database::sql_response r) { handle_db_response(r); });
                } else
                {
                    db.async_query({ "update * tasks set text = 'update' where id = 123" }, [=] (database::sql_response r) { handle_db_response(r); });
                }
            } else
            {
                in_progress.reset();
                lock_guard<mutex> lk(console_mx);
                std::cout << "business_process " << id << " has completed its work\n";
            }
        }
    };

}
int main()
{
    io_service app;
    database::service db;

    ptr_vector<domain::business_process> bps;
    for (int i = 0; i < 10; ++i)
    {
        bps.push_back(new domain::business_process(app, db));
    }

    app.run();
}
thread_group g;
for (unsigned i = 0; i < thread::hardware_concurrency(); ++i)
    g.create_thread(boost::bind(&io_service::run, &app));
g.join_all();