Update: soluzione potenziale al di sottoPostgres 9.3: problema ShareLock con semplice INSERT
Ho un grande corpus di file di configurazione, comprensivi di coppie chiave/valore che sto cercando di spingere in un database. Molte chiavi e valori sono ripetuti attraverso i file di configurazione, quindi sto memorizzando i dati utilizzando 3 tabelle. Uno per tutti i valori chiave univoci, uno per tutti i valori di coppia univoci e un elenco di tutte le coppie chiave/valore per ogni file.
Problema: Sto utilizzando più processi simultanei (e quindi connessioni) per aggiungere i dati grezzi nel database. Sfortunatamente ottengo un sacco di deadlock rilevati quando provo ad aggiungere valori alla chiave e alle tabelle dei valori. Ho un provato alcuni metodi diversi di inserimento dati (vedi sotto), ma sempre finire con un "deadlock rilevato" Errore
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.
mi chiedevo se qualcuno potrebbe far luce su esattamente ciò potrebbe causare questi deadlock e possibilmente mi indicano un qualche modo di risolvere il problema. Osservando le istruzioni SQL che sto usando (elencate di seguito), non vedo per quale motivo vi sia alcuna co-dipendenza.
Grazie per la lettura!
Esempio file di configurazione:
example_key this_is_the_value
other_example other_value
third example yet_another_value
definizioni Tabella:
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);
istruzioni SQL:
Inizialmente stavo cercando di utilizzare questa istruzione per evitare eventuali eccezioni :
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;
Ma anche qualcosa di semplice come questo fa sì che le situazioni di stallo:
INSERT INTO keys (hash, key)
VALUES ('hash_value', 'key_value')
RETURNING id;
- In entrambi i casi, se ricevo un eccezione generata poiché il valore hash inserito non è unico, io uso i punti di salvataggio per ripristinare la modifica e un'altra istruzione per selezionare solo l'ID che sto cercando.
- sto usando gli hash per il campo unico, come alcune delle chiavi e valori sono troppo lunghi per essere indicizzati
completa esempio del codice Python (usando psycopg2) con i punti di salvataggio:
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]
Aggiornamento: quindi credo che un suggerimento su another stackexchange site:
In particolare:
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.
Mentre io non sono ancora esattamente sicuro dove il co-dipendenza è, sembra che l'elaborazione di un gran numero di coppie chiave/valore, senza commettere sarebbe probabilmente porterà a qualcosa di simile. Abbastanza sicuro, se mi impegno dopo ogni singolo file di configurazione viene aggiunto, i deadlock non si verificano.
Un'altra alternativa sarebbe inserire i valori sempre nello stesso ordine. –