Гость
Map
Форумы / Oracle [игнор отключен] [закрыт для гостей] / Одновременное создание множества индексов / 14 сообщений из 14, страница 1 из 1
31.12.2021, 00:56
    #40124295
Arez
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Одновременное создание множества индексов
Под Новый Год решил поделиться нашим решением проблемы быстрого создания нескольких индексов для таблицы, может кому пригодится.
Версия тогда была кажется 11.2.

Итак, проблема:
Каждые полчаса образуется очередная секция таблицы фактов, как - неважно. Важно, что секция не имеет локальных bitmap-индексов, и их нужно построить, всего 27 штук. При этом даже просто полное, серийное, чтение секции занимает 8 минут. Простая арифметика показывает, что потребуется как минимум 27*8 = 3.5 часа чтобы просто прочитать таблицу для каждого индекса. Не вариант.

Естественно, первым делом попробовали parallel, но он не сильно помог. Если я правильно помню, параллелизм работает (работал?) на уровне партиций, а читать одну секцию параллельно Оракл не мог. Или были другие причины о которых я теперь забыл, в общем - все равно было слишком медленно.

Тогда пришла мысль запускать создание индексов параллельно в разных процессах, через job. Один процесс будет читать блоки в кэш-буфер, а остальные читать оттуда. Главное, чтобы всем хватило памяти для одновременной работы, и чтобы блоки не вытеснялись до того, как будут использованы всеми. И действительно, скорость существенно возросла: 20 индексов строились за 22 минуты, 27 - за 24.

В процессе, одна задача читала блоки, ожидая на "scattered read", а другие ждали ее на "read by other sesion". Время от времени какая-то задача отставала на сортировке "sort output wait", но потом быстро догоняла остальных.

Была пара ньюансов:
  • по умолчанию, чтение использовало "direct path read" в PGA сессии задачи, и блоки были недоступны для других сессий. Пришлось установить параметр _very_large_object_threshold и event 10949, чтобы чтение шло в SGA.
  • чтобы все задачи запускались на одном и том же экземпляре RAC, мы создали job class и привязали его к сервису, работающем на нужном узле:
Код: plsql
1.
2.
EXEC DBMS_SCHEDULER.create_job_class('CONCURRENT_INDEX_BUILD', service => 'index_builder_svc');
GRANT EXECUTE ON sys.CONCURRENT_INDEX_BUILD TO index_builder_user;



Сам код, запускающий индексирование, очень прост, он просто создает по задаче для каждого индекса. Вся хитрость в установке параметров сессии:

код
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
DECLARE
  v_tmpl VARCHAR2(1000) := q'[
begin
  execute immediate 'alter session set db_file_multiblock_read_count=256';
  execute immediate 'alter session set workarea_size_policy=manual';
  execute immediate 'alter session set sort_area_size = '||(75*1024*1024);   /* customize */
  execute immediate 'alter session set "_serial_direct_read" = false';
  execute immediate 'alter session set "_very_large_object_threshold" = '||(100*1024);
  execute immediate 'alter session set events = ''10949 trace name context forever, level 1''';
  execute immediate 'create bitmap index [IDX] on changeme_table_name([COL]) nologging noparallel local compute statistics tablespace change_me';
end;
]';
BEGIN
  FOR r IN (
    SELECT 'IDX_TZ_ID'     AS idx_name, 'TZ_ID'               AS col_name FROM dual UNION ALL
    SELECT 'IDX_FUNC_ID'   AS idx_name, 'FUNCTION_ID'         AS col_name FROM dual UNION ALL
... перечислены все 27 индексов
    SELECT 'IDX_DEAL_ID'   AS idx_name, 'DEAL_ID'             AS col_name FROM dual
  ) LOOP
    --dbms_scheduler.drop_job('IDX_BLD_'||r.idx_name, TRUE);
    dbms_scheduler.create_job(
        job_name   => 'IDX_BLD_'||r.idx_name
      , job_type   => 'PLSQL_BLOCK'
      , job_action => REPLACE(REPLACE(v_tmpl, '[IDX]', r.idx_name), '[COL]', r.col_name)
      , enabled    => TRUE
      , job_class  => 'CONCURRENT_INDEX_BUILD'  /* bind to RAC node */
      , comments   => 'Build index '||r.idx_name||' on changeme_table_name.'||r.col_name
    );
  END LOOP;
