TL;DR:
LOAD DATA INFILE é uma ordem de magnitude mais rápida que vários INSERT instruções, que são uma ordem de magnitude mais rápidas do que um único INSERT declarações. Comparo abaixo as três principais estratégias para importar dados do R para o Mysql:
-
únicoinsertdeclarações , como na pergunta:
INSERT INTO test (col1,col2,col3) VALUES (1,2,3)
-
váriosinsertdeclarações , formatado assim:
INSERT INTO test (col1,col2,col3) VALUES (1,2,3),(4,5,6),(7,8,9)
-
load data infiledeclaração , ou seja, carregar um arquivo CSV escrito anteriormente emmysql:
LOAD DATA INFILE 'the_dump.csv' INTO TABLE test
Eu uso
RMySQL aqui, mas qualquer outro driver mysql deve levar a resultados semelhantes. A tabela SQL foi instanciada com:CREATE TABLE `test` (
`col1` double, `col2` double, `col3` double, `col4` double, `col5` double
) ENGINE=MyISAM;
Os dados de conexão e teste foram criados em
R com:library(RMySQL)
con = dbConnect(MySQL(),
user = 'the_user',
password = 'the_password',
host = '127.0.0.1',
dbname='test')
n_rows = 1000000 # number of tuples
n_cols = 5 # number of fields
dump = matrix(runif(n_rows*n_cols), ncol=n_cols, nrow=n_rows)
colnames(dump) = paste0('col',1:n_cols)
Benchmarking único
insert declarações: before = Sys.time()
for (i in 1:nrow(dump)) {
query = paste0('INSERT INTO test (',paste0(colnames(dump),collapse = ','),') VALUES (',paste0(dump[i,],collapse = ','),');')
dbExecute(con, query)
}
time_naive = Sys.time() - before
=> isso leva cerca de 4 minutos no meu computador
Comparando com vários
insert declarações: before = Sys.time()
chunksize = 10000 # arbitrary chunk size
for (i in 1:ceiling(nrow(dump)/chunksize)) {
query = paste0('INSERT INTO test (',paste0(colnames(dump),collapse = ','),') VALUES ')
vals = NULL
for (j in 1:chunksize) {
k = (i-1)*chunksize+j
if (k <= nrow(dump)) {
vals[j] = paste0('(', paste0(dump[k,],collapse = ','), ')')
}
}
query = paste0(query, paste0(vals,collapse=','))
dbExecute(con, query)
}
time_chunked = Sys.time() - before
=> isso leva cerca de 40 segundos no meu computador
Benchmarking
load data infile declaração :before = Sys.time()
write.table(dump, 'the_dump.csv',
row.names = F, col.names=F, sep='\t')
query = "LOAD DATA INFILE 'the_dump.csv' INTO TABLE test"
dbSendStatement(con, query)
time_infile = Sys.time() - before
=> isso leva cerca de 4 segundos no meu computador
Criar sua consulta SQL para lidar com muitos valores de inserção é a maneira mais simples de melhorar o desempenho. Transição para
LOAD DATA INFILE levará a ótimos resultados. Dicas de bom desempenho podem ser encontradas esta página de documentação do mysql .