2017-08-17 2 views
0

Je travaille actuellement sur un projet avec Ruby/Rails, en important des factures dans la base de données mais en essayant de maximiser l'efficacité des processus, ce qui est en effet trop lent en ce moment.Travailler avec des importations de jeux de données volumineuses dans Ruby/Rails

Pour un lot d'importation de 100 000 lignes, il faut environ 2,5 3 heures pour traiter et enregistrer chaque enregistrement dans la base de données.

//// Code Ruby

class DeleteImportStrategy 
def pre_process(merchant_prefix, channel_import) 
    # channel needed to identify invoices so an import from another channel cannot collude if they had same merchant_prefix 
    Jzbackend::Invoice.where(merchant_prefix: merchant_prefix, channel: channel_import.channel).delete_all 
    # get rid of all previous import patches which becomes empty after delete_import_strategy 
    Jzbackend::Import.where.not(id: channel_import.id).where(channel: channel_import.channel).destroy_all 
end 

def process_row(row, channel_import) 
    debt_claim = Jzbackend::Invoice.new 
    debt_claim.import = channel_import 
    debt_claim.status = 'pending' 
    debt_claim.channel = channel_import.channel 
    debt_claim.merchant_prefix = row[0] 
    debt_claim.debt_claim_number = row[1] 
    debt_claim.amount = Monetize.parse(row[2]) 
    debt_claim.print_date = row[3] 
    debt_claim.first_name = row.try(:[], 4) 
    debt_claim.last_name = row.try(:[], 5) 
    debt_claim.address = row.try(:[], 6) 
    debt_claim.postal_code = row.try(:[], 7) 
    debt_claim.city = row.try(:[], 8) 
    debt_claim.save 
end 

fin

////

Donc, pour le chaque lot d'importation qui vient CSV, je débarrasser des lots précédents et début pour en importer de nouveaux en lisant chaque ligne et en l'insérant dans les nouveaux enregistrements Importer comme facture. Cependant, 2,5-3 heures pour 100.000 entrées semble un peu exagéré. Comment puis-je optimiser ce processus car je suis sûr que ce n'est certainement pas efficace de cette façon.

Édité: Donc ça fait longtemps que je l'ai posté mais juste pour le noter, j'ai fini par utiliser la bibliothèque activerecord-import qui marche plutôt bien depuis. Cependant, notez que c'est: la fonctionnalité on_duplicate_key_update n'est disponible que dans PostgreSQL v9.5 +.

Répondre

2

Première règle d'importation de masse: lot, lot, lot.

Vous sauvegardez chaque ligne séparément. Cela entraîne d'énormes frais généraux. Dites, l'insertion elle-même prend 1ms, mais l'aller-retour à la base de données est 5ms. Temps total utilisé - 6ms. Pour 1000 enregistrements, c'est 6000ms ou 6 secondes. Maintenant, imaginez que vous utilisez un insert de masse, où vous envoyez des données pour plusieurs lignes dans la même instruction. Cela ressemble à ceci:

INSERT INTO users (name, age) 
VALUES ('Joe', 20), ('Moe', 22), ('Bob', 33'), ... 

Supposons que vous envoyiez des données pour 1000 lignes dans cette requête. La requête elle-même prend 1000ms (mais en réalité, elle sera probablement beaucoup plus rapide, moins de frais généraux pour l'analyse de la requête, la préparation du plan d'exécution, etc.). Le temps total pris est de 1000ms + 5ms. Au moins 6x réduction! (Dans mes projets réels, j'observais une réduction de 100x-200x).

+0

Merci! Qu'en est-il de la suppression des enregistrements précédents que j'ai dans la méthode pre_process ci-dessus? Devrais-je l'aborder de la même manière? –

+0

@SahilGadimbayli: oui, lot-supprimer ceux aussi. Un seul 'DELETE FROM users' ferait l'affaire, mais pourrait bloquer le travail trop longtemps. Si c'est le cas, supprimez en plus petits lots. –

+0

Vous pouvez être intéressé par https://github.com/jamis/bulk_insert gem. –