Как я могу ускорить запросы к огромным таблицам хранилища данных с эффективными данными? - PullRequest
7 голосов
/ 24 июня 2010

Итак, я запрашиваю очень большие таблицы. Причина, по которой они так велики, заключается в том, что PeopleSoft вставляет новые записи каждый раз, когда вносятся изменения в некоторые данные, вместо обновления существующих записей. Фактически его транзакционные таблицы также являются хранилищем данных.

Это требует запросов, в которых есть вложенные выборки, чтобы получить самую последнюю / текущую строку. Они оба имеют эффективную дату и в пределах каждой даты (приведенные к дню) могут иметь эффективную последовательность. Таким образом, чтобы получить текущую запись для user_id=123, я должен сделать это:

select * from sometable st
where st.user_id = 123
and st.effective_date = (select max(sti.effective_date) 
  from sometable sti where sti.user_id = st.user_id)
and st.effective_sequence = (select max(sti.effective_sequence) 
  from sometable sti where sti.user_id = st.user_id
  and sti.effective_date = st.effective_date)

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

Моя проблема в том, что я часто хочу получить данные о человеке из этих таблиц, возможно, для 50 user_ids, но когда я присоединяюсь к своим таблицам, имеющим только несколько записей в них с несколькими из этих таблиц PeopleSoft, все просто дерьмо.

Таблицы PeopleSoft находятся в удаленной базе данных, доступ к которой осуществляется через ссылку на базу данных. Мои запросы имеют тенденцию выглядеть следующим образом:

select st.* from local_table lt, sometable@remotedb st
where lt.user_id in ('123', '456', '789')
and lt.user_id = st.user_id
and st.effective_date = (select max(sti.effective_date) 
  from sometable@remotedb sti where sti.user_id = st.user_id)
and st.effective_sequence = (select max(sti.effective_sequence) 
  from sometable@remotedb sti where sti.user_id = st.user_id
  and sti.effective_date = st.effective_date)

Ситуация становится еще хуже, когда мне приходится соединять несколько таблиц PeopleSoft с моей локальной таблицей. Производительность просто недопустима.

Что я могу сделать, чтобы улучшить производительность? Я попробовал подсказки запросов, чтобы убедиться, что моя локальная таблица сначала присоединяется к ее партнеру в PeopleSoft, поэтому она не пытается объединить все свои таблицы вместе, прежде чем сузить ее до правильного user_id. Я попробовал подсказку LEADING и поиграл с подсказками, которые пытались перенести обработку в удаленную базу данных, но план объяснения был скрыт и просто сказал «УДАЛЕНО» для нескольких операций, и я понятия не имел, что происходит на.

Если у меня нет полномочий менять PeopleSoft и расположение моих столов, подсказки - мой лучший выбор? Если бы я соединял локальную таблицу с четырьмя удаленными таблицами, а локальная таблица соединялась с двумя из них, как бы я отформатировал подсказку, чтобы моя локальная таблица (которая очень мала - на самом деле, я могу просто сделать встроенное представление чтобы моя локальная таблица была только интересующими меня user_ids) сначала объединяется с каждой из удаленных?

РЕДАКТИРОВАТЬ: Приложению требуются данные в реальном времени, поэтому, к сожалению, материализованного представления или другого метода кэширования данных будет недостаточно.

Ответы [ 10 ]

4 голосов
/ 25 июня 2010

Помогает ли вообще что-либо подобное рефакторингу вашего запроса?

SELECT *
  FROM (SELECT st.*, MAX(st.effective_date) OVER (PARTITION BY st.user_id) max_dt,
                     MAX(st.effective_sequence) OVER (PARTITION BY st.user_id, st.effective_date) max_seq
          FROM local_table lt JOIN sometable@remotedb st ON (lt.user_id = st.user_id)
         WHERE lt.user_id in ('123', '456', '789'))
 WHERE effective_date = max_dt
   AND effective_seq = max_seq;

Я согласен с @Mark Baker в том, что объединение производительности через DB Links действительно может не сработать, и вы, вероятно, будете ограничены в том, что вы можете достичь с помощью этого подхода.

