Многопоточность - предотвращение взаимоблокировок базы данных и их устранение

StackOverflow https://stackoverflow.com/questions/1866287

Вопрос

Я ищу хорошую стратегию борьбы с взаимоблокировками базы данных из приложения Java 6;несколько параллельных потоков потенциально могут выполнять запись в одну и ту же таблицу одновременно.База данных (Ingres RDMBS) случайным образом завершит один из сеансов, если обнаружит взаимоблокировку.

Какой был бы приемлемый метод для решения тупиковой ситуации, учитывая следующие требования?

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

Пока что стратегия, которую я разработал, примерно такова:

short attempts = 0;
boolean success = false;
long delayMs = 0;

Random random = new Random();
do {
    try {
        //insert loads of records in table 'x'
        success = true;
    } catch (ConcurrencyFailureException e) {
        attempts++;
        success = false;
        delayMs = 1000*attempts+random.nextInt(1000*attempts);

        try {
                Thread.sleep(delayMs);
            } catch (InterruptedException ie) {
        }
    }
} while (!success);

Можно ли это как-то улучшить?например ,ожидание фиксированного количества (магического числа) секунд.Существует ли другая стратегия, которая приведет к лучшим результатам?

Примечание: Несколько методов на уровне базы данных будут использованы для обеспечения того, чтобы взаимоблокировки на практике встречались очень редко.Кроме того, приложение попытается избежать планирования потоков, которые записывают данные в одну и ту же таблицу в одно и то же время.Описанная выше ситуация будет всего лишь “наихудшим сценарием”.

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

Это было полезно?

Решение

Обычно используемый подход - это некоторая форма экспоненциального отступления.Скорее , чем ваш 1000*attempts+random подход, сделайте задержку экспоненциальной функцией от количества попыток.Это гарантирует минимальную задержку в первые одну или две попытки, когда, возможно, вам просто не повезло, что вы зашли в тупик, но дает вам гораздо большие задержки позже, когда становится ясно, что соединение действительно перегружено.

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

Другие советы

Вот как мы это сделали.Зациклите и повторяйте транзакцию до тех пор, пока она не завершится.

Мы не связывались со случайными задержками.

Кроме того, мы выполнили фиксацию внутри try блокировка и откат в обработчике исключений.

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

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

При загрузке вставки (особенно в таблице КУЧИ) могут (часто) выполняться параллельно без многих проблем с конфликтом.Если вы отложите построение индексов, то во время вставки не будет никаких других обновлений.

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

При выполнении обновлений или удалений это мало что помогает.

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

С такой базой данных, как Ingres, вы всегда будете сталкиваться с некоторыми взаимоблокировками, поэтому вы должны предположить, что любая вставка, обновление или удаление завершатся неудачей, и использовать стратегию повторных попыток (как в вашем примере).Вы должны спроектировать свою базу данных таким образом, чтобы конфликты были сведены к минимуму, а взаимоблокировки возникали редко.Если вы постоянно получаете сбои транзакций даже после нескольких попыток, то это признак того, что вам придется провести некоторую серьезную переработку базы данных (или перейти на систему, подобную Oracle, где обычно возможно разрабатывать приложения, позволяющие избежать взаимоблокировок, используя подходящее блокирование на уровне строк).

как это ?

short attempts = 0;
boolean success = false;
long delayMs = 0;

Random random = new Random();
do {
try {
     synchronized(ClassName.class) {
         //insert loads of records in table 'x'
      }

    success = true;
} catch (ConcurrencyFailureException e) {
    attempts++;
    success = false;
    delayMs = 1000*attempts+random.nextInt(1000*attempts);

    try {
                    Thread.sleep(delayMs);
            } catch (InterruptedException ie) {
    }
  }
} while (!success);
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top