Оптимальный способ переноса 4 виртуальных таблиц SDA в Hana DB

#sql #stored-procedures #hana #s4hana #slt

#sql #хранимые процедуры #hana #s4hana #slt

Вопрос:

Проблема: Моя команда в настоящее время проходит миграцию ERP с ECC-системы на новую систему S / 4 Hana. В рамках go-live нашей команде необходимо реплицировать все таблицы из системы S / 4 в нашу схему SLT, в которой будут размещены данные. Большая часть таблиц будет обрабатываться репликацией SLT из SAP. Но из-за сжатых сроков мы определили 4 таблицы, для репликации которых потребуется несколько дней. Идея состоит в том, чтобы скопировать существующие данные из удаленного источника (ABAP / SDA) и поместить в нашу схему SLT. Как только это будет выполнено, мы можем активировать репликацию с прямой передачей и разрешить обновлять все новые или измененные записи с помощью репликации SLT.

Предпринятые подходы: Наш текущий подход заключается в установлении соединения SDA с серверной базой данных S / 4 и разбивке данных по годам для вставки в нашу локальную таблицу с использованием хранимой процедуры. При таком подходе возник ряд проблем, но в настоящее время он работает. Это просто очень медленно.

Вопросы для форума:

  • Так ли вы подходите к решению такого рода проблем? Если нет, то какое ваше предлагаемое решение?
  • Видите ли вы в целевой таблице что-нибудь, что необходимо настроить для повышения производительности?
  • Выделяет ли что-нибудь для вас в хранимой процедуре, которую, возможно, потребуется настроить?

Пример: давайте представим, что у нас есть исходная таблица с именем: A_tbl

  • 500 миллионов записей в A_tbl
  • Ширина примерно 500 столбцов

Тогда у нас будет наша целевая таблица: B_tbl

  • То же количество столбцов, что и A_tbl (500)
  • Циклическое разбиение из 12
  • Индексируется по 5 столбцам

Текущая процедура:

 CREATE OR REPLACE procedure LOAD_B_TBL_FROM_A_TBL ()
as
begin

    declare v_offset_nbr integer;
    declare v_record_count integer;
    declare v_commit_count integer;
    declare i integer;
    declare v_year nvarchar(4);
    declare v_record_per_commit_count CONSTANT INT = 1000000; 
    declare v_table_name CONSTANT NVARCHAR(30) = 'A_TBL';   
    declare v_start_year CONSTANT INT = 2011;
    declare v_end_year CONSTANT INT = 2022;
    declare year_nbr integer;

    
    for year_nbr in v_start_year..v_end_year do
    
        select IfNull(max(offset_nbr),0) into v_offset_nbr from B_TBL_SCHEMA.bulk_load_log where table_name = :v_table_name AND year_nbr = to_varchar(year_nbr); -- Get offset number of records
        
        select count(*) into v_record_count from A_TBL_SCHEMAA_TBL A_TBL WHERE A_TBL.YEAR = to_varchar(year_nbr); -- Count the source records.
    
        v_record_count = v_record_count - v_offset_nbr; -- Subtract out the records already committed for the current year. Failsafe if procedure fails
        
        v_commit_count = v_record_count / v_record_per_commit_count; -- Number of times we need to loop
        
        IF v_record_count < v_record_per_commit_count THEN -- Don't enter the loop if it's not necessary
            INSERT INTO B_TBL_SCHEMA.B_TBL (
                SELECT * FROM A_TBL_SCHEMAA_TBL
                WHERE A_TBL.YEAR = to_varchar(year_nbr)
            ); -- Insert into our target table 
            COMMIT;
            
            
            insert into B_TBL_SCHEMA.bulk_load_log values(
                v_table_name,
                to_varchar(year_nbr),
                :v_offset_nbr,
                now()
            ); -- Insert into a logging table to keep up with offset
    
        ELSE
    
            for i in 0..v_commit_count do -- Loop number of commit times. (500 million / 1 million) = 500 commits necessary to process entire table
        
                INSERT INTO B_TBL_SCHEMA.B_TBL (
                    SELECT * FROM A_TBL_SCHEMAA_TBL 
                    WHERE A_TBL.YEAR = to_varchar(year_nbr)
                    LIMIT :v_record_per_commit_count OFFSET :v_offset_nbr
                ); -- Insert into our target table
                COMMIT;
            
                v_offset_nbr = v_offset_nbr   v_record_per_commit_count; -- Update the offset before logging so we know where to begin if procedure fails
                
                insert into B_TBL_SCHEMA.bulk_load_log values(
                    v_table_name,
                    to_varchar(year_nbr),
                    :v_offset_nbr,
                    now()
                ); -- Insert into logging table to keep up with offset
                COMMIT;
            end for;
        end if;
    end for;
