AWS RDS MySQL rallenta nel tempo

Aug 24 2020

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 tryaffermazioni, 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 Infileper scrivere i file di testo nel database. Mi rendo solo conto di sapere che forse non è una buona idea commitogni volta che inserisco una riga. Proverò a eseguire il commit dopo 10.000 righe o giù di lì.

Risposte

11 MLu Aug 24 2020 at 06:03

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 :)

9 RickJames Aug 24 2020 at 07:09
  • Le righe singole INSERTssono 10 volte più lente di 100 righe INSERTso LOAD DATA.

  • Gli UUID sono lenti, soprattutto quando la tabella diventa grande.

  • UNIQUEgli indici devono essere controllati prima di terminare un file iNSERT.

  • Non univoco INDEXespuò essere eseguito in background, ma richiedono comunque un certo carico.

Si prega di fornire SHOW CREATE TABLEe il metodo utilizzato per INSERTing. Potrebbero esserci altri suggerimenti.

7 tater Aug 23 2020 at 23:07

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.