Работа с тупиками в длительных транзакциях Hibernate - PullRequest
4 голосов
/ 23 февраля 2011

У меня есть приложение Hibernate, которое может производить одновременные вставки и обновления (через Session.saveOrUpdate) для записей с тем же первичным ключом, которому назначено . Эти транзакции выполняются в течение некоторого времени, в среднем около 15 секунд (поскольку данные собираются из удаленных источников и сохраняются по мере поступления). Мой уровень изоляции БД установлен на Read Committed, и я использую MySQL и InnoDB.

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

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

Edit:

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

@Entity
static class Person {
    @Id
    Long id = Long.valueOf(1);
    @Version
    private int version;
}

@Test
public void updateTest() {
    for (int i = 0; i < 5; i++) {
        new Thread() {
            public void run() {
                Session s = sf.openSession();
                Transaction t = s.beginTransaction();
                Person p = new Person();
                s.saveOrUpdate(p);
                s.flush(); // Waits...
            }
        }.run();
    }
}

И запросы, которые это выжидательно производит, ожидают на второй вставке:

select id, version from person where id=?
insert into person (version, id) values (?, ?)
select id, version from person where id=?
insert into person (version, id) values (?, ?)

1 Ответ

1 голос
/ 24 февраля 2011

Это верно, база данных снимает блокировки только тогда, когда транзакция зафиксирована. Поскольку вы используете спящий режим, вы можете использовать оптимистическую блокировку, которая блокирует базу данных на длительные периоды времени. По сути, hibernate делает то, что вы предлагаете, разделяя части чтения и записи на отдельные транзакции. При записи он проверяет, что данные в памяти не были изменены одновременно в базе данных.

...