Які можливі способи уникнути дублікатів, коли ви не можете додати унікальний індекс


10

Я застряг у проблемі одночасності.

Це типова проблема, коли користувач надсилає 2 або 3 транзакції, щоб зберегти деякі дані, які НЕ БУДУТЬ дублюватись у БД, у випадку дублювання запису ви повинні повернути помилку.

Ця проблема проста, коли ви можете просто додати індекс (унікальний) до стовпця, де ви зберігаєте хеш.

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

Насправді у нас стовпчик, де ми зберігаємо хеш даних, який не слід дублювати, але не встановлено унікальний індекс.

Я пробую свій код Java, щоб перевірити, чи існує він безпосередньо перед флеш, все ще отримую дублікати.

Мої можливі рішення для цього:

  • Створіть тригер, який перевіряє, чи є хеш, який я намагаюся вставити, вже існує на столі.
  • Створіть іншу таблицю для зберігання унікальних індексів для цієї таблиці та додайте іноземний ключ до головної таблиці.
  • Сидіти на положенні плода і плакати

Чи не вдалося перевірити хеш через зіткнення хешу або помилку в чеку?
candied_orange

4
Я не отримав вашого запитання. Отже, замість того, щоб індексувати один раз всю величезну таблицю з мільйонами записів, ви вважаєте за краще прочитати для кожного наступного мільйона записів, які ви додасте, існуючих мільйонів, щоб шукати парні? або дублювати якусь інформацію та додавати приєднання, щоб зробити чек?
Крістоф

Проблема полягає в тому, що для внесення змін мене попередили, що нам потрібно багато місця та тривалий час простою для нашої служби, щоб виконати деякі вимоги, що наша служба не може працювати більше 2 годин щомісяця. Я знаю, що найкращий спосіб - це виконувати сервісне обслуговування за цим столом, але це я не можу зробити на даний момент, тому нам потрібен спосіб вирішення.
rafuru

4
Я не розумію - чому додавання тригера або додавання іншої таблиці для "емуляції" індексу займає менше часу простою, ніж просто додавання індексу до існуючої таблиці?
Док Браун

2
@rafuru: хто сказав, що вам потрібно створити унікальний індекс? Стандартний, унікальний індекс, ймовірно, все, що вам потрібно, щоб швидко знайти всі рядки з однаковим хеш-значенням.
Док Браун

Відповіді:


3

Є кілька можливих сценаріїв, які легко вирішити, і згубний, який не є.

Для користувача, який вводить значення, а потім через деякий час вводить те саме значення, простий ВИБІР, перш ніж INSERT виявить проблему. Це працює у випадку, коли один користувач подає значення, а через деякий час інший користувач подає те саме значення.

Якщо користувач подає список значень з дублікатами - скажімо, {ABC, DEF, ABC} - за один виклик коду програма може виявити та фільтрувати дублікати, можливо, видаючи помилку. Також вам потрібно буде перевірити, чи БД не містить жодного з унікальних значень перед вставкою.

Складний сценарій полягає в тому, коли запис одного користувача знаходиться всередині СУБД одночасно з записом іншого користувача, і вони записують те саме значення. Тоді у вас є гонка умова між ними. Оскільки СУБД є (швидше за все, ви не говорите, яку саме з них використовуєте), попереджувальна система багатозадачності, будь-яке завдання можна призупинити в будь-який момент її виконання. Це означає, що завдання user1 може перевірити відсутність рядка, потім завдання user2 може перевірити, що немає рядка, тоді завдання user1 може вставити цей рядок, тоді завдання user2 може вставити цей рядок. У кожному пункті завдання поодинці задоволені, що роблять правильно. Однак глобально виникає помилка.

Зазвичай СУБД обробляє це, ставлячи блокування на значення, про яке йдеться. У цій проблемі ви створюєте новий рядок, щоб не було нічого для блокування. Відповідь - блокування діапазону. Як це передбачає, це блокує діапазон значень, незалежно від того, існують вони зараз чи ні. Після блокування цього діапазону неможливо отримати інше завдання, поки блокування не буде звільнено. Щоб отримати блокування діапазону, вам потрібно вказати та рівень ізоляції SERIALIZABLE . Явище чергового завдання, що пробирається поспіль після перевірки вашої задачі, відоме як фантомні записи .

Встановлення рівня ізоляції в Serializable для всієї програми матиме наслідки. Пропускна здатність буде зменшена. Інші умови гонки, які працювали досить добре в минулому, можуть почати показувати помилки вже зараз. Я б запропонував встановити його на з'єднання, яке виконує ваш код, що викликає дублікат, а решту програми залишає такою, якою є.

Альтернативою на основі коду є перевірка після запису, а не раніше. Тож зробіть ВСТУП, потім порахуйте кількість рядків, які мають це хеш-значення. Якщо є дублікати відкату, дія. Це може мати певні збоки. Скажіть, завдання 1 пише потім завдання 2. Потім завдання 1 перевіряє і знаходить дублікат. Він котиться назад, хоча це було першим. Аналогічно обидві завдання можуть виявити дублікат і обидва відкати. Але принаймні у вас з’явиться повідомлення, з яким потрібно працювати, механізм повторного повторення та відсутність нових копій. Відкази нахмурені, як і винятки для контролю над програмою. Зауважте, що всіробота в транзакції буде повернута назад, а не лише запис, що викликає дублікат. І вам доведеться мати явні транзакції, які можуть зменшити паралельність. Перевірка дублікатів буде жахливо повільною, якщо у вас немає індексу на хеш. Якщо ви це зробите, ви можете зробити його унікальним!

