2009-02-26 8 views
4

J'utilise des rails avec l'adaptateur oracleenhanced pour créer une nouvelle interface pour une application héritée.Rails: rake db: migrate * très * lent sur Oracle

Les migrations de base de données fonctionnent avec succès, mais prennent un temps incroyablement long avant la fin du rake. Les modifications de la base de données se produisent assez rapidement (1 ou 2 secondes), mais la vidage db/schema.db prend plus d'une heure pour terminer. (Voir l'exemple de migration ci-dessous.)

C'est un schéma relativement grand (environ 150 tables), mais je suis sûr que cela ne devrait pas prendre autant de temps pour vider chaque description de table.

Y a-t-il un moyen de l'accélérer en prenant simplement le dernier schema.db et en lui appliquant la modification spécifiée dans la migration? Ou suis-je capable d'ignorer complètement ce vidage de schéma? Je comprends que schema.db est utilisé pour créer la base de données de test à chaque fois, mais dans ce cas, il y a une grande partie de la logique de base de données dans les déclencheurs de table qui ne sont pas inclus dans le schema.rb pas bon pour nous en tout cas. (C'est une question tout à fait différent que je dois régler à un autre point.)

 
[email protected]:~/rails/voyager$ time rake db:migrate 
(in /home/dgs/rails/voyager) 
== 20090227012452 AddModuleActionAndControllerNames: migrating ================ 
-- add_column(:modules, :action_name, :text) 
    -> 0.9619s 
    -> 0 rows 
-- add_column(:modules, :controller_name, :text) 
    -> 0.1680s 
    -> 0 rows 
== 20090227012452 AddModuleActionAndControllerNames: migrated (1.1304s) ======= 


real 87m12.961s 
user 0m12.949s 
sys 0m2.128s 

Répondre

4

Après toutes les migrations sont appliquées à la base de données puis rake db: migrate appels db: schéma: tâche de vidage pour générer un fichier schema.rb à partir du schéma de base de données actuel. Db: schema: dump appelle la méthode "tables" de l'adaptateur pour obtenir la liste de toutes les tables, puis pour chaque table appelle la méthode "indexes" et la méthode "columns". Vous pouvez trouver les instructions SQL SELECT qui sont utilisées dans ces méthodes dans le fichier oracle_enhanced_adapter.rb du fichier activerecord-oracle_enhanced-adapter gem. Fondamentalement, il sélectionne des tables de dictionnaire de données ALL% ou USER% pour trouver toutes les informations. Au début, j'avais des problèmes avec l'adaptateur Oracle original lorsque je l'utilisais avec des bases de données avec beaucoup de schémas différents (les performances pouvaient être affectées par le nombre total de tables dans la base de données - et pas seulement dans votre schéma) optimisations dans l'adaptateur amélioré Oracle. Il serait bon de savoir quelles sont les méthodes qui sont lentes dans votre cas (je soupçonne que ce soit la méthode "indexes" ou "columns" qui est exécutée pour chaque table). Une manière hoe de déboguer ce problème serait si vous mettriez des messages de débogage dans le fichier oracle_enhanced_adapter.rb afin que vous puissiez identifier les appels de méthode prenant si longtemps.

+0

Cool. Je vais jouer dans oracle_enhanced_adapter.rb et voir ce que je peux trouver. Je suppose qu'il est lié au schéma car il y a un certain nombre de schémas identiques avec des noms différents dans la base de données. Salutations –

+0

Vous pouvez également essayer la nouvelle version de l'adaptateur oracle_enhanced version 1.2.0 - il existe des améliorations pour les performances de vidage de schéma. –

2

Problème résolu pour la plupart après un tour de creusage en oracle_enhanced_adapter.rb. Le problème est survenu à trop de tables dans le schéma local (beaucoup de tables EBA_%, EVT_%, EMP_%, SMP_% ont été créées en même temps), les tables d'archivage étant incluses dans le vidage et une sélection parmi les dictionnaires de données prenant 14 secondes à exécuter.

Pour fixer la vitesse, je l'ai fait trois choses:

  1. Dropped toutes les tables non nécessaires (environ 250 sur 500)
  2. tables d'archives exclues de la décharge du schéma
  3. mises en cache le résultat de la longue requête en cours d'exécution

Cela a amélioré le temps écoulé depuis la migration/le vidage de schéma pour les 350 tables restantes, passant d'environ 90 minutes à environ 15 secondes.Plus que assez vite.

Mon code comme suit (pour l'inspiration, ne pas copier et coller - ce code est assez spécifique à ma base de données, mais vous devriez être en mesure d'obtenir l'idée). Vous devez créer la table temporaire manuellement. Cela prend environ 2 ou 3 minutes - encore trop long à générer à chaque migration, et c'est quand même assez statique =)

module ActiveRecord 
    module ConnectionAdapters 
    class OracleEnhancedAdapter 
     def tables(name = nil) 
     select_all("select lower(table_name) from all_tables where owner = sys_context('userenv','session_user') and table_name not like 'A!_%' escape '!' ").inject([]) do | tabs, t | 
      tabs << t.to_a.first.last 
     end 
     end 


     # TODO think of some way to automatically create the rails_temp_index table 
     # 
     # Table created by: 
     # create table rails_temp_index_table as 
     # SELECT lower(i.index_name) as index_name, i.uniqueness, 
     #   lower(c.column_name) as column_name, i.table_name 
     # FROM all_indexes i, user_ind_columns c 
     # WHERE c.index_name = i.index_name 
     #  AND i.owner = sys_context('userenv','session_user') 
     #  AND NOT exists (SELECT uc.index_name FROM user_constraints uc 
     #    WHERE uc.constraint_type = 'P' and uc.index_name = i.index_name); 

     def indexes(table_name, name = nil) #:nodoc: 

       result = select_all(<<-SQL, name) 
       SELECT index_name, uniqueness, column_name 
        FROM rails_temp_index_table 
       WHERE table_name = '#{table_name.to_s.upcase}' 
        ORDER BY index_name 
       SQL 

       current_index = nil 
       indexes = [] 

      result.each do |row| 
       if current_index != row['index_name'] 
        indexes << IndexDefinition.new(table_name, row['index_name'], row['uniqueness'] == "UNIQUE", []) 
        current_index = row['index_name'] 
       end 

       indexes.last.columns << row['column_name'] 
       end 

       indexes 
      end 
end 
Questions connexes