4 голосов
/ 25 июня 2010

Один из подходов - привязать функции PL / SQL ко всему. Как пример

create table remote (user_id number, eff_date date, eff_seq number, value varchar2(10));

create type typ_remote as object (user_id number, eff_date date, eff_seq number, value varchar2(10));
.
/

create type typ_tab_remote as table of typ_remote;
.
/

insert into remote values (1, date '2010-01-02', 1, 'a');
insert into remote values (1, date '2010-01-02', 2, 'b');
insert into remote values (1, date '2010-01-02', 3, 'c');
insert into remote values (1, date '2010-01-03', 1, 'd');
insert into remote values (1, date '2010-01-03', 2, 'e');
insert into remote values (1, date '2010-01-03', 3, 'f');

insert into remote values (2, date '2010-01-02', 1, 'a');
insert into remote values (2, date '2010-01-02', 2, 'b');
insert into remote values (2, date '2010-01-03', 1, 'd');

create function show_remote (i_user_id_1 in number, i_user_id_2 in number) return typ_tab_remote pipelined is
    CURSOR c_1 is
    SELECT user_id, eff_date, eff_seq, value
    FROM
        (select user_id, eff_date, eff_seq, value, 
                        rank() over (partition by user_id order by eff_date desc, eff_seq desc) rnk
        from remote
        where user_id in (i_user_id_1,i_user_id_2))
    WHERE rnk = 1;
begin
    for c_rec in c_1 loop
        pipe row (typ_remote(c_rec.user_id, c_rec.eff_date, c_rec.eff_seq, c_rec.value));
    end loop;
    return;
end;
/

select * from table(show_remote(1,null));

select * from table(show_remote(1,2));

Вместо того, чтобы user_id передавался индивидуально в качестве параметров, вы можете загрузить их в локальную таблицу (например, глобальную временную таблицу). Затем PL / SQL будет циклически проходить по таблице, делая удаленный выбор для каждой строки в локальной таблице. Ни один запрос не будет иметь как локальных, так и удаленных таблиц. По сути, вы будете писать свой собственный код соединения.

3 голосов
/ 25 июня 2010

Один из вариантов - сначала материализовать удаленную часть запроса, используя общее табличное выражение, чтобы вы могли быть уверены, что из удаленной базы данных извлекаются только релевантные данные. Другое усовершенствование - объединение двух подзапросов с удаленной базой данных в одинФункциональный подзапрос. Такой запрос также может быть использован в вашем текущем запросе.Я могу делать другие предложения только после игры с БД.

см. Ниже

with remote_query as
(
    select /*+ materialize */  st.* from sometable@remotedb st
    where st.user_id in ('123', '456', '789')
    and st.rowid in( select first_value(rowid) over (order by effective_date desc, 
                         effective_sequence desc ) from sometable@remotedb st1 
                      where st.user_id=st1.user_id)
)

select lt.*,st.* 
FROM local_table st,remote_query rt
where st.user_id=rt.user_id
1 голос
/ 25 июня 2010

Проблемой производительности будет доступ через ссылку. При выполнении части запроса к локальным таблицам все выполняется локально, поэтому нет доступа к удаленным индексам, и он возвращает все удаленные данные для тестирования lkocally.

Если бы вы могли использовать материализованные представления в локальной базе данных, обновляющейся из базы данныхoplesoft на периодической (ночной) основе для исторических данных, получая доступ только к удаленной базе данныхoplesoft для сегодняшних изменений (добавляя эффективную_дату = сегодня в предложение where) и объединение двух запросов.

Другим вариантом может быть использование INSERT INTO X SELECT FROM только для удаленных данных, чтобы перетащить их во временную локальную таблицу или материализованное представление, а затем второй запрос, чтобы объединить это с вашими локальными данными ... аналогично предложению josephj1989

В качестве альтернативы (хотя могут быть проблемы с лицензированием) попробуйте RAC Clustering вашу локальную базу данных с удаленной базой данныхoplesoft.

