2010-02-17 14 views
5

Sto provando a creare un programma di utilità rake che aggiornerà il mio database ogni tanto.efficiente database di rotaie di aggiornamento bulk

Questo è il codice che ho finora:

namespace :utils do 

    # utils:update_ip 
    # Downloads the file frim <url> to the temp folder then unzips it in <file_path> 
    # Then updates the database. 

    desc "Update ip-to-country database" 
    task :update_ip => :environment do 

    require 'open-uri' 
    require 'zip/zipfilesystem' 
    require 'csv' 

    file_name = "ip-to-country.csv" 
    file_path = "#{RAILS_ROOT}/db/" + file_name 
    url = 'http://ip-to-country.webhosting.info/downloads/ip-to-country.csv.zip' 


    #check last time we updated the database. 
    mod_time = '' 
    mod_time = File.new(file_path).mtime.httpdate if File.exists? file_path 

    begin 
     puts 'Downloading update...' 
     #send conditional GET to server 
     zipped_file = open(url, {'If-Modified-Since' => mod_time}) 
    rescue OpenURI::HTTPError => the_error 
     if the_error.io.status[0] == '304' 
     puts 'Nothing to update.' 
     else 
     puts 'HTTPError: ' + the_error.message 
     end 
    else # file was downloaded without error. 

     Rails.logger.info 'ip-to-coutry: Remote database was last updated: ' + zipped_file.meta['last-modified'] 
     delay = Time.now - zipped_file.last_modified 
     Rails.logger.info "ip-to-country: Database was outdated for: #{delay} seconds (#{delay/60/60/24 } days)" 

     puts 'Unzipping...' 
     File.delete(file_path) if File.exists? file_path 
     Zip::ZipFile.open(zipped_file.path) do |zipfile| 
     zipfile.extract(file_name, file_path) 
     end 

     Iptocs.delete_all 

     puts "Importing new database..." 


     # TODO: way, way too heavy find a better solution. 


     CSV.open(file_path, 'r') do |row| 
     ip = Iptocs.new( :ip_from  => row.shift, 
         :ip_to   => row.shift, 
         :country_code2 => row.shift, 
         :country_code3 => row.shift, 
         :country_name => row.shift) 
     ip.save 
     end #CSV 
     puts "Complete." 

    end #begin-resuce 
    end #task 
end #namespace 

Il problema che sto avendo è che questo richiede pochi minuti per inserire i 100 mila voci più. Mi piacerebbe trovare un modo più efficiente di aggiornare il mio database. Idealmente questo rimarrà indipendente dal tipo di database, ma se non il mio server di produzione verrà eseguito su MySQL.

Grazie per qualsiasi intuizione.

risposta

9

Hai provato a utilizzare AR Extensions per l'importazione di massa? Ottieni incredibili miglioramenti delle prestazioni quando inserisci 1000 di righe su DB. Visita il loro website per maggiori dettagli.

fare riferimento a questi esempi per ulteriori informazioni

Usage Example 1

Usage Example 2

Usage Example 3

+0

Questo è esattamente quello che stavo cercando, grazie. – codr

+0

La gem supporta l'importazione da CSV. Questo elimina i costi di istanziazione e validazione di 'ActiveRecord'. Fare riferimento a questo articolo per ulteriori dettagli. http://www.rubyinside.com/advent2006/17-extendingar.html –

+0

Aiutato anche a me - grazie! – ambertch

1

si potrebbe generare un file di testo con tutti gli inserti che ti serve e poi eseguire:

mysql -u user -p db_name < mytextfile.txt 

Non so se questo sarà più veloce, ma vale la pena provare ...

+0

Rails utilizza l'istruzione di inserimento SQL. - Vedi il registro delle tue rotaie. Quindi questo metodo non sarebbe un miglioramento della velocità. –

+2

Ovviamente Rails fa INSERTS, in quale altro modo aggiungerebbe record al database? Ma nel suo post originale l'autore usa il metodo "salva" che ha un sovraccarico maggiore di un semplice inserto. Sono sicuro che comporta il commit su ogni inserto, facendo convalide del modello, ecc. – Zepplock

0

Come Larry dice, utilizzare le utilità di importazione specifiche DB se il file viene nel formato desiderato. Tuttavia, se è necessario manipolare i dati prima di inserirli, è possibile generare una singola query INSERT con dati per più righe, che è più veloce rispetto all'utilizzo di una query separata per ogni riga (come farà ActiveRecord). Ad esempio:

INSERT INTO iptocs (ip_from, ip_to, country_code) VALUES 
    ('xxx', 'xxx', 'xxx'), 
    ('yyy', 'yyy', 'yyy'), 
    ...; 
Problemi correlati