END;




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

Навскидку я не нашел похожих решений, впрочем я давно не занимаюсь Ораклом напрямую. Возможно, сейчас появились способы получше? Любопытно было бы узнать.
С Новым Годом!
...
Рейтинг: 0 / 0
31.12.2021, 18:55
    #40124345
flexgen
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Одновременное создание множества индексов
ArezКаждые полчаса образуется очередная секция таблицы фактов, как - неважно. Важно, что секция не имеет локальных bitmap-индексов, и их нужно построить, всего 27 штук. При этом даже просто полное, серийное, чтение секции занимает 8 минут. Простая арифметика показывает, что потребуется как минимум 27*8 = 3.5 часа чтобы просто прочитать таблицу для каждого индекса. Не вариант.

Естественно, первым делом попробовали parallel, но он не сильно помог. Если я правильно помню, параллелизм работает (работал?) на уровне партиций, а читать одну секцию параллельно Оракл не мог. Или были другие причины о которых я теперь забыл, в общем - все равно было слишком медленно.

Я не очень понял что имеется ввиду под секцией и что под партицией? Или разговор идет о partitioned table? Далее, я правильно понял, в таблице каждые полчаса создается новый partition? И при этом в таблице нужно построить 27 (двадцать семь?!!!) bitmap индексов?
...
Рейтинг: 0 / 0
31.12.2021, 20:51
    #40124352
Arez
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Одновременное создание множества индексов
flexgenЯ не очень понял что имеется ввиду под секцией и что под партицией? Или разговор идет о partitioned table? Да. Пытался везде использовать слово "секция" для обозначения partition, но не удержался :)
И там еще "чтобы все задачи запускались на одном и том же экземпляре RAC" - конечно же на узле (node)

flexgen Далее, я правильно понял, в таблице каждые полчаса создается новый partition? И при этом в таблице нужно построить 27 (двадцать семь?!!!) bitmap индексов? Именно. Приходил из external table -- с данными, но без индексов. И надо успеть построить локальные индексы.
...
Рейтинг: 0 / 0
01.01.2022, 04:40
    #40124368
Sayan Malakshinov
Модератор форума
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Одновременное создание множества индексов
Arez
по умолчанию, чтение использовало "direct path read" в PGA сессии задачи, и блоки были недоступны для других сессий. Пришлось установить параметр _very_large_object_threshold и event 10949, чтобы чтение шло в SGA.

Arez
Код: plsql
1.
2.
3.
  execute immediate 'alter session set "_serial_direct_read" = false';
  execute immediate 'alter session set "_very_large_object_threshold" = '||(100*1024);
  execute immediate 'alter session set events = ''10949 trace name context forever, level 1''';

на самом деле достаточно ивента 10949 или _serial_direct_read=never/false (c нюансом ). _VLOT менять не нужно, т.к. он используется только для принятий решения адаптивного serial direct reads.
А еще проще через alter table cache: https://alexanderanokhin.com/2018/10/22/_serial_direct_read-is-not-a-criteria-for-cursor-non-sharing/
Arez
27 - за 24.
Arez
Каждые полчаса образуется очередная секция
вообще, в целом выглядит как сизифов труд...
...
Рейтинг: 0 / 0
01.01.2022, 13:21
    #40124386
MaratIsk
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Одновременное создание множества индексов
Arez
Под Новый Год решил поделиться нашим решением проблемы быстрого создания нескольких индексов для таблицы, может кому пригодится.
Версия тогда была кажется 11.2.

Итак, проблема:
Каждые полчаса образуется очередная секция таблицы фактов, как - неважно. Важно, что секция не имеет локальных bitmap-индексов, и их нужно построить, всего 27 штук. При этом даже просто полное, серийное, чтение секции занимает 8 минут. Простая арифметика показывает, что потребуется как минимум 27*8 = 3.5 часа чтобы просто прочитать таблицу для каждого индекса. Не вариант.

Естественно, первым делом попробовали parallel, но он не сильно помог. Если я правильно помню, параллелизм работает (работал?) на уровне партиций, а читать одну секцию параллельно Оракл не мог. Или были другие причины о которых я теперь забыл, в общем - все равно было слишком медленно.

