Aktualizacja: Potencjalne rozwiązanie poniżejPostgreSQL 9.3: Sharelock problem z prostego INSERT
Mam duży korpus plików konfiguracyjnych składających się z par klucz/wartość, które staram się wcisnąć do bazy danych. Wiele kluczy i wartości jest powtarzanych w plikach konfiguracyjnych, więc przechowuję dane za pomocą 3 tabel. Jedna dla wszystkich unikatowych wartości klucza, jedna dla wszystkich unikalnych wartości par i jedna dla wszystkich par klucz/wartość dla każdego pliku.
Problem:Korzystam z wielu równoległych procesów (a więc połączeń) w celu dodania nieprzetworzonych danych do bazy danych. Niestety otrzymuję wiele wykrytych zakleszczeń podczas próby dodania wartości do tabel kluczy i wartości. Mam wypróbowany kilka różnych metod wstawiania danych (pokazany poniżej), ale zawsze skończyć z „impasu wykrytego” Błąd
TransactionRollbackError: deadlock detected
DETAIL: Process 26755 waits for ShareLock on transaction 689456; blocked by process 26754. Process 26754 waits for ShareLock on transaction 689467; blocked by process 26755.
Zastanawiałem się, czy ktoś może rzucić nieco światła na dokładnie co może być przyczyną tych impasów i być może wskazał mi jakiś sposób rozwiązania problemu. Patrząc na instrukcje SQL, których używam (wymienione poniżej), nie widzę powodu, dla którego istnieje jakakolwiek współzależność.
Dzięki za przeczytanie!
Przykładowy plik konfiguracyjny:
example_key this_is_the_value
other_example other_value
third example yet_another_value
definicje Tabela:
CREATE TABLE keys (
id SERIAL PRIMARY KEY,
hash UUID UNIQUE NOT NULL,
key TEXT);
CREATE TABLE values (
id SERIAL PRIMARY KEY,
hash UUID UNIQUE NOT NULL,
key TEXT);
CREATE TABLE keyvalue_pairs (
id SERIAL PRIMARY KEY,
file_id INTEGER REFERENCES filenames,
key_id INTEGER REFERENCES keys,
value_id INTEGER REFERENCES values);
Oświadczenia SQL:
Początkowo starałem się użyć tego komunikatu, aby uniknąć jakichkolwiek wyjątków :
WITH s AS (
SELECT id, hash, key FROM keys
WHERE hash = 'hash_value';
), i AS (
INSERT INTO keys (hash, key)
SELECT 'hash_value', 'key_value'
WHERE NOT EXISTS (SELECT 1 FROM s)
returning id, hash, key
)
SELECT id, hash, key FROM i
UNION ALL
SELECT id, hash, key FROM s;
Ale nawet coś tak prostego jak to powoduje zakleszczenia:
INSERT INTO keys (hash, key)
VALUES ('hash_value', 'key_value')
RETURNING id;
- W obu przypadkach, jeśli otrzymuję wyjątek rzucony ponieważ wstawiony wartość hash nie jest wyjątkowa, używam punkty zapisu aby przywrócić zmianę i inne zdanie, aby wybrać id, którego szukam.
- używam skrótów dla wyjątkowej ostrości, jak niektórzy z kluczy i wartości są zbyt długo, aby być indeksowane
Pełny przykład kodu python (używając psycopg2) o punkty zapisu:
key_value = 'this_key'
hash_val = generate_uuid(value)
try:
cursor.execute(
'''
SAVEPOINT duplicate_hash_savepoint;
INSERT INTO keys (hash, key)
VALUES (%s, %s)
RETURNING id;
'''
(hash_val, key_value)
)
result = cursor.fetchone()[0]
cursor.execute('''RELEASE SAVEPOINT duplicate_hash_savepoint''')
return result
except psycopg2.IntegrityError as e:
cursor.execute(
'''
ROLLBACK TO SAVEPOINT duplicate_hash_savepoint;
'''
)
#TODO: Should ensure that values match and this isn't just
#a hash collision
cursor.execute(
'''
SELECT id FROM keys WHERE hash=%s LIMIT 1;
'''
(hash_val,)
)
return cursor.fetchone()[0]
Aktualizacja: Więc wierzę, że wskazówka na another stackexchange site:
Konkretnie:
UPDATE, DELETE, SELECT FOR UPDATE, and SELECT FOR SHARE commands behave the same as SELECT in terms of searching for target rows: they will only find target rows that were committed as of the command start time1. However, such a target row might have already been updated (or deleted or locked) by another concurrent transaction by the time it is found. In this case, the would-be updater will wait for the first updating transaction to commit or roll back (if it is still in progress). If the first updater rolls back, then its effects are negated and the second updater can proceed with updating the originally found row. If the first updater commits, the second updater will ignore the row if the first updater deleted it2, otherwise it will attempt to apply its operation to the updated version of the row.
A ja nadal nie jestem do końca pewien, gdzie współzależność jest, wydaje się, że przetwarzanie dużą liczbę par klucz/wartość bez popełnienia prawdopodobnie spowodować coś takiego. Rzeczywiście, jeśli zatwierdzę po dodaniu każdego pliku konfiguracyjnego, zakleszczenia nie występują.
Inną alternatywą byłoby wstawianie wartości zawsze w tej samej kolejności. –