Насколько дорого обходится переход в спящий режим? - PullRequest
1 голос
/ 30 августа 2010

У меня есть следующий вариант использования, когда я получаю сообщение через JMS, касающееся сущности, через уникальное свойство (не PK), и мне требуется обновить состояние сущности:

HibernateUtil.beginSession();  
HibernateUtil.beginTransaction();  
try{  
  Entity entity = dao.getEntityByUniqueProperty(propertyValue);  
  if (entity==null){  
    entity = dao.addEntityByUniqueProperty(propertyValue)  
  }  
  entity.setSomeProperty(otherPropertyValue);
  HibernateUtil.commitTransaction();
} catch (ConstraintViolationException e){  
  HibernateUtil.rollbackTransaction();  
  //Do other things additionally  
} catch (StaleStateObjectException e){  
  HibernateUtil.rollbackTransaction();  
  //Do other things additionally  
} finally {  
  HibernateUtil.closeSession();  
}

В этом случае использования я должен быть готов к тому, что сущность, которую я пытаюсь обновить, еще не создана, и поэтому я прошу, чтобы такая сущность была создана (шаблон этого должен быть точным с уникальным свойством ) а потом меняю это. Моя дилемма выглядит следующим образом: С одной стороны, у меня есть два совершенно разных блока, и я должен использовать различные предложения catch, где это уместно, НО рассматривается как конечный случай, когда сущность отсутствует, когда я запрашиваю, но есть ли мс позже, когда я пытаюсь ее создать (отсюда ConstraintViolationException ) - это то, что не должно происходить слишком часто, и вставка из-за этого дополнительная фиксация / beginTransaction в середине кажется просто неэффективной.

Я в основном обеспокоен дополнительным ударом по производительности при синхронизации сеанса и соединении JDBC, которые выполняются, когда происходит фиксация / начало.
Я ошибся? Я ищу оптимизацию там, где не должен? Я что-то упустил?
Заранее спасибо

Ответы [ 4 ]

3 голосов
/ 30 августа 2010

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

  • В потоке 1 создан уникальный экземпляр
  • Поток 2 получает уникальный экземпляр
  • Тема 2 устанавливает другое свойство
  • Тема 1 устанавливает другое свойство
  • Тема 1 очищает кэши
  • Тема 2 сбрасывает тайники

Вопрос: будет ли БД согласованной в этом случае или нет? Будет ли (не) непротиворечивым в подобных случаях?

Всегда используйте транзакции. БД оптимизированы для этого. Начните думать о своем дизайне, если у вас есть проблемы. Например, когда вам приходится обрабатывать тысячи сообщений в секунду и , ваши инструменты производительности показывают, что этот код стал узким местом. Не доверяйте своим внутренним ощущениям.

2 голосов
/ 30 августа 2010

Транзитная транзакция - это всего лишь оболочка транзакции с БД. Так что это дорого, поскольку транзакция с БД будет.

Как всегда с оптимизацией, обычно лучше иметь ясный и безопасный код, чем пытаться получить эту дополнительную 1% -ую производительность. НО я не знаю ваш вариант использования. Если вышеперечисленное вызывается несколько раз в секунду, не беспокойтесь о производительности. Если его вызывают пару сотен раз в секунду, это может быть проблемой.

Если у вас есть проблемы с производительностью, измерьте / время / профилируйте код, пока не найдете проблему. Зачастую вы можете предположить, что проблема в одном месте, а на самом деле она где-то еще.

В вашем случае выше я бы сделал следующее

  • Поместите цикл while вокруг вашего кода (все это, включая сеанс открытия / закрытия)
  • Если он попадает в журнал блоков ConstraintViolationException и continue, поэтому вместо того, чтобы кодировать какую-то дополнительную логику, просто повторите попытку, он найдет новую строку, добавленную другой транзакцией, и обновит соответствующим образом.
  • Это работает нормально, тогда break выходит из цикла

РЕДАКТИРОВАТЬ: Как бы я это сделал ....

// loop as it is possible we get a retryable exception, see below
while (true){
    HibernateUtil.beginSession();
    HibernateUtil.beginTransaction();  
    try{  
      Entity entity = dao.getEntityByUniqueProperty(propertyValue);  
      if (entity==null){  
        entity = dao.addEntityByUniqueProperty(propertyValue)  
      }  
      entity.setSomeProperty(otherPropertyValue);
      HibernateUtil.commitTransaction();

      // success
      break;

    } catch (ConstraintViolationException e){  
      HibernateUtil.rollbackTransaction();

      // this is ok, another txn must have added the entity at the same time
      // try again and it will find the entity this time
      continue;

    } catch (StaleStateObjectException e){  
      HibernateUtil.rollbackTransaction();  
      //Do other things additionally  

    } finally {  
      HibernateUtil.closeSession();  
    }
}
1 голос
/ 30 августа 2010

Независимо от того, что вы будете делать, операции записи не могут быть выполнены вне транзакции, Hibernate пожалуется, если нет текущей транзакции, и выдаст исключение. Так что здесь нет выбора.

Теперь ваш вариант использования - findOrCreate() - не тривиален. Как вы упоминаете, вы можете столкнуться с состоянием гонки:

T1: BEGIN TX;

T2: BEGIN TX;

T1: getEntityByUniqueProperty("foo"); //returns null

T1: getEntityByUniqueProperty("foo"); //returns null

T1: addEntityByUniqueProperty("foo");
T1: COMMIT; //row inserted

T1: addEntityByUniqueProperty("foo");
T2: COMMIT; //constraint violation

Так что вам придется либо

  1. синхронизировать код (это только опция ЕСЛИ вы работаете в одной JVM) ~ или ~
  2. заблокировать всю таблицу (ой!) ~ Или ~
  3. разберитесь с этим и внедрите какой-нибудь механизм повтора.

Лично я бы выбрал вариант 3. С точки зрения производительности, это лучший выбор, и на самом деле это обычный шаблон, особенно при работе с сообщениями и высоким параллелизмом.

1 голос
/ 30 августа 2010

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

try {
    HibernateUtil.beginSession();
    Entity entity = dao.getEntityByUniqueProperty(propertyValue);
    if (entity==null){
        HibernateUtil.beginTransaction();
        try {
            entity = dao.addEntityByUniqueProperty(propertyValue)
            HibernateUtil.commitTransaction();
        } catch (ConstraintViolationException e){
            HibernateUtil.rollbackTransaction();
            HibernateUtil.closeSession();
            HibernateUtil.beginSession();
            entity = dao.getEntityByUniqueProperty(propertyValue);
            //Do other things additionally
        }
    }
    entity.setSomeProperty(otherPropertyValue);
    HibernateUtil.commitTransaction();
} catch (StaleStateObjectException e){
    HibernateUtil.rollbackTransaction();
    //Do other things additionally
} finally {
    HibernateUtil.closeSession();
}

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...