Тогда пришла мысль запускать создание индексов параллельно в разных процессах, через job. Один процесс будет читать блоки в кэш-буфер, а остальные читать оттуда. Главное, чтобы всем хватило памяти для одновременной работы, и чтобы блоки не вытеснялись до того, как будут использованы всеми. И действительно, скорость существенно возросла: 20 индексов строились за 22 минуты, 27 - за 24.

В процессе, одна задача читала блоки, ожидая на "scattered read", а другие ждали ее на "read by other sesion". Время от времени какая-то задача отставала на сортировке "sort output wait", но потом быстро догоняла остальных.

Была пара ньюансов:
  • по умолчанию, чтение использовало "direct path read" в PGA сессии задачи, и блоки были недоступны для других сессий. Пришлось установить параметр _very_large_object_threshold и event 10949, чтобы чтение шло в SGA.
  • чтобы все задачи запускались на одном и том же экземпляре RAC, мы создали job class и привязали его к сервису, работающем на нужном узле:
Код: plsql
1.
2.
EXEC DBMS_SCHEDULER.create_job_class('CONCURRENT_INDEX_BUILD', service => 'index_builder_svc');
GRANT EXECUTE ON sys.CONCURRENT_INDEX_BUILD TO index_builder_user;



Сам код, запускающий индексирование, очень прост, он просто создает по задаче для каждого индекса. Вся хитрость в установке параметров сессии:

код
Код: plsql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
DECLARE
  v_tmpl VARCHAR2(1000) := q'[
begin
  execute immediate 'alter session set db_file_multiblock_read_count=256';
  execute immediate 'alter session set workarea_size_policy=manual';
  execute immediate 'alter session set sort_area_size = '||(75*1024*1024);   /* customize */
  execute immediate 'alter session set "_serial_direct_read" = false';
  execute immediate 'alter session set "_very_large_object_threshold" = '||(100*1024);
  execute immediate 'alter session set events = ''10949 trace name context forever, level 1''';
  execute immediate 'create bitmap index [IDX] on changeme_table_name([COL]) nologging noparallel local compute statistics tablespace change_me';
end;
]';
BEGIN
  FOR r IN (
    SELECT 'IDX_TZ_ID'     AS idx_name, 'TZ_ID'               AS col_name FROM dual UNION ALL
    SELECT 'IDX_FUNC_ID'   AS idx_name, 'FUNCTION_ID'         AS col_name FROM dual UNION ALL
... перечислены все 27 индексов
    SELECT 'IDX_DEAL_ID'   AS idx_name, 'DEAL_ID'             AS col_name FROM dual
  ) LOOP
    --dbms_scheduler.drop_job('IDX_BLD_'||r.idx_name, TRUE);
    dbms_scheduler.create_job(
        job_name   => 'IDX_BLD_'||r.idx_name
      , job_type   => 'PLSQL_BLOCK'
      , job_action => REPLACE(REPLACE(v_tmpl, '[IDX]', r.idx_name), '[COL]', r.col_name)
      , enabled    => TRUE
      , job_class  => 'CONCURRENT_INDEX_BUILD'  /* bind to RAC node */
      , comments   => 'Build index '||r.idx_name||' on changeme_table_name.'||r.col_name
    );
  END LOOP;
END;




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

Навскидку я не нашел похожих решений, впрочем я давно не занимаюсь Ораклом напрямую. Возможно, сейчас появились способы получше? Любопытно было бы узнать.
С Новым Годом!


очередной автоматизатор ))
...
Рейтинг: 0 / 0
01.01.2022, 15:24
    #40124395
flexgen
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Одновременное создание множества индексов
Arez
Приходил из external table -- с данными, но без индексов. И надо успеть построить локальные индексы.


Т.е. что мы имеем - есть partitioned table, куда грузятся данные из external table. В таблице нет индексов. После загрузки данных необходимо построить 27 индексов.

Почему не сделать следующее:
  • Создать все необходимые индексы на пустой partitioned table
  • Создать stage таблицу с такой же структурой, как и вышеупомянутая таблица
  • Загрузить данные из external table в stage table
  • Выполнить exchange partition между stage и partitioned tables с опцией update indexes
