Информационные технологииStfw.Ru 🔍

Экспорт - импорт БД oracle большого размера.

Евгений Воронянский
🕛 29.05.2007, 12:45
Если вы читаете данную статью, то скорее всего Вас настигло несчастье. Экземпляр продуктивной БД доживает последние дни и вот вот перестанет вообще запускаться. Из RMAN-а вы восстановиться не можете, так как пользователи «наработали» достаточно большое количество данных, а архивные журналы с момента аварии безнадежно утеряны. Забегая вперед, отмечу, что с помощью ниже описываемого метода был произведен экпорт-импорт БД размером около 160GB за 18 часов. Платформа:

1 Хост с «испорченным» экземпляром - RAM 8G, 4CPU, дисковый массив Clariion, SunOS Solaris, Oracle 8.1.7.4-64
2 Хост приемник - RAM 24G, 12CPU, дисковый массив Clariion, SunOS Solaris, Oracle 8.1.7.4-64.
Дополнительное программное обеспечение: ToadForOracle (Quest).
Итак, приступим.
Будем считать что для восстановления мы добавили отдельный диск для расположения на нем исполняемых файлов, а также журналов импорта/экспорта, и для того чтобы не путаться в достаточно большом количестве журналов выполнения создадим структуру дирректорий:
mount /dev/dsk/c1t1d0s0 /mnt/drive
mkdir /mnt/drive/exp-imp
chown oracle:dba /mnt/drive/exp-imp
su - oracle
cd /mnt/drive/exp-imp
mkdir pipe ilog elog sql
Внимание! В данной статье приводится вариант экспорта/импорта от владельца объекта. Т.е. прежде чем стартовать экспорт/импорт, необходимо убедиться что:

1 В БД источнике все пользователи имеют право подключаться.
2 Нет заблокированных пользователей.

В связи с пунктом 3 необходимо создать список заблокированных пользователе, а также сохранить хэши паролей пользователей.
Для этого используем SQL приведенный ниже:
cd sql
cat > lockuser.sql
Код:
#Список заблокированных пользователей
whenever sqlerror exit sql.sqlcode;
set pause off;
set pages 0;
set linesize 140;
set feedback off;
set termout off;
spool lockuser.spool.sql
select 'alter user "'||username||'" account lock;'
from dba_users
where account_status <> 'OPEN';
exit;

