C++ MySQL异步?
我基本上面临着阻塞问题。 我的服务器基于C++的BooSt.asIO编码,使用8个线程,因为服务器有8个逻辑内核。 我的问题是一个线程可能面临0.2到1.5秒的MySQL查询阻塞,我真的不知道该如何绕过这个问题,因为MySQL C++连接器不支持异步查询,我不知道如何正确地设计服务器来使用多个线程来执行查询。 这就是我要征求意见的地方,在这种情况下该怎么办。 为异步查询sql创建100个线程?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专家,但下面是通用的多线程建议 当没有任何线程阻塞并
我可以从专家那里得到一个意见吗?我不是MySQL专家,但下面是通用的多线程建议
- 当没有任何线程阻塞并且您只是在所有CPU上分配负载时,使用
是合适的NumberOfThreads==NumberOfCores
- 一种常见的模式是每个CPU有多个线程,因此一个线程在执行,另一个线程在等待
- 在您的情况下,我倾向于设置
,其中“n”是从配置文件、注册表项或其他用户可设置的值读取的。您可以使用不同的“n”值测试系统,以获得最佳结果。我建议在3点左右先猜一猜NumberOfThreads=n*NumberOfCores
mysql\u服务
实现来集成它。我几乎要去,但我想开始使用“模拟”
我们的想法是
- 使用
io\u服务的业务流程
- 数据库“facade”接口,将异步查询分派到不同的队列(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();