Наше приложение управляет таблицей, содержащей набор строк для каждого пользователя, который является результатом запроса, требующего больших вычислительных ресурсов. Сохранение этого результата в таблице кажется хорошим способом ускорить дальнейшие вычисления.
Структура этой таблицы в основном следующая:
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 раз в день, с набором результатов, состоящим из одного элемента до 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
ГЛОБАЛЬНОЙ ВРЕМЕННОЙ, чтобы она была изолирована, и мы могли бы, например,TRUNCATE
... но наше приложение иногда теряет соединение с Oracle из-за проблем с сетью и автоматически подключается снова. К тому времени мы теряем содержимое наших вычислений. - Мы могли бы разбить эту таблицу на определенное количество сегментов, создать представление, которое объединяет ВСЕ эти сегменты и запускает 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. Это широкий спектр версий, с которыми мы должны быть совместимы.
Спасибо.
DELETE
. - person Benoit   schedule 27.02.2012