Як ви прокоментували, реальне рішення - це унікальний індекс. Мені здається, що це повинно вписуватися у ваше вікно технічного обслуговування (хоча, звичайно, ви найкраще знаєте свою систему). Скажіть, хеш - вісім байтів. На сто мільйонів рядків це близько 1 ГБ. Досвід показує, що розумний шматочок обладнання обробить ці рядки за хвилину-дві, вершини. Дублікація перевірки та усунення додасть цього, але може бути заздалегідь написана сценарієм. Це, однак, лише бік.


2

Насправді у нас стовпчик, де ми зберігаємо хеш даних, який не слід дублювати, але не встановлено унікальний індекс.

Перевірка хеш-зіткнень є хорошим першим кроком, але будьте обережні, ви не можете гарантувати, що однакова програма буде створювати той самий хеш для тих самих даних, якщо вона буде перезапущена . Багато "швидких" хеш-функцій використовують вбудований prng, який висівають у час запуску програми. Використовуйте криптографічний хеш, якщо хеш завжди повинен бути однаковим незалежно від того, що ви робите в цій програмі. Зверніть увагу, що вам не потрібен хороший або захищений криптографічний хеш.

Другий крок - це фактично перевірити рівність даних, оскільки навіть найкращі хеш-функції іноді призводять до зіткнень, оскільки ви (як правило) зменшуєте ентропію своїх даних.

Тому:

Крок 1. Перевірте, чи не зіткнулося ви з криптографічним хешем

Крок 2: якщо хеші відповідають, перевіряють, чи фактичні дані однакові


Я не бачу, як це відповідає на питання. Припустимо на мить наявний хеш-стовпчик заповнюється детермінованою хеш-функцією (інакше будь-яка спроба використовувати його не мала б сенсу). Наскільки я розумію, проблема полягає в тому, що в базі даних немає індексу цього хеш-стовпця, тому навіть перший крок у вашій відповіді - перевірка наявності зіткнення - все одно вимагатиме повного сканування таблиці для кожної нової записи в таблиці з кілька мільйонів записів, які, мабуть, стануть занадто повільними.
Док Браун

Це найкраще, що можна зробити, не створюючи індексу, саме до цього ставилося питання. Принаймні сканування хешу означає, що вам потрібно перевірити лише один стовпець, що набагато швидше, ніж перевірка, проте скільки стовпців вони б інакше мали перевірити.
Турксарама

Я впевнений, що навіть коли створення індексу неможливо (що в цьому випадку, мабуть, є), оригінальна пропозиція ОП " створити іншу таблицю для зберігання унікальних індексів для цієї таблиці та додати іноземний ключ до основної таблиці " робить багато більше сенсу.
Док Браун

Детермінований хеш і криптографічний хеш - це два ортогональних поняття, чи не так? криптографічний хеш може бути не детермінованим, і навпаки, детермінований хеш може не мати криптографічної сили.
ньютопчанин

Вони не те саме, але вони не є ортогональними. Криптографічні хеші - це підмножина детермінованих хешів, але ніхто насправді не заважає робити некриптографічні детерміновані хеші, якщо ви конкретно не хочете, щоб воно з якихось причин було реверсивним.
Турксарама

2

Створіть нову таблицю з унікальним первинним ключем

На стороні клієнта починайте генерувати GUID для кожної записи, щоб ви могли виявити прості повтори.

Покладіть нові записи в нову таблицю, щоб ви принаймні були хороші для нових даних, що надходять.

Майте стовпець у новій таблиці "CheckedAgainstOldData"

У вас є завдання, що робить те, що ви виконуєте повільно перевірити хеш, щоб перевірити, чи зможе він знайти дублікат у старих даних і відповідно встановити прапор, відхилити дублікати в цей момент, відправивши повідомлення клієнту.

Тим часом є ще одне завдання, яке переміщує дані зі старої до нової таблиці, перевіряючи наявність дублікатів за допомогою хеш-перевірки та генеруючи GUID.

Ви можете залишити це завдання на кілька днів (якщо потрібно), переносячи дані без простоїв.

Після завершення передачі можна вимкнути повільний процес "CheckedAgainstOldData". і перенести всі дані в одну таблицю.

Відверто кажучи, якщо проблема настільки ж погана, як ви описуєте, а програмне забезпечення старе, тоді у вас буде тисячі копій.


1

Якщо припустити, що дані, що надходять від "користувача", означають, що хтось сидить за клавіатурою і що обдурювання виникає у двох користувачів, що вводять ті самі дані в один і той же момент. Спробуйте додати функцію, яка спричиняє випадкову затримку на початку тригера. Дайте йому мінімум, скільки часу потрібно, щоб написати новий запис до таблиці і, мабуть, максимум не більше ніж наноцентрик або близько того. Таким чином, коли ви отримуєте запити на обдурювання, слід зробити перше, а тригер існування повинен відбити правильний результат. (Уточнення: кожен виклик повинен мати свій унікальний час випадкової затримки, за тими ж принципами, що й протокол ALOHA )

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.