2010-02-17 6 views
5

J'essaye de construire un utilitaire rake qui mettra à jour ma base de données de temps en temps.base de données de mise à jour en vrac efficace

C'est le code que j'ai jusqu'à présent:

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 

Le problème que je vais avoir est que cela prend quelques minutes pour entrer dans les 100 mille entrées et plus. J'aimerais trouver un moyen plus efficace de mettre à jour ma base de données. Idéalement, cela restera indépendant du type de base de données, mais sinon mon serveur de production tournera sur MySQL.

Nous vous remercions de votre compréhension.

Répondre

9

Avez-vous essayé d'utiliser AR Extensions pour l'importation en bloc? Vous obtenez des améliorations de performances impressionnantes lorsque vous insérez des milliers de lignes dans DB. Visitez leur website pour plus de détails.

Reportez-vous à ces exemples pour plus d'informations

Usage Example 1

Usage Example 2

Usage Example 3

+0

C'est exactement ce que je cherchais, merci. – codr

+0

Le gem prend en charge l'importation à partir de CSV. Ceci élimine les coûts d'instanciation et de validation 'ActiveRecord '. Reportez-vous à cet article pour plus de détails. http://www.rubyinside.com/advent2006/17-extendingar.html –

+0

A m'a aidé aussi - merci! – ambertch

1

Vous pouvez générer un fichier texte avec toutes INSERTs dont vous avez besoin, puis exécutez:

mysql -u user -p db_name < mytextfile.txt 

Je ne sais pas si ce sera plus vite, mais la peine d'essayer ...

+0

Rails lui-même utilise l'instruction d'insertion SQL. - Voir le journal de vos rails. Donc, cette méthode ne serait pas une amélioration de la vitesse. –

+2

Bien sûr, Rails fait INSERTS, sinon comment ajouter des enregistrements à la base de données? Mais dans son article original, l'auteur utilise la méthode "save" qui a plus de frais généraux qu'un simple insert. Je suis sûr que cela implique de faire des commit sur chaque insert, de faire des validations de modèles, etc – Zepplock

0

Comme le dit Larry, utilisez vos services spécifiques à l'importation-DB si le fichier est dans le format que vous voulez. Toutefois, si vous devez manipuler les données avant de les insérer, vous pouvez générer une seule requête INSERT avec des données pour plusieurs lignes, ce qui est plus rapide que d'utiliser une requête distincte pour chaque ligne (comme le fera ActiveRecord). Par exemple:

INSERT INTO iptocs (ip_from, ip_to, country_code) VALUES 
    ('xxx', 'xxx', 'xxx'), 
    ('yyy', 'yyy', 'yyy'), 
    ...; 
Questions connexes