我在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。详细说明:
来自很多线程,特别是当负载增加时。
为什么我要回来?最有效的方法是什么,硬性一致性是一项要求。
答案 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查询找出哪些作业是活动的。这可能允许您处理因任何原因从未完成的工作。