1 голос
/ 25 июня 2010

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

0 голосов
/ 25 июня 2010

Мне кажется, что вы имеете дело с измерением типа 2 в хранилище данных.Существует несколько способов реализации измерения типа 2, в большинстве случаев с такими столбцами, как ValidFrom, ValidTo, Version, Status.Не все они присутствуют всегда, было бы интересно, если бы вы могли опубликовать схему для своей таблицы.Вот пример того, как это может выглядеть (Джон Смит переехал из Индианы в Огайо в 2010-06-24)

UserKey  UserBusinessKey State    ValidFrom    ValidTo   Version  Status
7234     John_Smith_17   Indiana  2005-03-20  2010-06-23    1     expired
9116     John_Smith_17   Ohio     2010-06-24  3000-01-01    2     current

Для получения последней версии строки обычно используют

WHERE Status = 'current'

или

WHERE ValidTo = '3000-01-01'

Обратите внимание, что у этого есть некоторая константа в будущем.

или

WHERE ValidTo > CURRENT_DATE

Кажется, что ваш пример используетValidFrom (ffective_date), поэтому вы вынуждены найти max(), чтобы найти последнюю строку.Посмотрите на схему - есть ли Status or ValidTo эквивалентов в ваших таблицах?

0 голосов
/ 25 июня 2010

Является ли стол PeopleSoft доставленным, или это заказ? Вы уверены, что это физическая таблица, а не плохо написанное представление со стороны PS? Если это поставленная вами запись (пример выглядит очень похоже на PS_JOB или представление, которое ссылается на него), возможно, вы могли бы указать это. PS_JOB - зверь с тоннами поставленных индексов, и большинство сайтов добавляют еще больше.

Если вы знаете индексы в таблице, вы можете использовать подсказки Oracle, чтобы указать предпочтительный индекс для использования; это иногда помогает.

Составили ли вы план объяснения, чтобы определить, в чем проблема? Может быть, есть декартово объединение, полное сканирование таблицы и т. Д.?

0 голосов
/ 25 июня 2010

Можете ли вы вытянуть строки с желаемыми идентификаторами user_id в свою собственную таблицу, создавая только необходимые индексы для поддержки ваших запросов и выполнения ваших запросов к ней?

0 голосов
/ 25 июня 2010

Будет ли возможность создать базу данных, которую вы используете для вещей нехранилищного типа, которые вы могли бы обновлять еженедельно? Если это так, вы можете создать ночной процесс, который будет перемещаться только по самым последним записям. Это избавило бы от того, что вы МАКСИМАЛЬНО выполняете для ежедневных запросов, и значительно сократило бы количество или записи.

Кроме того, зависит от того, можете ли вы иметь 1 день между самыми последними данными и тем, что доступно.

Я не очень знаком с Oracle, поэтому может быть способ добиться улучшений, внеся изменения в ваш запрос также ...

0 голосов
/ 25 июня 2010

Вместо использования подзапросов вы можете попробовать это.Я не знаю, будет ли Oracle работать лучше с этим или нет, так как я не пользуюсь Oracle много.

SELECT
    ST1.col1,
    ST1.col2,
    ...
FROM
    Some_Table ST1
LEFT OUTER JOIN Some_Table ST2 ON
    ST2.user_id = ST1.user_id AND
    (
        ST2.effective_date > ST1.effective_date OR
        (
            ST2.effective_date = ST1.effective_date AND
            ST2.effective_sequence > ST1.effective_sequence
        )
    )
WHERE
    ST2.user_id IS NULL

Другое возможное решение будет:

SELECT
    ST1.col1,
    ST1.col2,
    ...
FROM
    Some_Table ST1
WHERE
    NOT EXISTS
    (
        SELECT
        FROM
            Some_Table ST2
        WHERE
            ST2.user_id = ST1.user_id AND
            (
                ST2.effective_date > ST1.effective_date OR
                (
                    ST2.effective_date = ST1.effective_date AND
                    ST2.effective_sequence > ST1.effective_sequence
                )
            )
    )
...