Рельсы:rake db: миграция *очень* медленная в Oracle
-
09-09-2019 - |
Вопрос
Я использую рельсы с oracleenhanced
адаптер для создания нового интерфейса для устаревшего приложения.
Миграция базы данных работает успешно, но до завершения работы 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
Решение
После того, как все миграции применены к базе данных, rake db:migrate вызывает задачу db:schema:dump для создания файла Schema.rb из текущей схемы базы данных.
db:schema:dump вызывает метод «tables» адаптера, чтобы получить список всех таблиц, затем для каждой таблицы вызывает метод «indexes» и метод «columns».Вы можете найти инструкции SQL SELECT, которые используются в этих методах, в файле oracle_enhanced_adapter.rb гема activerecord-oracle_enhanced-adapter.По сути, он выбирает из таблиц словаря данных ALL% или USER%, чтобы найти всю информацию.
Первоначально у меня были проблемы с исходным адаптером Oracle, когда я использовал его с базами данных с множеством различных схем (поскольку на производительность могло влиять общее количество таблиц в базе данных, а не только в вашей схеме), и поэтому я провел некоторые оптимизации в Oracle Enhanced. адаптер.Было бы хорошо узнать, какие методы в вашем случае медленные (я подозреваю, что это может быть метод «индексов» или «столбцов», который выполняется для каждой таблицы).
Один из способов отладки этой проблемы — поместить несколько отладочных сообщений в файл oracle_enhanced_adapter.rb, чтобы можно было определить, какие вызовы методов занимают так много времени.
Другие советы
Проблема в основном решена после некоторых копаний oracle_enhanced_adapter.rb
.
Проблема заключалась в том, что в локальной схеме было слишком много таблиц (многие EBA_%, EVT_%, EMP_%, SMP_%
таблицы были созданы там случайно в какой-то момент), архивные таблицы включаются в дамп, а выбор из словарей данных занимает 14 секунд.
Чтобы исправить скорость, я сделал три вещи:
- Удалены все ненужные таблицы (около 250 из 500)
- Исключенные архивные таблицы из дампа схемы
- Кэширован результат длительного запроса
Это сократило время миграции/дампа схемы для оставшихся 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