Параллелизм и блокировка Java EE - PullRequest
4 голосов
/ 07 мая 2009

У меня есть MDB (Message-bean-компонент), который получает сообщения со строкой, которые представляют слово. Также у меня есть таблица в базе данных. MDB должен хранить в таблице слова и количество раз, которое было получено каждое слово (счетчик).

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

Чтобы решить эту проблему, я должен сделать поле слова уникальным, и тогда второй экземпляр потерпит неудачу при коммите, повторно передав сообщение, которое будет работать, но может быть проблематичным. Это хорошая практика?

Другим решением является объединение этих строк после суммирования счетчика. Но что, если другой экземпляр увеличит счетчик в середине обновления.

Что если два экземпляра попытаются увеличить счетчик? @Version должно быть достаточно?

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

Также можете ли вы предложить несколько книг о методах параллелизма (не об использовании synchronized, поскольку мне нужно поддерживать Java EE и может запускать кластер серверов приложений)?


Обновление: Прочитав больше о EJB и JPA, я предполагаю, что хочу что-то вроде блокирующего объекта. Например, я могу создать новую таблицу, содержащую только столбцы id и key, а также такие данные:

ID | KEY
1  | WORDS_CREATE_LOCK

Так что, когда мне понадобится обработать новое слово, я сделаю что-то вроде этого (не точный код, не уверен, что оно даже скомпилируется):

// MAIN FUNCTION
public void handleWord(String wordStr) {
  Word w = getWord(wordStr);

  if (w == null)
    w = getNewOrSychronizedWord(wordStr);

  em.lock(w);
  w.setCounter(w.getCounter() + 1);
  em.unlock(w);
}

// Returns Word instance or null if not found
private Word getWord(String wordStr) {
  Word w = null;

  Query query = em.createQuery("select w from words as w where w.string = :wordStr order by w.id asc");
  query.setParameter("wordStr", wordStr);
  List<Word> words = query.getResultList();

  if (words.getSize() > 0)
    w = words.get(0);

  return w;
}

// Handles locking to prevent duplicate word creation
private Word getNewOrSynchronizedWord(String wordStr) {
  Word w = null;
  Locks l = em.find(WORDS_CREATE_LOCK_ID, Locks.class);
  em.lock(l);

  Word w = getWord(wordStr);

  if (w == null) {
    w = new Word(wordStr);
    em.persist(w);
  }

  em.unlock(l);
  return w;
}

Итак, вопрос в том, будет ли это работать таким образом? И можно ли сделать то же самое без ведения таблицы БД с блокировкой строк? Может быть какой-то механизм блокировки контейнера Java EE?

Если это поможет, я использую JBoss 4.2.


У меня есть новая идея для этого. Я могу создать два MDB:

1-й MDB со многими разрешенными экземплярами, который будет обрабатывать все сообщения и, если слово не найдено, отправит слово во второй MDB

2-й MDB с разрешенным только одним экземпляром, будет обрабатывать сообщения последовательно и позволит создавать новое слово

Лучшая часть: нет полной блокировки таблицы / метода / процесса, только блокировка строки при обновлении счетчика

Насколько это хорошо?

Спасибо.

Ответы [ 3 ]

2 голосов
/ 08 мая 2009

Если вы ищете производительность, отсутствие блокировки и т. Д., Я бы предложил другую таблицу: (word, timestamp). Ваши MDB просто вставят слово и метку времени. Другой процесс будет считать и обновлять таблицу с итогами.

1 голос
/ 07 мая 2009

Вы имеете в виду, что несколько экземпляров обрабатывают одно и то же сообщение или одно и то же слово используется в разных сообщениях? Если это одно и то же сообщение, вы должны использовать очередь вместо темы. Это, конечно, не решает проблему одного и того же слова в нескольких сообщениях. В этом случае вы можете последовать совету @Michael Borgwardt и @Vitaly Polonetsky.

Другой вариант, за пределами базы данных, состоит в том, чтобы разные экземпляры MDB обрабатывали слова, начинающиеся с набора букв. Это может быть легко достигнуто с помощью селекторов. Тогда будет только один MDB, обрабатывающий любое конкретное слово, но обработка по-прежнему распределяется между несколькими экземплярами для повышения производительности. Я не утверждаю, что это лучшая альтернатива, а просто другая, которая поддерживает довольно простую обработку на основе очередей.

1 голос
/ 07 мая 2009

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

Вам нужна книга о базах данных, посвященная транзакциям.

...