тупики - PullRequest
       24

тупики

6 голосов
/ 05 августа 2009

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

using (var session = NhibernateHelper.OpenSession())
using (var transaction = session.BeginTransaction())
{
    session.Lock(mall, LockMode.None);

    var voucher = new Voucher();
    voucher.FirstName = firstName ?? string.Empty;
    voucher.LastName = lastName ?? string.Empty;
    voucher.Address = address ?? string.Empty;
    voucher.Address2 = address2 ?? string.Empty;
    voucher.City = city ?? string.Empty;
    voucher.State = state ?? string.Empty;
    voucher.Zip = zip ?? string.Empty;
    voucher.Email = email ?? string.Empty;
    voucher.Mall = mall;
    session.Save(voucher);

    var issued = session.CreateCriteria<Voucher>()
        .Add(Restrictions.Eq("Mall", mall))
        .SetProjection(Projections.Count("ID"))
        .UniqueResult<int>();

    if (issued >= mall.TotalVouchers)
    {
        transaction.Rollback();
        throw new VoucherLimitException();
    }

    transaction.Commit();
    return voucher;
}        

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

  • Кто-нибудь может это подтвердить?
  • Может кто-нибудь предложить исправление?

Я пытался вызвать SetLockMode (LockMode.None) для окончательного запроса, но это приводит к исключению NullReferenceException, которое я не могу выяснить.

Редактировать: если я запускаю запрос перед сохранением объекта, он работает, но тогда я не достигаю цели проверки того, что моя вставка каким-то образом не превысила лимит (в случае одновременных вставок).

Редактировать: Я обнаружил, что использование IsolationLevel.ReadUncommited в сеансе. Вызов BeginTransaction решает проблему, но я не эксперт по базам данных. Это подходящее решение проблемы, или я должен как-то настроить свою логику?

Ответы [ 2 ]

3 голосов
/ 05 августа 2009

Такая схема может быть склонной к взаимоблокировке - обычно (не всегда) одно соединение вряд ли само блокируется, но множественные соединения, которые выполняют вставку и агрегирование по одной и той же таблице, с большой вероятностью могут заблокироваться. Это потому, что, хотя все действия в одной транзакции выглядят завершенными с точки зрения соединения, выполняющего работу - БД не будет блокировать транзакцию из «своих» записей - совокупные запросы из ДРУГИХ транзакций будут пытаться заблокировать весь стол или его большие части одновременно, и они зашли бы в тупик.

Read Uncommitted - , а не ваш друг в этом случае, потому что он в основном говорит "игнорировать блокировки", что в какой-то момент будет означать нарушение правил, которые вы установили для данных. И.Е. количество записей в таблице будет неточным, и вы будете действовать по этому неточному количеству. Ваш счет вернется 10 или 13, когда реальный ответ будет 11.

Лучший совет, который я имею, состоит в том, чтобы перестроить логику вставки так, чтобы вы уловили идею подсчета без буквального подсчета строк. Вы могли бы пойти пару направлений. У меня есть одна идея: буквально пронумеровать вставленные ваучеры последовательностью и наложить ограничение на саму последовательность.

  1. Создать таблицу последовательности со столбцами (я предполагаю) MallID, nextVoucher, maxVouchers
  2. Заполните эту таблицу маллидами, 1 и любым пределом для каждого торгового центра
  3. Измените логику вставки на этот псевдокод:
Begin Transaction
Sanity check the nextVoucher for Mall in the sequence table; if too many exist abort
If less than MaxVouchers for Mall then {
  check, fetch, lock and increment nextVoucher
  if increment was successful then use the value of nextVoucher to perform your insert. 
    Include it in the target table.
}
Error? Rollback
No Error? Commit

Такая таблица последовательностей вредит некоторым параллелизмам, но я думаю, что не так много, как постоянный подсчет строк в таблице. Обязательно перф тест. Кроме того, [проверка, выборка, блокировка и приращение] важны - вам нужно только заблокировать строку в таблице последовательности, чтобы не допустить использования этим же значением другого соединения в течение доли секунды, прежде чем увеличивать его. Я знаю синтаксис SQL для этого, но боюсь, что я не эксперт по nHibernate.

Для ошибок чтения незафиксированных данных, проверьте это: http://sqlblog.com/blogs/merrill_aldrich/archive/2009/07/29/transaction-isolation-dirty-reads-deadlocks-demo.aspx (отказ от ответственности: Merrill Aldrich - это я: -)

0 голосов
/ 05 августа 2009

2 вопроса:

  1. Как часто удаляются ваучеры
  2. Любые возражения (помимо чистоты) триггер уровня дБ?
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...