Rails: rake db: migrate * очень * медленно в Oracle - PullRequest
4 голосов
/ 27 февраля 2009

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

Миграции баз данных работают успешно, но до окончания рейка требуется невероятно много времени. Изменения в базе данных происходят довольно быстро (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 ]

4 голосов
/ 28 февраля 2009

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

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

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

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

2 голосов
/ 31 марта 2009

Проблема в основном решена после некоторого раунда копания в 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
...