TL;DR: LOAD DATA INFILE är en storleksordning snabbare än flera INSERT satser, som i sig är en storleksordning snabbare än enstaka INSERT uttalanden.
Jag jämför nedan de tre huvudstrategierna för att importera data från R till Mysql:
-
enkel
insertuttalanden , som i frågan:INSERT INTO test (col1,col2,col3) VALUES (1,2,3) -
flera
insertuttalanden , formaterad så här:INSERT INTO test (col1,col2,col3) VALUES (1,2,3),(4,5,6),(7,8,9) -
load data infileuttalande , dvs. ladda en tidigare skriven CSV-fil imysql:LOAD DATA INFILE 'the_dump.csv' INTO TABLE test
Jag använder RMySQL här, men alla andra mysql-drivrutiner bör leda till liknande resultat. SQL-tabellen instansierades med:
CREATE TABLE `test` (
`col1` double, `col2` double, `col3` double, `col4` double, `col5` double
) ENGINE=MyISAM;
Anslutningen och testdata skapades i R med:
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 single insert uttalanden:
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
=> detta tar ungefär 4 minuter på min dator
Benchmarking flera insert uttalanden:
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
=> detta tar ungefär 40 sekunder på min dator
Benchmarking load data infile uttalande :
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
=> detta tar ungefär 4 sekunder på min dator
Att skapa din SQL-fråga för att hantera många infogningsvärden är det enklaste sättet att förbättra prestandan. Övergår till LOAD DATA INFILE kommer att leda till optimala resultat. Bra prestandatips finns på denna sida med mysql-dokumentation .