Rails: rake db: migrate * очень * медленно на Oracle

Я использую рельсы с oracleenhanced адаптером для создания нового интерфейса для устаревшего приложения.

Миграция базы данных выполняется успешно, но до завершения рейка требуется невероятно много времени. Изменения в базе данных происходят довольно быстро (1-2 секунды), но db/schema.db дамп занимает больше часа. (См. Пример миграции ниже.)

Это относительно большая схема (около 150 таблиц), но я уверен, что выгрузка описания каждой таблицы не займет так много времени.

Есть ли способ ускорить это, просто взяв последнее schema.db и применив к нему изменение, указанное в миграции? Или я могу вообще пропустить этот дамп схемы?

Я понимаю, что это schema.db используется для создания тестовой базы данных с нуля каждый раз, но в этом случае в триггерах таблиц есть большой кусок логики базы данных, который в schema.rb любом случае не включен , поэтому тесты рейка никуда не годятся для нас. кейс. (Это совершенно другая проблема, с которой мне нужно разобраться в другом месте.)

dgs @ dgs-laptop: ~ / rails / voyager $ time rake db: migrate
(в / home / dgs / rails / voyager)
== 20090227012452 AddModuleActionAndControllerNames: перенос ================
- add_column (: modules,: action_name,: text)
   -> 0,9619 с
   -> 0 строк
- add_column (: модули,: имя_контроллера,: текст)
   -> 0,1680 с
   -> 0 строк
== 20090227012452 AddModuleActionAndControllerNames: перенесено (1.1304 с) =======


реальный 87 мин. 12.961 сек.
пользователь 0m12.949s
sys 0m2.128s

Ответов (2)

Решение

После того, как все миграции применены к базе данных, выполните rake db: migrate calls db: schema: dump для создания файла schema.rb из текущей схемы базы данных.

db: schema: dump вызывает метод «tables» адаптера для получения списка всех таблиц, затем для каждой таблицы вызывает метод «indexes» и метод «columns». Вы можете найти операторы SQL SELECT, которые используются в этих методах, в файле gem-пакета oracle_enhanced_adapter.rb activerecord-oracle_enhanced-adapter. В основном он выбирает из таблиц словаря данных ALL% или USER%, чтобы найти всю информацию.

Изначально у меня были проблемы с оригинальным адаптером Oracle, когда я использовал его с базами данных с множеством разных схем (так как на производительность может влиять общее количество таблиц в базе данных, а не только в вашей схеме), поэтому я сделал некоторые оптимизации в Oracle, улучшенные адаптер. Было бы хорошо узнать, какие методы в вашем случае медленные (я подозреваю, что это может быть метод «индексов» или «столбцов», который выполняется для каждой таблицы).

Один из способов отладки этой проблемы - это поместить несколько отладочных сообщений в файл oracle_enhanced_adapter.rb, чтобы вы могли определить, какие вызовы методов занимают так много времени.

Проблема в основном решается после некоторого покопания oracle_enhanced_adapter.rb .

Проблема сводилась к тому, что в локальной схеме было слишком много таблиц (многие EBA_%, EVT_%, EMP_%, SMP_% таблицы были созданы там по совпадению в какой-то момент), архивные таблицы включались в дамп, а выбор из словарей данных занимал 14 секунд для выполнения.

Чтобы исправить скорость, я сделал три вещи:

  1. Удалили все ненужные таблицы (около 250 из 500)
  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