Eu li muitos posts sobre esse tópico, mas nenhum deles fala sobre o banco de dados AWS RDS MySQL. Há três dias, estou executando um script python em uma instância AWS EC2 que grava linhas em meu banco de dados MySQL AWS RDS. Tenho que escrever 35 milhões de linhas, então sei que isso levará algum tempo. Periodicamente, verifico o desempenho do banco de dados e três dias depois (hoje) percebo que o banco de dados está ficando lento. Quando começou, as primeiras 100.000 linhas foram escritas em apenas 7 minutos (este é um exemplo das linhas com as quais estou trabalhando)
0000002178-14-000056 AccountsPayableCurrent us-gaap/2014 20131231 0 USD 266099000.0000
Após três dias, 5.385.662 linhas foram gravadas no banco de dados, mas agora são necessárias quase 3 horas para gravar 100.000 linhas. O que está acontecendo?
A instância EC2 que estou executando é a t2.small. Aqui você pode verificar as especificações se precisar:ESPECIFICAÇÕES EC2 . O banco de dados RDS que estou executando é o db.t2.small. Verifique as especificações aqui:ESPECIFICAÇÕES RDS
Anexarei aqui alguns gráficos sobre o desempenho do banco de dados e da instância EC2: CPU de banco de dados/Memória banco de dados/IOPS de gravação de banco de dados/Taxa de transferência de gravação de banco de dados/ Rede EC2 em (bytes)/Saída de rede EC2 (bytes)
Seria ótimo se você pudesse me ajudar. Muito obrigado.
EDIT 1: Como estou inserindo linhas? Como eu disse antes, tenho um script python rodando em uma instância EC2, esse script lê arquivos de texto, faz alguns cálculos com esses valores e depois grava cada "nova" linha no banco de dados. Aqui está um pequeno pedaço do meu código. Como leio os arquivos de texto?
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()
Eu sei que não há except:
como analisar as try
declarações, mas isso é apenas uma parte do roteiro. Acho que a parte importante é como insiro cada linha. Caso não precise fazer cálculos com os valores, utilizarei Load Data Infile
para gravar os arquivos texto no banco de dados. Acabei de perceber que talvez não seja uma boa ideia commit
sempre que insiro uma linha. Tentarei confirmar após 10.000 linhas ou mais.
Responder1
Instâncias T2 e T3 (incluindo instâncias db.t2 db.t3) usamCrédito de CPUsistema. Quando a instância está ociosa, ela acumula créditos de CPU que podem ser usados para funcionar mais rápido por curtos períodos de tempo -Desempenho de explosão. Depois de esgotar os créditos, ele fica lentoDesempenho de linha de base.
Uma opção é ativarT2/T3 Ilimitadoconfiguração na configuração do RDS que permitirá que a instância seja executada em velocidade máxima pelo tempo que for necessário, mas você pagará pelos créditos extras necessários.
A outra opção é alterar o tipo de instância para db.m5 ou algum outro tipo não-T2/T3 que suporte desempenho consistente.
Aqui está uma visão mais aprofundadaexplicação dos créditos da CPUe como eles são acumulados e gastos:Sobre o esclarecimento das condições de trabalho t2 e t3?
Espero que ajude :)
Responder2
As linhas únicas
INSERTs
são 10 vezes mais lentas que as de 100 linhasINSERTs
ouLOAD DATA
.UUIDs são lentos, especialmente quando a tabela fica grande.
UNIQUE
índices precisam ser verificadosantesterminando umiNSERT
.Não exclusivos
INDEXes
podem ser feitos em segundo plano, mas ainda assim exigem um pouco de carga.
Forneça SHOW CREATE TABLE
e o método usado para INSERTing
. Pode haver mais dicas.
Responder3
Cada vez que você confirma, um(s) índice(s) de transação precisa(m) ser atualizado(s). A complexidade de atualizar um índice está relacionada ao número de linhas da tabela, portanto, à medida que o número de linhas aumenta, a atualização do índice torna-se progressivamente mais lenta.
Supondo que você esteja usando tabelas InnoDB, você pode fazer o seguinte:
SET FOREIGN_KEY_CHECKS = 0;
SET UNIQUE_CHECKS = 0;
SET AUTOCOMMIT = 0;
ALTER TABLE table_name DISABLE KEYS;
Em seguida, faça as inserções, mas agrupe-as de modo que uma instrução insira (por exemplo) várias dezenas de linhas. Como INSERT INTO table_name VALUES ((<row1 data>), (<row2 data>), ...)
. Quando as inserções terminarem,
ALTER TABLE table_name ENABLE KEYS;
SET UNIQUE_CHECKS = 1;
SET FOREIGN_KEY_CHECKS = 1;
COMMIT;
Você pode ajustar isso para sua própria situação, por exemplo, se o número de linhas for enorme, talvez você queira inserir meio milhão e depois confirmar. Isso pressupõe que seu banco de dados não esteja 'ativo' (ou seja, usuários lendo/gravando ativamente nele) enquanto você está fazendo as inserções, porque você está desativando verificações nas quais você poderia confiar quando eles inserem dados.