как включить многопоточность / подключение к одной и той же таблице mysql?

#java #mysql #acid

#java #mysql #acid

Вопрос:

У меня есть программа, в которой запущено 2 потока, и каждый поток имеет свое собственное подключение к базе данных JDBC, и они будут получать доступ / изменять одну и ту же таблицу базы данных A, как показано ниже. В таблице A всего 2 столбца (id, name), а первичный ключ представляет собой комбинацию id и name.

 statement stmt;

// first delete it if the record has exist in table
stmt.addBatch("delete from A where id='arg_id' and name='arg_name';");

// then insert it to table
stmt.addBatch("insert into A values (arg_id, arg_name);");

stmt.executeBatch();
  

Возможно, два потока вставляют одни и те же данные в таблицу, и я получил следующее исключение,

 java.sql.BatchUpdateException: Duplicate entry '0001-joey' for key 1
        at com.mysql.jdbc.Statement.executeBatch(Statement.java:708)
        at com.mchange.v2.c3p0.impl.NewProxyStatement.executeBatch(NewProxyStatement.java:743)
        at proc.Worker.norD(NW.java:450)
  

У вас есть какие-либо идеи, как я могу исправить эту проблему? Спасибо.

С уважением, Джоуи

Ответ №1:

Почему бы не ввести простой оптимистичный механизм блокировки в базе данных?

Добавьте столбец версии и отслеживайте номер версии при выполнении транзакций удаления или обновления.

Ваша таблица будет выглядеть следующим образом

 create table test(
id int not null primary key,
name varchar,
rowversion int default = 0);
  

Каждый раз, когда вы извлекаете строку, вы должны извлекать версию строки, чтобы вы могли делать

 update test set name='new name' rowversion=rowversion 1 where id=id and rowversion=retrieved row version;
  

То же самое с удалением

 delete from test where id=id and rowversion=retrievedRowVersion;
  

Это простой механизм, который будет использовать функции управления совместимостью СУБД. Проверьте эту ссылку для получения дополнительной информации о оптимистичной блокировке http://en.wikipedia.org/wiki/Optimistic_concurrency_control#Examples

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

Также для двойной вставки хорош тот факт, что ваша транзакция отклонена, это означает, что дубликаты ключей не вставляются. Вы должны просто обработать исключение и уведомить пользователя о том, что произойдет.

Комментарии:

1. Спасибо, Карл. похоже, для реализации occ на уровне базы данных необходимо изменить структуру таблицы, поэтому я перейду к уровню кода Java, чтобы добавить некоторую синхронизацию, чтобы гарантировать, что разные потоки обращаются к таблице в последовательном порядке.

Ответ №2:

Оберните оба оператора в транзакцию:

 BEGIN;
DELETE FROM a WHERE ...;
INSERT INTO a VALUES (...);
COMMIT;
  

Обратите внимание, что пока таблица состоит только из первичного ключа, этот конфликт возникает только тогда, когда таблица не изменена в конце; я предполагаю, что вы хотите добавить больше столбцов, и в этом случае вам следует использовать UPDATE ... WHERE синтаксис для изменения значений.

Комментарии:

1. привет, Саймон, я попробовал следующие инструкции, stmt.executeUpdate(«удалить из A, где id =’arg_id’ и name =’arg_name’; вставить в значения (arg_id, arg_name); «); и я получил следующее исключение, у вас ошибка в вашем синтаксисе SQL; проверьте руководство, соответствующее вашей версии сервера MySQL, на предмет правильного синтаксиса для использования рядом с ‘ВСТАВИТЬ В ЗНАЧЕНИЯ (1, ‘joey’)’ в строке 1 . но этот оператор может выполняться в консоли mysql без ошибок. вы знаете, почему не удается запустить 2 sql за одно выполнение? Спасибо.

2. Я бы предположил, что executeUpdate выполняется некоторая дополнительная обработка инструкции, поскольку она должна быть в состоянии понять результат. Обратите внимание, что два оператора, разделенные точкой с запятой, — это не то же самое, что транзакция.

Ответ №3:

Используете ли вы какой-либо вид синхронизации? Сначала вам нужно будет обернуть код, который изменяет таблицу в:

 synchronized(obj)
{
    // code
}
  

где obj — это объект, к которому могут обращаться оба потока.
Я не знаю точной семантики ваших изменений таблицы, но если они оба вставляют идентификаторы, вам также нужно будет хранить «глобальный» идентификатор и атомарно увеличивать его в каждом потоке, чтобы они оба не получали одинаковое значение.

Комментарии:

1. спасибо за ваш ответ, Tudor. Нет, у двух потоков нет механизма синхронизации, и идентификатор может иметь одинаковое значение. есть ли способ на уровне базы данных Mysql, чтобы две транзакции (поскольку у них есть собственное соединение) выполнялись независимо и последовательно?

2. Две транзакции будут выполняться в любом порядке, в котором вы их запускаете. Ничто из того, что вы делаете на стороне базы данных, этого не изменит. Если вы хотите, чтобы они применялись к базе данных в определенном порядке, вам нужно использовать какую-то блокировку или синхронизацию, чтобы гарантировать это.