Є кілька можливих сценаріїв, які легко вирішити, і згубний, який не є.
Для користувача, який вводить значення, а потім через деякий час вводить те саме значення, простий ВИБІР, перш ніж INSERT виявить проблему. Це працює у випадку, коли один користувач подає значення, а через деякий час інший користувач подає те саме значення.
Якщо користувач подає список значень з дублікатами - скажімо, {ABC, DEF, ABC} - за один виклик коду програма може виявити та фільтрувати дублікати, можливо, видаючи помилку. Також вам потрібно буде перевірити, чи БД не містить жодного з унікальних значень перед вставкою.
Складний сценарій полягає в тому, коли запис одного користувача знаходиться всередині СУБД одночасно з записом іншого користувача, і вони записують те саме значення. Тоді у вас є гонка умова між ними. Оскільки СУБД є (швидше за все, ви не говорите, яку саме з них використовуєте), попереджувальна система багатозадачності, будь-яке завдання можна призупинити в будь-який момент її виконання. Це означає, що завдання user1 може перевірити відсутність рядка, потім завдання user2 може перевірити, що немає рядка, тоді завдання user1 може вставити цей рядок, тоді завдання user2 може вставити цей рядок. У кожному пункті завдання поодинці задоволені, що роблять правильно. Однак глобально виникає помилка.
Зазвичай СУБД обробляє це, ставлячи блокування на значення, про яке йдеться. У цій проблемі ви створюєте новий рядок, щоб не було нічого для блокування. Відповідь - блокування діапазону. Як це передбачає, це блокує діапазон значень, незалежно від того, існують вони зараз чи ні. Після блокування цього діапазону неможливо отримати інше завдання, поки блокування не буде звільнено. Щоб отримати блокування діапазону, вам потрібно вказати та рівень ізоляції SERIALIZABLE . Явище чергового завдання, що пробирається поспіль після перевірки вашої задачі, відоме як фантомні записи .
Встановлення рівня ізоляції в Serializable для всієї програми матиме наслідки. Пропускна здатність буде зменшена. Інші умови гонки, які працювали досить добре в минулому, можуть почати показувати помилки вже зараз. Я б запропонував встановити його на з'єднання, яке виконує ваш код, що викликає дублікат, а решту програми залишає такою, якою є.
Альтернативою на основі коду є перевірка після запису, а не раніше. Тож зробіть ВСТУП, потім порахуйте кількість рядків, які мають це хеш-значення. Якщо є дублікати відкату, дія. Це може мати певні збоки. Скажіть, завдання 1 пише потім завдання 2. Потім завдання 1 перевіряє і знаходить дублікат. Він котиться назад, хоча це було першим. Аналогічно обидві завдання можуть виявити дублікат і обидва відкати. Але принаймні у вас з’явиться повідомлення, з яким потрібно працювати, механізм повторного повторення та відсутність нових копій. Відкази нахмурені, як і винятки для контролю над програмою. Зауважте, що всіробота в транзакції буде повернута назад, а не лише запис, що викликає дублікат. І вам доведеться мати явні транзакції, які можуть зменшити паралельність. Перевірка дублікатів буде жахливо повільною, якщо у вас немає індексу на хеш. Якщо ви це зробите, ви можете зробити його унікальним!
Як ви прокоментували, реальне рішення - це унікальний індекс. Мені здається, що це повинно вписуватися у ваше вікно технічного обслуговування (хоча, звичайно, ви найкраще знаєте свою систему). Скажіть, хеш - вісім байтів. На сто мільйонів рядків це близько 1 ГБ. Досвід показує, що розумний шматочок обладнання обробить ці рядки за хвилину-дві, вершини. Дублікація перевірки та усунення додасть цього, але може бути заздалегідь написана сценарієм. Це, однак, лише бік.