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
insert
uttalanden , som i frågan:INSERT INTO test (col1,col2,col3) VALUES (1,2,3)
-
flera
insert
uttalanden , formaterad så här:INSERT INTO test (col1,col2,col3) VALUES (1,2,3),(4,5,6),(7,8,9)
-
load data infile
uttalande , 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 .