Ускорьте индексирование текста Oracle или позвольте индексу работать только при малой нагрузке

Мы используем индекс Oracle Text CTXSYS.CONTEXT для индексации около полумиллиона строк, содержащих метаинформацию. Информация распространяется на две таблицы, которые объединены процедурой, которую вызывает индексатор во время выполнения (функциональный индекс).

Когда я запускаю CREATE INDEX на моей локальной машине (простой двухъядерный ноутбук), индекс создается примерно через 3 минуты. На нашем сервере БД, который работает на Solaris с 8 ядрами и 16 ГБ ОЗУ, требуется 24 часа для создания индекса для тех же (точно таких же) данных.

Пример кода: Это наш индексный фидер для двух таблиц и трех столбцов:

create or replace procedure docmeta_revisions_text_feeder 
    ( p_rowid in rowid , p_clob in out nocopy clob) as v_clob CLOB begin
    FOR c1 IN (select DM.DID, DM.XDESCRIB || ' ' || DM.XAUTHOR AS data
        from DOCMETA DM
        WHERE ROWID = p_rowid) 
    LOOP
        v_clob := v_clob || c1.data;
        FOR c2 IN (
            SELECT ' ' || RV.DDOCTITLE AS data
            FROM   REVISIONS RV
            WHERE  RV.DID = c1.DID)
        LOOP
            v_clob := v_clob || c2.data;
        END LOOP;
    END LOOP;
    p_clob := v_clob;    
    end docmeta_revisions_text_feeder

Это настройки

BEGIN
CTX_DDL.CREATE_PREFERENCE ('concat_DM_RV_DS', 'USER_DATASTORE');
CTX_DDL.SET_ATTRIBUTE ('concat_DM_RV_DS', 'PROCEDURE',
'docmeta_revisions_text_feeder');
 END;

Теперь мы создаем индекс

CREATE INDEX concat_DM_RV_idx ON DOCMETA (FULLTEXTIDX_DUMMY)
INDEXTYPE IS CTXSYS.CONTEXT
PARAMETERS ('datastore concat_DM_RV_DS 
section group CTXSYS.AUTO_SECTION_GROUP
') PARALLEL 4;

Данные в основном состоят из простого названия или имени автора + краткое описание с < 1k.

Я попытался немного поиграть с задействованными параметрами памяти и параметром PARALLEL, но не имел успеха. Итак, вот мои вопросы:

  • Есть ли способ приостановить и возобновить процесс индексирования (у меня есть роль CTX_SYS)?
  • У кого-нибудь есть подсказка, какой параметр можно настроить (например, размер памяти)?
  • Можно ли экспортировать и импортировать текстовый индекс? → Затем я смог выполнить индексацию на своей локальной машине и просто скопировать ее на наш сервер
  • может ли указатель работать с "более низким приоритетом"?
  • возможно, что индексатор был нарушен операциями блокировки (это промежуточная машина, в которой другой доступ осуществляется параллельно). Есть ли способ заблокировать задействованные таблицы, создать индекс и разблокировать их впоследствии?

Ответ 1

Наконец мы выяснили, как сделать расщепленную синхронизацию индекса. Вот несколько основных шагов, которые показывают, что мы сделали:

CREATE INDEX concat_DM_RV_idx ON DOCMETA (FULLTEXTIDX_DUMMY)
INDEXTYPE IS CTXSYS.CONTEXT
PARAMETERS ('datastore concat_DM_RV_DS section group CTXSYS.AUTO_SECTION_GROUP
NOPOPULATE
');

см. параметр NOPOPULATE? который говорит, что он не должен запускать процесс заполнения/индексации. Если вы находитесь на 11g, теперь у вас есть очень хорошая функция CTX_DDL, которая заполняет индекс по своему усмотрению, а именно процедуру "POPULATE_PENDING". Вызов его по имени индекса будет заполнять таблицу CTXSYS, которая содержит строки, которые были изменены и, следовательно, не синхронизированы. Обратите внимание, что после вызова этого метода индексатор все еще ничего не запускал. Поскольку 10g (?), Соответствующая процедура CTX_DDL.SYNC_INDEX имеет несколько дополнительных параметров, например. параметр "maxtime". Предоставьте его, скажем, 4H, и ваш индексщик начнет синхронизировать ожидающие строки в течение примерно 4 часов. Вы повторяете эту процедуру по расписанию и выполняете.

Это не работает в 9i, к сожалению. Поэтому мы успешно попытались "имитировать" процесс Oracle POPULATE_PENDING. Единственное ограничение на этот метод: вам нужен уникальный идентификатор строки, чтобы иметь возможность запрашивать куски одного и того же контента из вашей таблицы. Вот что мы сделали:

1.) Создайте индекс с помощью NOPOPULATE (см. выше) 2.) Станьте SYS/DBA/CTXSYS (да, вы можете называть своего администратора для этого). Узнайте идентификатор, который имеет только что созданный индекс, запросив метаданные индекса:

SELECT IDX_ID FROM CTXSYS.CTX_INDEXES WHERE IDX_NAME ='concat_DM_RV_idx';

3.) обратите внимание на идентификатор индекса, который уступает желтому фрагменту бумаги и выполняет этот оператор вставки как роль CTXSYS и заменяет < > вашим идентификатором индекса и < > именем таблицы, которое индекс построен. Уникальным идентификатором строки может быть какой-то идентификатор документа или любой счетный оператор, который создает уникальный фрагмент данных вашей таблицы:

INSERT INTO CTXSYS.DR$PENDING (PND_CID,PND_PID,PND_ROWID,PND_TIMESTAMP)
SELECT <<your index id>>, 0, <<basetable name>>.ROWID, CURRENT_DATE
FROM gsms.DOCMETA
WHERE <<basetable unique row identifier>> < 50000;
COMMIT; -- Dont forget the COMMIT! DONT FORGET IT!!! WE MEAN IT!

"50.000" обозначает количество строк в зависимости от ограниченности вашей базовой таблицы, которая будет вставлена ​​в таблицу ожидающих строк в качестве полезной нагрузки для индексатора. Отрегулируйте его для ваших нужд.

4.) Теперь мы настроены на то, чтобы индексир проиграл.

CALL CTX_DDL.SYNC_INDEX(
  'CONCAT_DM_RV_IDX', -- your index name here
  '100M', -- memory count
  NULL, -- param for partitioned idxes
  2 -- parallel count
);

запустит процесс индексирования по любому счету строк, которые вы вставили на шаге 3.) Чтобы выполнить следующий шаг повтора шага 3.) со следующими 50 000 строк ( "где id между 50 000 и 100 000" )

Если вы случайно запустили индексатор в том же наборе строк, индекс сильно фрагментируется. Единственный способ его очистки - оптимизировать индекс с помощью параметра REBUILD. На нашей локальной машине это было очень быстро, так как индексиру не нужно запускать, но только переупорядочивает содержимое индексных таблиц:

CALL CTX_DDL.OPTIMIZE_INDEX('CONCAT_DM_RV_IDX', 'REBUILD');

Если вам нужна метаинформация о состоянии и размере индексации, вы можете запросить пакет CTX_REPORT:

SELECT CTX_REPORT.INDEX_SIZE('CONCAT_DM_RV_IDX') FROM DUAL;

И если вы забыли, какие параметры вы выбрали при индексировании:

SELECT * FROM CTXSYS.CTX_PARAMETERS;

Счастливое индексирование!