Единственная система, в которой я видел такое количество индексов, был Siebel, в той версии что стоит у нас в конторе, около 26 тысяч индексов, в среднем около 20 индексов на таблицу, но ни одного bitmap. Больше я такого нигде не видел.
...
Рейтинг: 0 / 0
01.01.2022, 19:57
    #40124413
Arez
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Одновременное создание множества индексов
Sayan Malakshinovна самом деле достаточно ивента 10949 или _serial_direct_read=never/falseСпасибо, по-существу.

Sayan Malakshinovвообще, в целом выглядит как сизифов труд... MaratIskочередной автоматизатор ))Вот такое хреновое лето

flexgenСоздать все необходимые индексы на пустой partitioned tableТ.е. создать индексы перед загрузкой, и пусть они сами обновляются. Эксперименты показали, что так было сильно медленнее, не вписывались в рамки. Вообще, построение индексов потом - это такое общепринятое колдунство. Хотя, признаться, я уже давно не пробовал пере-проверить этот тезис.

flexgenКстати, я все еще не понимаю зачем такое количество bitmap индексов. При таких массивных изменениях, новый partition каждые полчаса, разумнее использовать b-tree индексыИндексы - вне рамок, аналитике надо было. В мире DWH это даже мало.
"Массивные изменения" - заметь, индексы все локальные. Глобальные при таких объемах строго противопоказаны.

Про преимущество btree не понял. Тогда оптимизатор не умел толком сливать индексы при запросах вида
Код: plaintext
a = 'x' and/or b = 'y'...
Сейчас что-то изменилось?


PS: А вообще, это по большому счету все костыли. Было бы интересно узнать, не появились ли в Oracle средства сделать подобное штатными средствами. Все-таки такие DIY-выкрутасы уж больно хрупкие.
Секретный параметр
Код: plaintext
_work_fast=true
:)
...
Рейтинг: 0 / 0
01.01.2022, 21:35
    #40124418
flexgen
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Одновременное создание множества индексов
Arez
Т.е. создать индексы перед загрузкой, и пусть они сами обновляются. Эксперименты показали, что так было сильно медленнее, не вписывались в рамки. Вообще, построение индексов потом - это такое общепринятое колдунство.

Похоже, ты не очень внимательно прочитал то, что я написал. Я предложил грузить данные в stage таблицу, а потом произвести exchange partition с опцией update indexes.
Индексы - вне рамок, аналитике надо было. В мире DWH это даже мало.

А такая штука как fact tables твоим аналитикам известна? И потом, зачастую, при больших объемах данных, full scan предпочтительнее использования индексов. Но тут надо на сами запросы смотреть.
"Массивные изменения" - заметь, индексы все локальные. Глобальные при таких объемах строго противопоказаны.

Про глобальные индексы я и не говорил.
Про преимущество btree не понял.

Если твоя база чистая DWH - тогда лучше использовать bitmap, при условии что данные в таблицах обновляются редко, раз в месяц, к примеру.
Если же у тебя OLTP, или, что более вероятно, смешанного назначения, с высокой частотой обновления данных, то оптимальнее использовать b-tree.
Опять-таки, все очень индивидуально.
...
Рейтинг: 0 / 0
02.01.2022, 02:05
    #40124436
Arez
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Одновременное создание множества индексов
flexgen Похоже, ты не очень внимательно прочитал то, что я написал. Я предложил грузить данные в stage таблицу, а потом произвести exchange partition с опцией update indexes. Хмм, поправь меня если я что-то пропустил, но процесс собственно и состоял в том, что данные сперва грузились в пустую таблицу из external table, по ним строились индексы, а потом через exchange partition вся эта трихомудрия подцеплялалсь к боевой таблице, по которой оналитеги гоняли свои "почти real-time" запросы.

flexgen А такая штука как fact tables твоим аналитикам известна? И потом, зачастую, при больших объемах данных, full scan предпочтительнее использования индексов. Но тут надо на сами запросы смотретьБро, ты, похоже, начинаешь открывать для себя мир DWH, и очень здраво понимаешь что к чему. Обычно все так и есть как ты говоришь. Добро пожаловать!
...
Рейтинг: 0 / 0
02.01.2022, 03:54
    #40124439
