AWS RDS MySQL rallenta nel tempo
Ho letto molti post su questo argomento, ma nessuno di loro parla del database MySQL di AWS RDS. Da tre giorni eseguo uno script Python in un'istanza AWS EC2 che scrive righe nel mio database MySQL di AWS RDS. Devo scrivere 35 milioni di righe, quindi so che ci vorrà del tempo. Periodicamente, controllo le prestazioni del database e tre giorni dopo (oggi) mi accorgo che il database sta rallentando. Quando è iniziato, le prime 100.000 righe sono state scritte in soli 7 minuti (questo è un esempio delle righe con cui sto lavorando)
0000002178-14-000056 AccountsPayableCurrent us-gaap/2014 20131231 0 USD 266099000.0000
Dopo tre giorni, nel database sono state scritte 5.385.662 righe, ma ora sono necessarie quasi 3 ore per scrivere 100.000 righe. Che cosa sta succedendo?
L'istanza EC2 che sto eseguendo è t2.small. Qui puoi controllare le specifiche se ne hai bisogno: EC2 SPECS . Il database RDS che sto eseguendo è db.t2.small. Controlla le specifiche qui: RDS SPECS
Allegherò qui alcuni grafici sulle prestazioni del database e dell'istanza EC2: Db CPU / Db Memory / Db Write IOPS / Db Write Throughput / EC2 Network in (bytes) / EC2 Network out (bytes)
Sarebbe fantastico se tu potessi aiutarmi. Molte grazie.
EDIT 1: come inserisco le righe? Come ho detto prima, ho uno script Python in esecuzione su un'istanza EC2, questo script legge file di testo, effettua alcuni calcoli con questi valori e quindi scrive ogni "nuova" riga nel database. Ecco una piccola parte del mio codice. Come leggo i file di testo?
for i in path_list:
notify("Uploading: " + i)
num_path = "path/" + i + "/file.txt"
sub_path = "path/" + i + "/file.txt"
try:
sub_dict = {}
with open(sub_path) as sub_file:
for line in sub_file:
line = line.strip().split("\t")
sub_dict[line[0]] = line[1] # Save cik for every accession number
sub_dict[line[1] + "-report"] = line[25] # Save report type for every CIK
sub_dict[line[1] + "-frecuency"] = line[28] # Save frecuency for every CIK
with open(num_path) as num_file:
for line in num_file:
num_row = line.strip().split("\t")
# Reminder: sometimes in the very old reports, cik and accession number does not match. For this reason I have to write
# the following statement. To save the real cik.
try:
cik = sub_dict[num_row[0]]
except:
cik = num_row[0][0:10]
try: # If there is no value, pass
value = num_row[7]
values_dict = {
'cik': cik,
'accession': num_row[0][10::].replace("-", ""),
'tag': num_row[1],
'value': value,
'valueid': num_row[6],
'date': num_row[4]
}
sql = ("INSERT INTO table name (id, tag, value_num, value_id, endtime, cik, report, period) "
"VALUES ('{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}', '{}')".format(
values_dict['cik'] + values_dict['accession'] + values_dict['date'] + values_dict['value'].split(".")[0] + "-" + values_dict['tag'],
values_dict['tag'],
float(values_dict['value']),
values_dict['valueid'],
values_dict['date'],
int(values_dict['cik']),
sub_dict[values_dict['cik'] + "-report"],
sub_dict[values_dict['cik'] + "-frecuency"]
))
cursor.execute(sql)
connection.commit()
So che non c'è modo except:
di cogliere le try
affermazioni, ma questo è solo un pezzo della sceneggiatura. Penso che la parte importante sia come inserisco ogni riga. Nel caso in cui non avessi bisogno di fare calcoli con i valori, userò Load Data Infile
per scrivere i file di testo nel database. Mi rendo solo conto di sapere che forse non è una buona idea commit
ogni volta che inserisco una riga. Proverò a eseguire il commit dopo 10.000 righe o giù di lì.
Risposte
Le istanze T2 e T3 (incluse le istanze db.t2 db.t3) utilizzano il sistema di credito CPU . Quando l'istanza è inattiva, accumula crediti CPU che può quindi utilizzare per eseguire più velocemente per brevi periodi di tempo: prestazioni Burst . Una volta esauriti i crediti, rallenta fino a raggiungere una prestazione di base .
Un'opzione è abilitare l' impostazione T2 / T3 Unlimited nella configurazione RDS che consentirà all'istanza di funzionare a piena velocità per tutto il tempo necessario, ma pagherai per i crediti extra necessari.
L'altra opzione è modificare il tipo di istanza in db.m5 o un altro tipo non T2 / T3 che supporti prestazioni coerenti.
Ecco una spiegazione più approfondita dei crediti CPU e di come vengono maturati e spesi: Chiarire le condizioni di lavoro t2 e t3?
Spero possa aiutare :)
Le righe singole
INSERTs
sono 10 volte più lente di 100 righeINSERTs
oLOAD DATA
.Gli UUID sono lenti, soprattutto quando la tabella diventa grande.
UNIQUE
gli indici devono essere controllati prima di terminare un fileiNSERT
.Non univoco
INDEXes
può essere eseguito in background, ma richiedono comunque un certo carico.
Si prega di fornire SHOW CREATE TABLE
e il metodo utilizzato per INSERTing
. Potrebbero esserci altri suggerimenti.
Ogni volta che effettui il commit, uno o più indici di transazione devono essere aggiornati. La complessità dell'aggiornamento di un indice è correlata al numero di righe nella tabella, quindi all'aumentare del numero di righe, l'aggiornamento dell'indice diventa progressivamente più lento.
Supponendo che tu stia utilizzando le tabelle InnoDB, puoi fare quanto segue:
SET FOREIGN_KEY_CHECKS = 0;
SET UNIQUE_CHECKS = 0;
SET AUTOCOMMIT = 0;
ALTER TABLE table_name DISABLE KEYS;
Quindi fai gli inserimenti, ma raggruppali in modo che un'istruzione inserisca (ad esempio) diverse dozzine di righe. Mi piace INSERT INTO table_name VALUES ((<row1 data>), (<row2 data>), ...)
. Quando gli inserti sono finiti,
ALTER TABLE table_name ENABLE KEYS;
SET UNIQUE_CHECKS = 1;
SET FOREIGN_KEY_CHECKS = 1;
COMMIT;
Puoi adattarlo alla tua situazione, ad esempio se il numero di righe è enorme, forse vuoi inserire mezzo milione e poi eseguire il commit. Questo presume che il tuo database non sia "live" (cioè gli utenti che leggono / scrivono attivamente su di esso) mentre stai facendo gli inserimenti, perché stai disabilitando i controlli su cui potresti fare affidamento altrimenti quando immettono dati.