Istnieje kilka możliwych scenariuszy, które są łatwe do rozwiązania, i zgubny, który nie jest.
Dla użytkownika, który wprowadza wartość, a następnie wprowadza tę samą wartość jakiś czas później prosty WYBÓR, zanim INSERT wykryje problem. Działa to w przypadku, gdy jeden użytkownik przesyła wartość, a jakiś czas później inny użytkownik przesyła tę samą wartość.
Jeśli użytkownik prześle listę wartości z duplikatami - powiedzmy {ABC, DEF, ABC} - w jednym wywołaniu kodu aplikacja może wykryć i filtrować duplikaty, być może zgłaszając błąd. Musisz także sprawdzić, czy DB nie zawiera żadnych unikalnych wartości przed wstawieniem.
Trudny scenariusz polega na tym, że zapis jednego użytkownika znajduje się w DBMS w tym samym czasie, co zapis innego użytkownika i zapisują tę samą wartość. Potem masz wyścig między nimi. Ponieważ DBMS jest (najprawdopodobniej - nie mówisz, którego używasz) prewencyjnym systemem wielozadaniowym, każde zadanie może zostać wstrzymane w dowolnym momencie jego wykonywania. Oznacza to, że zadanie użytkownika 1 może sprawdzić, czy nie ma istniejącego wiersza, następnie zadanie użytkownika 2 może sprawdzić, czy nie ma istniejącego wiersza, następnie zadanie użytkownika 1 może wstawić ten wiersz, a następnie zadanie użytkownika 2 może wstawić ten wiersz. W każdym punkcie zadania są indywidualnie zadowolone, że robią dobrze. Jednak globalnie występuje błąd.
Zwykle DBMS poradziłby sobie z tym, blokując daną wartość. W tym problemie tworzysz nowy wiersz, więc nie ma jeszcze nic do zablokowania. Odpowiedzią jest blokada zasięgu. Jak sugeruje, blokuje to zakres wartości, niezależnie od tego, czy obecnie istnieją, czy nie. Po zablokowaniu dostęp do tego zakresu nie będzie możliwy przez inne zadanie, dopóki blokada nie zostanie zwolniona. Aby uzyskać blokady zasięgu, musisz określić i poziom izolacji opcji SERIALIZABLE . Zjawisko skradania się innego zadania z rzędu po sprawdzeniu zadania jest znane jako rekordy fantomowe .
Ustawienie poziomu izolacji na Serializable w całej aplikacji będzie miało implikacje. Przepustowość zostanie zmniejszona. Inne warunki wyścigowe, które w przeszłości działały wystarczająco dobrze, mogą teraz zacząć wykazywać błędy. Sugerowałbym ustawienie go na połączenie, które wykonuje kod wywołujący duplikaty i pozostawienie pozostałej części aplikacji bez zmian.
Alternatywą opartą na kodzie jest sprawdzenie po zapisie zamiast wcześniej. Podobnie INSERT, a następnie policz liczbę wierszy o tej wartości skrótu. Jeśli istnieją duplikaty, wycofaj działanie. Może to mieć pewne przewrotne wyniki. Powiedz, że zadanie 1 zapisuje, a następnie zadanie 2. Następnie zadanie 1 sprawdza i znajduje duplikat. Cofa się, mimo że był pierwszy. Podobnie oba zadania mogą wykryć duplikat i oba wycofać. Ale przynajmniej będziesz mieć wiadomość do pracy, mechanizm ponownej próby i żadnych nowych duplikatów. Wycofywanie jest marszczone, podobnie jak stosowanie wyjątków do kontrolowania przebiegu programu. Zauważ, że wszyscypraca nad transakcją zostanie wycofana, a nie tylko zapis wywołujący duplikaty. I będziesz musiał mieć wyraźne transakcje, które mogą zmniejszyć współbieżność. Powtórzenie kontroli będzie strasznie wolne, chyba że masz indeks w haszowaniu. Jeśli to zrobisz, równie dobrze możesz uczynić go wyjątkowym!
Jak skomentowałeś, prawdziwym rozwiązaniem jest unikalny indeks. Wydaje mi się, że powinno to pasować do okna konserwacji (choć oczywiście najlepiej znasz swój system). Powiedzmy, że hash ma osiem bajtów. Na sto milionów wierszy to około 1 GB. Doświadczenie sugeruje, że rozsądna ilość sprzętu przetwarzałaby te wiersze w ciągu minuty lub dwóch. Powielanie sprawdzania i eliminacji doda do tego, ale może być wcześniej napisane w skrypcie. To jednak tylko na bok.