MySQL选择更新,许多线程超时

时间:2018-05-03 08:56:44

标签: c++ mysql database transactions timeout

我在MySQL数据库中有一个表,其中包含一些“准备好”的工作。

CREATE TABLE `ww_jobs_for_update` (
  `id` bigint(11) NOT NULL AUTO_INCREMENT,
  `status` int(11) NOT NULL,
  `inc` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) 

现在我有一个C ++ 1y多线程应用程序,其中每个线程转到表并选择status = 0(未完成)的作业,进行一些计算并在完成时设置status = 1.

问题是许多线程会同时获取“作业行”,因此必须在数据库中进行一些锁定。

锁定/更新/提交的C ++方法如下

connection = "borrow a connection from the pool"
std::unique_ptr<sql::Statement> statement(connection->sql_connection_->createStatement());
connection->sql_connection_->setAutoCommit(false);


//statement->execute("START TRANSACTION");
std::unique_ptr<sql::ResultSet> rs(statement->executeQuery("select id from ww_jobs_for_update where status=0 ORDER BY id LIMIT 1 FOR UPDATE"));


if (rs->next()) {

  db_id = rs->getInt64(1);
  DEBUG << "Unlock Fetched: " << db_id;
}

rs->close();

std::stringstream ss;
ss << "update ww_jobs_for_update set status=1 where id=" << db_id;


statement->execute(ss.str());
//statement->execute("COMMIT;");
connection->sql_connection_->commit();

"release the connection to the pool();" 

但这种做法似乎效率不高。我总是回来

  

ErrorCode:1205,SQLState:HY000。详细说明:

来自很多线程,特别是当负载增加时。

为什么我要回来?最有效的方法是什么,硬性一致性是一项要求。

2 个答案:

答案 0 :(得分:1)

根据我的经验,这项任务的最佳方法是使用redis队列。 锁定表SELECT ... FOR UPDATE在您运行多线程应用程序等时挂起数据库。

我建议您安装一个redis,并根据表中的数据编写一些脚本来创建队列,并重写程序以使用redis队列完成任务。 redis队列不会为不同的线程提供相同的值,因此您获得了唯一性,并且数据库中没有锁 - 因此您的脚本可以快速运行。

答案 1 :(得分:1)

您可以缩短交易时间吗?这就是我的意思。

对于&#34;等待&#34;您的状态值为0和#34;完成&#34;。使用状态值2(或-1,或您选择的任何内容)来表示&#34;工作&#34;。

然后当一个工作线程从表中获取要执行的工作时,它将执行此操作(伪SQL)。

  BEGIN TRANSACTION
  SELECT id FROM ww_jobs_for_update WHERE status=0 ORDER BY id LIMIT 1 FOR UPDATE
  UPDATE ww_jobs_for_update SET status=2 WHERE id = << db_id
  COMMIT

现在,您的线程已经完成了一项工作并释放了事务锁定。完成工作后,您只需执行此操作即可完成此操作,无需任何事务处理。

UPDATE ww_jobs_for_update SET status=1 WHERE id=" << db_id;

如果您可以保证每个工作线程都具有唯一标识符threadId,则可以采用更简单的方法。将thread列放在表中,并使用默认的NULL值。然后开始处理工作。

      UPDATE ww_jobs_for_update 
        SET thread = threadId, status = 2
      WHERE status = 0 AND threadId IS NULL
      ORDER BY id LIMIT 1;

      SELECT id FROM ww_jobs_for_update WHERE thread = threadId AND status=2

完成后

      UPDATE ww_jobs_for_update 
        SET thread = NULL, status = 1
      WHERE thread = threadId;

因为每个线程都有一个唯一的threadId,并且因为单个SQL UPDATE语句本身就是很少的事务,所以你可以在不使用任何事务或提交的情况下执行此操作。

这两种方法都有额外的好处,您可以使用SELECT查询找出哪些作业是活动的。这可能允许您处理因任何原因从未完成的工作。