Rails:rake db:migrate 在 Oracle 上*非常*慢

发布于 2024-07-14 04:48:06 字数 941 浏览 4 评论 0原文

我使用 Rails 和 oracleenhanced 适配器来为旧应用程序创建新接口。

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

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

无论如何,是否可以通过仅获取最后一个 schema.db 并将迁移中指定的更改应用到它来加快速度? 或者我可以完全跳过这个模式转储吗?

我知道这个 schema.db 用于每次从头开始创建测试数据库,但在这种情况下,表触发器中有很大一部分数据库逻辑不包含在 中无论如何,schema.rb 无论如何,rake 测试对我们来说都没有好处。 (这是一个完全不同的问题,我需要在其他时候解决。)

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

I'm using rails with the oracleenhanced adaptor to create a new interface for a legacy application.

Database migrations work successfully, but take an incredibly long amount of time before rake finishes. The database changes happen pretty quickly (1 or 2 seconds), but the db/schema.db dump takes over an hour to complete. (See example migration below.)

It's a relatively large schema (about 150 tables), but I'm sure it shouldn't be taking this long to dump out each table description.

Is there anyway to speed this up by just taking the last schema.db and applying the change specified in the migration to it? Or am I able to skip this schema dump altogether?

I understand this schema.db is used to create the test database from scratch each time, but this case, there's a large chunk of the database logic in table triggers which aren't included in the schema.rb anyway, so the rake tests are no good to us in any case. (That's a whole different issue that I need to sort out at some other point.)

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

感情旳空白 2024-07-21 04:48:06

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

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

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

调试此问题的一种方法是,将一些调试消息放入 oracle_enhanced_adapter.rb 文件中,以便您可以确定哪些方法调用花费了很长时间。

After all migrations are applied to database then rake db:migrate calls db:schema:dump task to generate schema.rb file from current database schema.

db:schema:dump call adapter's "tables" method to get the list of all tables, then for each table calls "indexes" method and "columns" method. You can find SQL SELECT statements that are used in these methods in activerecord-oracle_enhanced-adapter gem's oracle_enhanced_adapter.rb file. Basically it does selects from ALL% or USER% data dictionary tables to find all the information.

Initially I had issues with original Oracle adapter when I used it with databases with lot of different schemas (as performance might be affected by the total number of table in the database - not just in your schema) and therefore I did some optimizations in Oracle enhanced adapter. It would be good to find out which methods are slow in your case (I suspect that it could be either "indexes" or "columns" method which is executed for each table).

One way hoe to debug this issue would be if you would put some debug messages in oracle_enhanced_adapter.rb file so that you could identify which method calls are taking so long time.

淡淡绿茶香 2024-07-21 04:48:06

在对 oracle_enhanced_adapter.rb 进行一番深入研究后,问题基本得到解决。

问题归结为本地模式中的表太多(在某个时刻同时创建了许多 EBA_%、EVT_%、EMP_%、SMP_% 表),存档表包含在转储和从数据字典中进行选择需要 14 秒才能执行。

为了提高速度,我做了三件事:

  1. 删除所有不需要的表(500 个表中大约有 250 个)
  2. 从架构转储中排除存档表
  3. 缓存长时间运行查询的结果

这缩短了剩余 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

Problem mostly solved after some digging round in oracle_enhanced_adapter.rb.

The problem came down to way too many tables in the local schema (many EBA_%, EVT_%, EMP_%, SMP_% tables had been created in there coincidently at some point), archive tables being included in the dump and a select from the data dictionaries taking 14 seconds to execute.

To fix the speed, I did three things:

  1. Dropped all unneeded tables (about 250 out of 500)
  2. Excluded archive tables from the schema dump
  3. Cached the result of the long running query

This improved the time from the migration/schema dump for the remaining 350 tables from about 90 minutes to about 15 seconds. More than fast enough.

My code as follows (for inspiration not copying and pasting - this code is fairly specific to my database, but you should be able to get the idea). You need to create the temp table manually. It takes about 2 or 3 minutes for me to do - still too long to generate with each migration, and it's fairly static anyway =)

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文