Помощь с потоками Java или исполнителями: выполнение нескольких операций выбора, вставки и обновления MySQL одновременно - PullRequest
0 голосов
/ 02 июня 2010

Я пишу приложение для анализа базы данных MySQL, и мне нужно выполнить несколько DML одновременно; например:

// In ResultSet rsA: Select * from A;
rsA.beforeFirst();
while (rsA.next()) {
   id = rsA.getInt("id");
   // Retrieve data from table B: Select * from B where B.Id=" + id;
   // Crunch some numbers using the data from B
   // Close resultset B
}

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

Я полагаю, что есть способ написать код таким образом, чтобы любой данный объект имел свое собственное соединение и выполнял требуемые задачи независимо от любого другого объекта. Например:

DataObject dataObject[0] = new DataObject(id[0]);
DataObject dataObject[1] = new DataObject(id[1]);
DataObject dataObject[2] = new DataObject(id[2]);
...
DataObject dataObject[N] = new DataObject(id[N]);
// The 'DataObject' class has its own connection to the database, 
// so each instance of the object should use its own connection. 
// It also has a "run" method, which contains all the tasks required.
Executor ex = Executors.newFixedThreadPool(10);

for(i=0;i<=N;i++) {
   ex.execute(dataObject[i]);
}
// Here where the problem is: Each instance creates a new connection,
// but every DML from any of the objects is cluttered in just one connection
// (in MySQL command line, "SHOW PROCESSLIST;" throws every connection, and all but
// one are idle).

Можете ли вы указать мне правильное направление?

Спасибо

Ответы [ 2 ]

1 голос
/ 02 июня 2010

Мне кажется, проблема в том, что вы смешали много логики среднего уровня, транзакционной и постоянной логики в одном классе.

Если вы имеете дело непосредственно с ResultSet, вы не думаете о вещах очень объектно-ориентированным образом.

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

Если нет, я бы рекомендовал оставлять соединения открытыми в течение минимально возможного времени. Откройте Соединение, получите ResultSet, сопоставьте его с объектом или структурой данных, закройте ResultSet и Connection в локальной области и верните сопоставленный объект / структуру данных для обработки.

Таким образом вы сохраняете логику сохранения и обработки отдельно. Вы спасаете себя от горя, сохраняя связи недолговечными.

Если решение хранимой процедуры медленное, это может быть связано с плохой индексацией. Другое решение будет работать одинаково плохо, если не хуже. Попробуйте запустить EXPLAIN PLAN и посмотрите, использует ли какой-либо из ваших запросов TABLE SCAN. Если да, у вас есть несколько индексов для добавления. Это также может быть связано с большими журналами отката, если ваши транзакции выполняются долго. Вы можете и должны сделать многое, чтобы убедиться, что вы сделали все возможное с решением, которое у вас было до переключения. Вы могли бы приложить огромные усилия и все еще не устранить первопричину.

0 голосов
/ 28 марта 2011

После некоторого времени срыва мозга я разобрался со своими собственными ошибками ... Я хочу применить это новое знание, так что ... вот и я

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

После исправления этой первой проблемы я вернулся к таблице параметров и понял, что мой процесс:

  1. Чтение идентификатора из входной таблицы
  2. Взять блок данных, относящихся к идентификатору, считанному на шаге 1, сохраненный в других таблицах ввода
  3. Числа хруста: прочитать соответствующие входные таблицы и обработать данные, хранящиеся в них
  4. Сохранить результаты в одной или нескольких выходных таблицах
  5. Повторите процесс, пока у меня есть ожидающие идентификаторы во входной таблице

Благодаря использованию выделенного соединения для чтения входных данных и выделенного соединения для записи выходных данных производительность моей программы увеличилась ... но мне нужно было намного больше!

Мой первоначальный подход к шагам 3 и 4 заключался в том, чтобы сохранить в выводе каждый из результатов, как только я их получил ... Но я нашел лучший подход:

  • Считать входные данные
  • Сократить числа и поместить результаты в кучу очередей (по одной для каждой выходной таблицы)
  • Отдельный поток проверяет каждую секунду, есть ли данные в какой-либо из очередей. Если в очереди есть данные, запишите их в таблицы.

Итак, разделив задачи ввода и вывода, используя разные соединения, и перенаправив выходные данные основного процесса в очередь, и используя выделенный поток для задач хранения вывода, я наконец достиг того, чего хотел: многопоточное выполнение DML!


Я знаю, что есть более подходящие подходы к этой конкретной проблеме, но этот работает вполне нормально.

Так что ... если кто-то застрянет с такой проблемой ... Надеюсь, это поможет.

...