MySQL выбран для обновления, время ожидания для многих потоков - PullRequest
0 голосов
/ 03 мая 2018

У меня есть таблица в базе данных 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, где каждый поток идет к таблице и выбирает задание, в котором статус = 0 (не завершен), выполняет некоторые вычисления и устанавливает статус = 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 голосов
/ 03 мая 2018

Можете ли вы сократить продолжительность транзакции? Вот что я имею в виду.

У вас есть значения состояния 0 для «ожидания» и 1 для «завершения». Используйте значение состояния 2 (или -1, или что вы выбираете), чтобы означать «работает».

Затем, когда рабочий поток получает задание из таблицы, он выполняет это (псевдо-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, чтобы выяснить, какие задания являются активными. Это может позволить вам работать с заданиями, которые по какой-либо причине никогда не выполнялись.

0 голосов
/ 03 мая 2018

Лучший способ для этой задачи в моем опыте - использование очередей redis. Блокировка таблиц SELECT ... FOR UPDATE - это зависание базы данных, когда у вас запущено многопоточное приложение и т. Д.

Я бы посоветовал вам установить Redis и написать несколько сценариев для создания очередей в соответствии с данными в таблицах и переписать вашу программу, чтобы использовать очереди Redis для этой задачи. Очереди redis не будут давать одно и то же значение для разных потоков, поэтому вы получаете уникальность, и в базе данных нет блокировок, поэтому ваши скрипты будут работать быстро.

...