Domanda

Ho una tabella DB in cui ogni riga ha una chiave primaria generata casualmente, un messaggio e un utente. Ogni utente ha circa 10-100 messaggi ma ci sono 10k-50k utenti.

Scrivo i messaggi ogni giorno per ogni utente in una volta sola. Voglio eliminare i vecchi messaggi per ogni utente prima di scrivere quelli nuovi per mantenere il tavolo il più piccolo possibile.

In questo momento lo faccio efficacemente:

delete from table where user='mk'

Quindi scrivi tutti i messaggi per quell'utente. Vedo molte contese perché ho molti thread che lo fanno allo stesso tempo.

Ho un requisito aggiuntivo per conservare il set di messaggi più recente per ciascun utente.

Non ho accesso diretto al DB. Sto cercando di indovinare il problema sulla base di alcuni feedback di seconda mano. Il motivo per cui mi sto concentrando su questo scenario è che la query di eliminazione sta mostrando molto tempo di attesa (di nuovo - per quanto ne so) e in più è un po 'di funzionalità aggiunta di recente.

Qualcuno può offrire qualche consiglio?

Sarebbe meglio:

select key from table where user='mk'

Quindi eliminare singole righe da lì? Sto pensando che potrebbe portare a un blocco meno brutale.

È stato utile?

Soluzione

No, è sempre meglio eseguire una singola istruzione SQL su un set di righe piuttosto che una serie di "riga per riga" (o ciò che Tom Kyte chiama operazioni "slow-by-slow"). Quando dici che stai vedendo molta contesa, cosa vedi esattamente? Una domanda ovvia: la colonna USER è indicizzata?

(Ovviamente, il nome della colonna non può davvero essere USER in un database Oracle, poiché è una parola riservata!)

MODIFICA: hai detto che la colonna USER non è indicizzata. Ciò significa che ogni eliminazione comporterà una scansione completa della tabella fino a 50 K * 100 = 5 milioni di righe (o nella migliore delle ipotesi 10 K * 10 = 100.000 righe) per eliminare solo 10-100 righe. L'aggiunta di un indice su USER può risolvere i tuoi problemi.

Altri suggerimenti

Se lo fai ogni giorno per ogni utente, perché non eliminare tutti i record dalla tabella in una singola istruzione? O anche

truncate table whatever reuse storage
/

modifica

Il motivo per cui suggerisco questo approccio è che il processo si presenta come un caricamento batch giornaliero di messaggi utente preceduto da una cancellazione dei vecchi messaggi. Cioè, le regole commerciali mi sembrano essere "la tabella conterrà solo un giorno di messaggi per un dato utente". Se questo processo viene eseguito per ogni utente, una singola operazione sarebbe la più efficiente.

Tuttavia, se gli utenti non ricevono un nuovo set di messaggi ogni giorno e esiste una regola sussidiaria che ci impone di conservare il set più recente di messaggi per ciascun utente, quindi lo zapping dell'intera tabella sbagliarsi.

Sei sicuro di vedere un conflitto di blocco? Sembra più probabile che tu stia riscontrando contese sul disco a causa di troppi aggiornamenti simultanei (ma non correlati). La soluzione a ciò è semplicemente quella di ridurre il numero di thread che stai usando: meno contesa sul disco significherà un throughput totale più elevato.

Penso che tu debba definire le tue esigenze in modo un po 'più chiaro ...

Ad esempio. Se conosci tutti gli utenti per i quali desideri scrivere messaggi, inserisci gli ID in una tabella temporanea, indicizzali su ID ed eliminazione batch. Quindi i fili che stai sparando stanno facendo due cose. Scrivi l'ID dell'utente su una tabella temporanea, Scrivi il messaggio su un'altra tabella temporanea. Quindi quando i thread hanno terminato l'esecuzione, il thread principale dovrebbe

DELETE * FROM Messaggi INNER JOIN TEMP_MEMBERS ON ID = TEMP_ID

INSERISCI NEI MESSAGGI SELEZIONA * DA TEMP_messges

Non ho familiarità con la sintassi Oracle, ma è così che mi avvicinerei se i messaggi degli utenti venissero tutti eseguiti in rapida successione.

Spero che questo aiuti

PARLA CON IL TUO DBA

Lui è lì per aiutarti. Quando noi DBA togliiamo l'accesso agli sviluppatori per qualcosa del genere, si presume che forniremo il supporto per tale compito. Se il completamento del codice richiede troppo tempo e il tempo sembra essere limitato nel database, il DBA sarà in grado di guardare esattamente cosa sta succedendo e offrire suggerimenti o eventualmente anche risolvere il problema senza cambiare nulla.

Dando solo un'occhiata alla tua affermazione sul problema, non sembra che tu stia esaminando i problemi di contesa, ma non so nulla della tua struttura sottostante.

Davvero, parla con il tuo DBA. Probabilmente si divertirà a guardare qualcosa di divertente invece di pianificare l'ultima distribuzione della CPU.

Questo potrebbe accelerare le cose:

Crea una tabella di ricerca:

create table rowid_table (row_id ROWID ,user VARCHAR2(100));
create index rowid_table_ix1 on rowid_table (user);

Esegui un lavoro notturno:

truncate table rowid_table;
insert /*+ append */ into rowid_table
select ROWID row_id , user
from table;
dbms_stats.gather_table_stats('SCHEMAOWNER','ROWID_TABLE');

Quindi, quando si eliminano i record:

delete from table
where ROWID IN (select row_id
                from rowid_table
                where user = 'mk');

Il tuo suggerimento sembra molto ragionevole. Il blocco in piccoli lotti ha due vantaggi:

  • le transazioni saranno più piccole
  • il blocco sarà limitato a poche righe alla volta

Il blocco in batch dovrebbe essere un grande miglioramento.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top