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
insert
oświadczenia , jak w pytaniu:INSERT INTO test (col1,col2,col3) VALUES (1,2,3)
-
wiele
insert
oświadczenia , sformatowany tak:INSERT INTO test (col1,col2,col3) VALUES (1,2,3),(4,5,6),(7,8,9)
-
load data infile
oś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 .