Улучшение времени DELETE и INSERT для большой таблицы, имеющей структуру индекса - PullRequest
3 голосов
/ 27 февраля 2012

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

Структура этой таблицы в основном следующая:

CREATE TABLE per_user_result_set
           ( user_login         VARCHAR2(N)
           , result_set_item_id VARCHAR2(M)
           , CONSTRAINT result_set_pk PRIMARY KEY(user_login, result_set_item_id)
           )
           ;

Типичный пользователь нашего приложения будет иметь этонабор результатов вычисляется 30 раз в день, причем набор результатов состоит из 1 одного элемента и 500 000 элементов.Типичный клиент объявляет около 500 пользователей в производственной базе данных.Таким образом, эта таблица обычно состоит из 5 миллионов строк.

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

BEGIN
    DELETE FROM per_user_result_set WHERE user_login = :x;
    INSERT INTO per_user_result_set(...) SELECT :x, ... FROM ...;
END;
/

После того, как возникнут проблемы с производительностью (часть DELETE будетЭто заняло много времени) мы решили создать ГЛОБАЛЬНУЮ ВРЕМЕННУЮ ТАБЛИЦУ (при фиксации удаления строк) для хранения «дельты» строк для подавления из таблицы и строк для вставки в нее:

BEGIN
    INSERT INTO _tmp
    SELECT ... FROM ...
     MINUS SELECT result_set_item_id
             FROM per_user_result_set
            WHERE user_login = :x;

    DELETE FROM per_user_result_set
          WHERE user_login = :x
            AND result_set_item_id NOT IN (SELECT result_set_item_id
                                             FROM _tmp
                                          );
    INSERT INTO per_user_result_set
    SELECT :x, result_set_item_id
      FROM _tmp;

    COMMIT;
END;
/

Это улучшилосьпроизводительность немного, но все же это не является удовлетворительным.Итак, мы исследуем способы ускорить этот процесс, и вот проблемы, с которыми мы сталкиваемся:

  • Мы бы хотели использовать разбиение таблиц (разбиение user_login).Но разбиение не всегда доступно (в наших тестовых базах мы нажимаем ORA-00439).Наши клиенты не могут позволить себе Oracle Enterprise Edition с платными дополнительными функциями.
  • Мы могли бы сделать таблицу per_user_result_set GLOBAL TEMPORARY, чтобы она была изолированной, и мы могли TRUNCATE, например, ... но наше приложение иногда теряетподключение к Oracle из-за проблем с сетью, и будет автоматически переподключен.К этому времени мы теряем содержимое наших вычислений.
  • Мы могли бы разбить эту таблицу на определенное количество сегментов, сделать вид, что UNION объединит ВСЕ все эти сегменты, и вызовет INSTEAD OF UPDATE и DELETE в этом представлении,и перекомпоновать строки в соответствии с ORA_HASH(user_login) % num_buckets.Но мы боимся, что это может сделать SELECT операций намного медленнее.Это привело бы к постоянному количеству таблиц с меньшими индексами, затронутыми в операциях DELETE или INSERT.Короче говоря, «таблица деления на бедных».
  • Мы пытались ALTER TABLE per_user_result_set NOLOGGING.Это не сильно улучшает ситуацию.
  • Мы пытались CREATE TABLE ... ORGANIZATION INDEX COMPRESS 1.Это ускоряет процесс в соотношении 1: 5.
  • Мы пытались иметь одну таблицу на user_login.Это именно то, что мы могли бы получить, разделив, используя количество разделов, равное количеству различных user_logins и хорошо выбранную хеш-функцию.Коэффициент производительности 1:10.Но я действительно хотел бы избежать этого решения: приходится поддерживать огромное количество индексов, таблиц, представлений для каждого пользователя.Это было бы интересным приростом производительности для пользователей, но не для нас, сопровождающих системы.
  • Поскольку пользователи работают одновременно, мы не можем создать новую таблицу и заменить ее на старую.один.

Что вы могли бы предложить в дополнение к этим подходам?

Примечание.Наши клиенты работают с базами данных Oracle с 9i до 11g, а с XE выпускают до версии Enterprise.Это широкий спектр версий, с которыми мы должны быть совместимы.

Спасибо.

Ответы [ 2 ]

1 голос
/ 01 марта 2012

Если бы все ваши пользователи были в 11g Enterprise Edition, я бы порекомендовал вам использовать встроенное в Oracle кэширование набора результатов вместо того, чтобы пытаться свернуть свое собственное.Но это не так, поэтому давайте двигаться дальше.

Еще одним привлекательным вариантом может быть использование коллекций PL / SQL, а не таблиц.Находясь в памяти, они быстрее извлекаются и требуют меньше обслуживания.Они также поддерживаются во всех нужных вам версиях.Тем не менее, они являются переменными сеанса, поэтому, если у вас много пользователей с большими наборами результатов, которые могут повлиять на распределение PGA.Также их данные будут потеряны, когда сетевое соединение обрывается.Так что это, вероятно, не то решение, которое вы ищете.

Суть вашей проблемы заключается в следующем утверждении:

DELETE FROM per_user_result_set WHERE user_login = :x;

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

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

«[это] было бы интересным повышением производительности для пользователей, но не для нас, сопровождающих системы», *

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

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

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

Таким образом, ваша процедура обновления будет выглядеть следующим образом:

BEGIN
    TRUNCATE TABLE per_user_result_set REUSE STORAGE;
    INSERT INTO per_user_result_set(...) 
          SELECT ...  FROM  ...;
    DBMS_STATS.GATHER_TABLE_STATS(user
          , 'PER_USER_RESULT_SET'
          , estimate_percent=>10);
    COMMIT;
END;
/

Обратите внимание, что вам больше не нужно включать столбец USER, поэтому в таблице yur будет только один столбец result_set_item_id (еще один признак пригодности IOT.

Сбор статистики по таблицене является обязательным, но это целесообразно. У вас есть широкий разброс в размере наборов результатов, и вы не хотите использовать план выполнения, разработанный для 500000 строк, когда таблица имеет только одну строку, или наоборот.

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

1 голос
/ 27 февраля 2012

Мы попытались создать одну таблицу на user_login. Это именно то, что мы может быть путем разделения с использованием количества разделов, равных количество отдельных user_logins и хорошо выбранная хеш-функция. Коэффициент производительности 1:10. Но я бы очень хотел этого избежать Решение: приходится поддерживать огромное количество индексов, таблиц, представлений, на для каждого пользователя. Это было бы интересным приростом производительности для пользователи, но не мы, сопровождающие системы.

Можете ли вы затем создать хранимую процедуру для генерации этих таблиц для каждого пользователя? Или, что еще лучше, эта хранимая процедура выполняет наиболее подходящие действия в зависимости от поддерживаемого лицензирования Oracle?

If Partitioning option 
  then create or truncate user-specific list partition
Else 
  drop user-specific result table
  Create user-specific result table 
      as Select from template result table
  create indexes
  create constraints
  perform grants
end if
Perform insert
...