end;
 

Ответ №1:

Я думаю, что самый простой и быстрый способ переноса таблиц без какого-либо дополнительного администрирования — это EXPORT оператор с BINARY параметром format . Это также можно сделать через контекстное меню HANA studio в схеме или через File -> Export... -> SAP HANA -> Catalog Objects , File -> Import... .

При таком подходе вы можете вручную задать количество потоков для экспорта и импорта без дополнительного сложного кода. После импорта в целевую систему у вас будет та же таблица с той же структурой в той же схеме, что и в исходной системе, поэтому, чтобы переместить таблицу с новым именем или схемой, вам нужно сначала скопировать ее в исходный код. После импорта вы можете insert ... select ... создать целевую таблицу или скопировать таблицу с желаемым разделением в исходной системе или перераспределить импортированную таблицу в целевой и использовать ее в качестве целевой таблицы.

В чем преимущества:

  • Вам не нужен какой-либо код или SDA-соединение между системами.
  • Вам не нужно проверять и перепроверять, если ваш код работал нормально, и все данные были переданы без дубликатов.
  • У вас будет точно такая же таблица в цели.
  • Таблица экспортируется и импортируется во внутреннем столбчатом формате параллельно, поэтому между ними не появляется восстановление кортежей (я не знаю, достаточно ли умен драйвер HANA SDA, чтобы передавать столбцы по каналу SDA и выполнять восстановление кортежей в цели). И количество столбцов и записей мало влияют, только столбцы с данными с высокой мощностью требуют времени, пустые или столбцы с низкой мощностью экспортируются в мгновение ока.

И, наконец, я протестировал это в своей системе через HANA studio (импорт на локальный компьютер): таблица с 130 миллионами записей и 57 столбцами размером 5 ГБ была экспортирована в 8 потоков в течение 6 минут.

Как насчет вашего первоначального подхода: вы всегда должны отключать индексы и ограничения удаления для действительно массовых операций и перестраивать / включать их в самом конце, чтобы сэкономить время на перестройку индекса или проверку ограничений во время вставки.

Комментарии:

1. Спасибо за всю эту информацию, я действительно ценю это! В ответ на ваше предложение, мы действительно пытались выполнить двоичный экспорт ранее при проектировании системы, но, оглядываясь назад на мои заметки, мы попытались сделать это в таблице с 49 миллионами записей и получили какую-то ошибку дискового пространства.

Ответ №2:

Узким местом производительности, вероятно, является передача через SDA, которая использует только один поток.

Обходной путь может быть:

  • создайте функцию с входным параметром для выбора подмножества таблицы A. Например, год.
  • поместите список лет в табличную переменную
  • вызовите map_merge для принудительного параллелизма и передачи выходных данных в целевую таблицу.

Вот некоторый псевдокод.

 -- virtual table to source A over SDA: VT_TBL_A
-- target table B: TBL_B

CREATE OR REPLACE FUNCTION F_TBL_A_YEAR ( IN YEAR INT)
RETURNS TABLE ( ... )
as begin
   select * from VT_TBL_A where year(MY_DT_COLUMN) = :YEAR;
end;

DO
BEGIN
   DECLARE t_var LIKE TBL_B;

   --list of all 'partitions'
   years=select distinct year(MY_DT_COLUMN) as MY_YEAR from VT_TBL_A;

   --call the function for each year
   t_var = MAP_MERGE(:years, F_TBL_A_YEAR( :years.MY_YEAR));

   insert into TBL_B select * from :t_var;
   commit;
END;
 
 

Пока это выполняется, я предлагаю вам проверить системное представление M_REMOTE_STATEMENTS, чтобы подтвердить, что записи передаются по нескольким соединениям