Rails:在Oracle上rake db:migrate * very * slow

时间:2009-02-26 21:06:45

标签: ruby-on-rails ruby oracle rake dbmigrate

我正在使用带oracleenhanced适配器的rails为旧版应用程序创建一个新界面。

数据库迁移成功完成,但在rake完成之前需要花费相当长的时间。数据库更改发生得非常快(1或2秒),但db/schema.db转储需要一个多小时才能完成。 (参见下面的示例迁移。)

这是一个相对较大的架构(约150个表),但我确信不应该花这么长时间来转储每个表格描述。

无论如何只需要使用最后一个schema.db并将迁移中指定的更改应用到它来加快速度吗?或者我能够完全跳过这个模式转储吗?

我理解这个schema.db每次都是从头开始创建测试数据库,但是这种情况下,表触发器中有很大一部分数据库逻辑没有包含在schema.rb中无论如何,所以在任何情况下,佣金测试对我们都没有好处。 (这是一个完全不同的问题,我需要在其他方面进行梳理。)

dgs@dgs-laptop:~/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

2 个答案:

答案 0 :(得分:4)

将所有迁移应用于数据库之后,然后rake db:migrate调用db:schema:dump task从当前数据库模式生成schema.rb文件。

db:schema:dump call adapter的“tables”方法获取所有表的列表,然后为每个表调用“索引”方法和“列”方法。您可以在activerecord-oracle_enhanced-adapter gem的oracle_enhanced_adapter.rb文件中找到这些方法中使用的SQL SELECT语句。基本上它确实从ALL%或USER%数据字典表中选择以查找所有信息。

最初,当我将其用于具有大量不同模式的数据库时,我遇到了原始Oracle适配器的问题(因为性能可能会受到数据库中表的总数的影响 - 而不仅仅是在您的模式中),因此我做了一些优化在Oracle增强适配器中。在你的情况下找出哪些方法很慢会很好(我怀疑它可能是为每个表执行的“索引”或“列”方法)。

调试此问题的一种方法是,如果您在oracle_enhanced_adapter.rb文件中放入一些调试消息,以便您可以识别哪些方法调用花了这么长时间。

答案 1 :(得分:2)

问题主要是在oracle_enhanced_adapter.rb进行一些挖掘后解决的。

问题归结为本地模式中的表太多(在某些时候恰好创建了许多EBA_%, EVT_%, EMP_%, SMP_%表),转储中包含的归档表和数据字典中的选择执行14秒。

为了确定速度,我做了三件事:

  1. 删除所有不需要的表格(约500个中的250个)
  2. 从架构转储中排除的归档表
  3. 缓存长时间运行查询的结果
  4. 这将剩余350个表的迁移/模式转储的时间从大约90分钟缩短到大约15秒。比足够快。

    我的代码如下(灵感不是复制和粘贴 - 这段代码对我的数据库非常具体,但你应该能够理解)。您需要手动创建临时表。我需要大约2到3分钟的时间 - 每次迁移仍然需要太长时间,而且无论如何它都是相当静态的=)

    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