^D
cat >user_password.sql
Код:
#Создание спаска хэшей паролей и
#скрипта подстановки времменного пароля для всех пользователей
whenever sqlerror exit sql.sqlcode;
set pause off;
set pages 0;
set linesize 140;
set feedback off;
set termout off;
column cmd_line format a80
column tick format a3
column sum_bytes format 999,999,999,999
spool return_pass.spool.sql;
select 'alter user "'||username||'" identified by values '''||password||''';' from dba_users;
spool off;
spool change_pass.spool.sql;
select 'alter user "'||username||'" identified by qqq;' from dba_users;
spool off;
exit;
^4D
cat > unlockuser.sql
Код:
#Разблокировка пользователей
whenever sqlerror exit sql.sqlcode;
set pause off;
set pages 0;
set linesize 140;
set feedback off;
set termout off;
spool unlockuser.spool.sql
select 'alter user "'||username||'" account unlock;'
from all_users
/
spool off;
exit;
^D
Ниже приведен скрипт создания пользовательских схем (без данных) в БД приемнике. В нем необходимо исправить SID, домашнюю дирректорию Oracle, а также установить правильные языковые настройки и пароль не схему system, т.е «system/pass@source» и «system/pass@dest» должно являться строкой подключения.
cat > full_export_no_rows.sh

ORACLE_HOME=/u01/app/oracle/products/8.1.7.4; export ORACLE_HOME
ORACLE_SID=ORCL; export ORACLE_SID
EXP=$ORACLE_HOME/bin/exp
IMP=$ORACLE_HOME/bin/imp
NLS_LANG=AMERICAN_AMERICA.CL8MSWIN1251; export NLS_LANG
DUMP_DIR=/mnt/drive/exp-imp
PIPE=$DUMP_DIR/pipe/exp_full_pipe
ELOG=$DUMP_DIR/elog/$ORACLE_SID-full-`date +%Y%m%d`.elog
ILOG=$DUMP_DIR/ilog/$ORACLE_SID-full-`date +%Y%m%d`.ilog
rm -f $PIPE
mknod $PIPE p # Make the pipe
date >> $ELOG
date >> $ILOG
$EXP system/pass@source FULL=Y ROWS=N CONSTRAINTS=N INDEXES=Y TRIGGERS=N log=$ELOG file=$PIPE& #2>/dev/null 1>/dev/null & # Export to the pipe
$IMP system/pass@dest FULL=Y IGNORE=Y log=$ILOG file=$PIPE #Import from the pipe 
date >> $ELOG
date >> $ILOG
rm -f $PIPE
^D
По аналогии создаем скрипт, с помощью которого можно перенести отдельный объект.
cd ..
cat > table_export.sh
Код:
ORACLE_HOME=/u01/app/oracle/products/8.1.7.4; export ORACLE_HOME
ORACLE_SID=ORCL; export ORACLE_SID
EXP=$ORACLE_HOME/bin/exp
IMP=$ORACLE_HOME/bin/imp
NLS_LANG=AMERICAN_AMERICA.CL8MSWIN1251; export NLS_LANG
DUMP_DIR=/mnt/drive/exp-imp
PIPE=$DUMP_DIR/pipe/$1.$2
ELOG=$DUMP_DIR/elog/$ORACLE_SID-$1.$2-`date +%Y%m%d`.elog
ILOG=$DUMP_DIR/ilog/$ORACLE_SID-$1.$2-`date +%Y%m%d`.ilog
rm -f $PIPE
mknod $PIPE p # Make the pipe
date >> $ELOG
date >> $ILOG
$EXP $1/temp_pass@source TABLES=$2 COMPRESS=Y FEEDBACK=1000 log=$ELOG file=$PIPE 2>/dev/null& # Export to the pipe
$IMP $1/temp_pass@dest TABLES=$2 COMMIT=Y IGNORE=Y log=$ILOG file=$PIPE 2>/dev/null # Import from the pipe
date >> $ELOG
date >> $ILOG
rm -f $PIPE
^DДля полного счастья строим отсортированный по размеру список таблиц, данные которых необходимо перенести с помощью следующего SQL:
cat >list_tbls.sql
Код:
whenever sqlerror exit sql.sqlcode;
set pause off;
set pages 0;
set linesize 150;
set feedback off;
set termout off;
column cmd_line format a80
column tick format a3
column sum_bytes format 999,999,999,999
spool master.list;
-select '/mnt/drive/exp-imp/full_export_no_rows.sh' from dual;
select '/mnt/drive/exp-imp/table_export.sh '||owner||' '||segment_name cmd_line,' # ' tick,
sum(bytes) sum_bytes
from dba_segments
where segment_type = 'TABLE'
and owner <> 'SYS'
and owner not like 'AURORA%'
group by owner, segment_name
order by sum(bytes) desc;
spool off;
exit;
^D
Далее создем скрипт который позволит пускать необходимые процессы в параллели:
cat >paresh
Код:
#!/bin/bash
#Исправте количество запускаемых процессов
#А также путь к командному файлу
message()
{ timestamp=`date +%Y.%m.%d:%H:%M` echo "$timestamp $*" | tee -a $logfile return
}
get_shell()
{ echo "`date` $1 Shell Request $$" >> $lklogfile while : do next_shell="" if [ ! -s ${workfile} ] then break fi if [ ! -f $lockfile ] then. echo $$ > $lockfile echo "`date` $1 Lock Obtained $$" >> $lklogfile if [ "$$" = "`cat $lockfile`" ] then next_shell=`sed -e q $workfile` sed -e 1d $workfile > ${workfile}.tmp mv ${workfile}.tmp $workfile rm -f $lockfile echo "`date` $1 Shell Issued " >> $lklogfile return else echo "`date` $1 Lock FAULTED $$" >> $lklogfile fi fi sleep 1 done return
}
paresh_slave()
{ shell_count=0 get_shell $1 while [ "$next_shell" != "" ] do shell_count=`expr $shell_count + 1` message "Slave $1: Running Shell $next_shell" $next_shell shell_status=$? if [ "$shell_status" -gt 0 ] then message "Slave $1: ERROR IN Shell $next_shell 2status=$shell_status" echo "Slave $1: ERROR IN Shell $next_shell status=$shell_status" >> $errfile fi get_shell $1 done message "Slave $1: Done (Executed $shell_count Shells)" return
}

paresh_driver()
{ rm -f $lklogfile if [ "$1" = "" ] then master_file="/mnt/drive/exp-imp/master.list" echo $master_file else if [ ! -f "$1" ] then echo "$0: Unable to find File $1" exit 1 else master_file="$1" fi fi if [ "$2" = "" ] then parallel_count=24 #(CPU*2) else if [ "$2" -lt 1 ] then echo "$0: Parallel Process Count Must be > 0" exit 1 else parallel_count=$2 fi fi message "-" message "Master Process ID: $PARESH" message "Processing File: $master_file" message "Parallel Count: $parallel_count" message "Log File: $logfile" message "-" cp $master_file $workfile while test $parallel_count -gt 0 do if [ ! -s $workfile ] then message "All Work Completed - Stopped Spawning at $parallel_count" break fi $0 $parallel_count & message "Spawned Slave $parallel_count [pid $!]" parallel_count=`expr $parallel_count - 1` done wait message "All Done" return 
}

# main
if [ "$PARESH" != "" ]; then 
workfile=/tmp/paresh.work.$PARESH 
lockfile=/tmp/paresh.lock.$PARESH 
lklogfile=/tmp/paresh.lklog.$PARESH
ogfile=/tmp/paresh.log.$PARESH 
errfile=/tmp/paresh.err.$PARESH 
paresh_slave $* 
else 
PARESH="$$"; export PARESH 
workfile=/tmp/paresh.work.$PARESH 
lockfile=/tmp/paresh.lock.$PARESH 
lklogfile=/tmp/paresh.lklog.$PARESH
logfile=/tmp/paresh.log.$PARESH 
errfile=/tmp/paresh.err.$PARESH 
rm -f $errfile 
paresh_driver $* 
rm -f $workfile 
rm -f $lklogfile 
if [ -f $errfile ] 
then
message "*************************************************"
message "FINAL ERROR SUMMARY. Errors logged in $errfile"
cat $errfile | tee -a $logfile
message "*************************************************"
exit 1
fi
fi
exit

^D
После создания всех необходимых файлов, а также базы данных на целевом хосте со всеми необходимыми табличными пространствами ставим перед фактом руководство, и приступаем к использованию только что созданных файлов:
Построение списков:
cd sql
sqlplus system/pass@source @lockuser.sql
sqlplus system/pass@source @user_password.sql
sqlplus system/pass@source @unlockuser.sql
cd ..
sqlplus system/pass@source @list_tbls.sql
cd sql
Изменение паролей пользователей и снятие блокировок с аккаунтов.
sqlplus system/pass@source @change_pass.spool.sql
sqlplus system/pass@source @unlockuser.spool.sql

Экспорт/импорт пользовательских схем без данных:
cd ..
./full_export_no_rows.sh
Далее внимательно проверяем все что в дирректории ilog, elog и только после окончания экспорта/импорта схем запускаем
./paresh
и анализируем лог-файлы процессов импорта/экспорта.
По завершению всех процессов для определения «потерянных» объектов (constraints, index…) используем Toad for Oracle (DBA->Compare Schemas, DBA->Compare Databases), собираем статистику, а также выполняем скрипт для определения разницы в таблицах на уровне количества строк, для работоспособности которого необходимо связать БД линком MIGRATION.WORLD :
cd /mnt/drives/exp-imp/sql
cat > rows_tables_diff.sql
Код:
whenever sqlerror exit sql.sqlcode;
set pause off;
set pages 0;
set linesize 150;
set feedback off;
set termout off;
column cmd_line format a80
column name format a3
column Diff format 999,999,999,999
spool rows_tables_diff.spool.sql;
select 'whenever sqlerror exit sql.sqlcode;'||chr(10)|| 'set pause off;'||chr(10)|| 'set pages 0;'||chr(10)|| 'set linesize 150;'||chr(10)|| 'set feedback off;'||chr(10)|| 'set termout off;'||chr(10)|| 'column cmd_line format a80'||chr(10)|| 'column tick format a3'||chr(10)|| 'column sum_bytes format 999,999,999,999'||chr(10)|| 'spool rows_tables_diff.txt'
from dual;
select
'select name, sum(Dest) - sum(Source) diff '||chr(10)||
'from ( '||chr(10)|| 'select '''||table_name||''' name,count(1) Dest, 0 Source '||chr(10)|| 'from '||owner||'.'||table_name||'@MIGRATION.WORLD union '||chr(10)|| 'select '''||table_name||''' name,0 Dest, count(1) Source '||chr(10)|| 'from '|| owner ||'.'||table_name||' )'||chr(10)|| 'group by name;' Str
from all_tables
where owner not in ('SYSTEM','SYS')
and owner not like 'AURORA%'
select chr(10)||'exit;' ||chr(10) from dual;
exit;
^D
И в зависомости от того в какой из БД был создан линк выполняем rows_tables_diff.spool.sql. Результат сравнения наблюдаем в rows_tables_diff.txt.

Значительно ускорить экспорт/импорт позволяет установка параметра скрытого параметра oracle - _disable_logging в true на БД в которую производится импорт, но при запуске БД в эксплуатацию его необходимо установить в false.
Удачи.

Базы данных   Теги: Oracle

Читать IT-новости в Telegram
Информационные технологии
Мы в соцсетях ✉