TL;DR: LOAD DATA INFILE jest o jeden rząd wielkości szybszy niż wiele INSERT instrukcje, które same są o jeden rząd wielkości szybsze niż pojedyncze INSERT oświadczenia.
Poniżej porównam trzy główne strategie importowania danych z R do Mysql:
-
pojedynczy
insertoświadczenia , jak w pytaniu:INSERT INTO test (col1,col2,col3) VALUES (1,2,3) -
wiele
insertoświadczenia , sformatowany tak:INSERT INTO test (col1,col2,col3) VALUES (1,2,3),(4,5,6),(7,8,9) -
load data infileoświadczenie , czyli ładowanie wcześniej napisanego pliku CSV wmysql:LOAD DATA INFILE 'the_dump.csv' INTO TABLE test
Używam RMySQL tutaj, ale każdy inny sterownik mysql powinien prowadzić do podobnych wyników. Utworzono instancję tabeli SQL za pomocą:
CREATE TABLE `test` (
`col1` double, `col2` double, `col3` double, `col4` double, `col5` double
) ENGINE=MyISAM;
Połączenie i dane testowe zostały utworzone w R z:
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)
Porównanie pojedynczego insert oświadczenia:
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
=> zajmuje to około 4 minuty na moim komputerze
Porównanie wielu insert oświadczenia:
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
=> zajmuje to około 40 sekund na moim komputerze
Benchmarking load data infile oświadczenie :
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
=> zajmuje to około 4 sekundy na moim komputerze
Stworzenie zapytania SQL do obsługi wielu wartości wstawiania to najprostszy sposób na poprawę wydajności. Przejście do LOAD DATA INFILE doprowadzi do optymalnych rezultatów. Dobre wskazówki dotyczące wydajności można znaleźć na tej stronie dokumentacji mysql .