Sayan Malakshinov
Модератор форума
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Одновременное создание множества индексов
Arez
Тогда оптимизатор не умел толком сливать индексы при запросах вида
Код: plaintext
a = 'x' and/or b = 'y'...
В 11.2-то? Умел, конечно. Что в общем-то не отменяет своих плюсов битмап индексов.

flexgen
При таких массивных изменениях, новый partition каждые полчаса, разумнее использовать b-tree индексы
bitmap индексы плохи при апдейтах, а при создании/инсертах ничего страшного. Так чем разумнее b-tree?

flexgen
exchange partition с опцией update indexes
Я разбирал/тюнил "update indexes": на самом деле, он просто перебирает индексы и по одному ребилдит их, что в итоге и будет 27 раз сканить таблицу. При больших таблицах/секциях это даже легко заметно: тупо видно, что очередной индекс стал валидным и сколько еще осталось.
...
Рейтинг: 0 / 0
02.01.2022, 04:06
    #40124440
Sayan Malakshinov
Модератор форума
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Одновременное создание множества индексов
flexgen
[li]Создать все необходимые индексы на пустой partitioned table
  • Создать stage таблицу с такой же структурой, как и вышеупомянутая таблица
что-то я тут подзапутался, зачем вообще exchange? Только ради update indexes, который все равно их последовательно ребилдит? Индексы можно создать просто с unusable или сделать unusable только на новой секции и заливать прямо туда, а потом просто отребилдить их на месте. Эдак и мув с update indexes можно сделать - еще и пожать табличку, например.
...
Рейтинг: 0 / 0
02.01.2022, 19:27
    #40124492
flexgen
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Одновременное создание множества индексов
Arez
Бро, ты, похоже, начинаешь открывать для себя мир DWH, и очень здраво понимаешь что к чему. Обычно все так и есть как ты говоришь. Добро пожаловать!

Ну да, именно сейчас и открываю (сарказм). Это после 27 лет работы админом Oracle, последние 15 из них в том числе и с DWH системами.
...
Рейтинг: 0 / 0
02.01.2022, 20:04
    #40124500
flexgen
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Одновременное создание множества индексов
Sayan Malakshinov
что-то я тут подзапутался, зачем вообще exchange? Только ради update indexes, который все равно их последовательно ребилдит? Индексы можно создать просто с unusable или сделать unusable только на новой секции и заливать прямо туда, а потом просто отребилдить их на месте. Эдак и мув с update indexes можно сделать - еще и пожать табличку, например.

Это уже вопрос подхода к проблеме, каждый решает по-своему.
...
Рейтинг: 0 / 0
04.01.2022, 15:52
    #40124668
Alexander Anokhin
Гость
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Одновременное создание множества индексов
Sayan Malakshinov
Arez
по умолчанию, чтение использовало "direct path read" в PGA сессии задачи, и блоки были недоступны для других сессий. Пришлось установить параметр _very_large_object_threshold и event 10949, чтобы чтение шло в SGA.

Arez
Код: plsql
1.
2.
3.
  execute immediate 'alter session set "_serial_direct_read" = false';
  execute immediate 'alter session set "_very_large_object_threshold" = '||(100*1024);
  execute immediate 'alter session set events = ''10949 trace name context forever, level 1''';

на самом деле достаточно ивента 10949 или _serial_direct_read=never/false (c нюансом ). _VLOT менять не нужно, т.к. он используется только для принятий решения адаптивного serial direct reads.
А еще проще через alter table cache: https://alexanderanokhin.com/2018/10/22/_serial_direct_read-is-not-a-criteria-for-cursor-non-sharing/
Arez
27 - за 24.
Arez
Каждые полчаса образуется очередная секция
вообще, в целом выглядит как сизифов труд...


К сожалению, в том блог посте есть ошибки.
1. _serial_direct_read=FALSE это эквивалент AUTO, не NEVER
2. свойство таблицы CACHE не учитывается при serial direct reads, это жук и будет скоро пофиксен.
...
Рейтинг: 0 / 0
Форумы / Oracle [игнор отключен] [закрыт для гостей] / Одновременное создание множества индексов / 14 сообщений из 14, страница 1 из 1
Целевая тема:
Создать новую тему:
Автор:
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]