Решение секционирования Oracle для проблемы удалить производительность
это последующий вопрос к Стратегия повышения производительности Oracle DELETE. Напомним, что у нас есть большая БД с иерархией таблиц, представляющих 1D через 4D выходные данные из системы оптимизации. Чтение и запись этих данных происходит быстро и обеспечивает удобные средства для наших различных систем для использования информации.
однако удаление неиспользуемых данных стало медведем. Текущая иерархия таблиц приведена ниже.
/* Metadata tables */
Case(CaseId, DeleteFlag, ...) On Delete Cascade CaseId
OptimizationRun(OptId, CaseId, ...) On Delete Cascade OptId
OptimizationStep(StepId, OptId, ...) On Delete Cascade StepId
/* Data tables */
Files(FileId, CaseId, Blob) /* deletes are near instantateous here */
/* Data per run */
OnedDataX(OptId, ...)
TwoDDataY1(OptId, ...) /* packed representation of a 1D slice */
/* Data not only per run, but per step */
TwoDDataY2(StepId, ...) /* packed representation of a 1D slice */
ThreeDDataZ(StepId, ...) /* packed representation of a 2D slice */
FourDDataZ(StepId, ...) /* packed representation of a 3D slice */
/* ... About 10 or so of these tables exist */
что я поиск-это средство разбиения Case
данные такие, что я мог бы удалить раздел, относящийся к делу, чтобы удалить его данные. В идеале,OptimizationRun
будет иметь раздел интервала на основе CaseId
и это будет просачиваться вниз к своим детям. Тем не менее, 11g не поддерживает комбинацию интервального и рефракционного разбиения.
Я довольно уверен, что включить движение строк не может быть и речи на основе размера БД и требования, чтобы табличные пространства жили в ASSM. Возможно Разбиение диапазона на OptimizationRun
и REF разбиение на остальные?
Я предполагаю, что с этой стратегией мне понадобится триггер, который выполняет что-то вроде следующего:
CREATE OR REPLACE TRIGGER Case_BeforeInsert_MakePartitions
BEFORE INSERT
ON Case
FOR EACH ROW
DECLARE
v_PartName varchar(64) := 'CASE_OPTPART_' || :new.CaseId;
v_PartRange Case.CaseId%type := :new.CaseId
BEGIN
-- Take :new.CaseId and create the partition
ALTER TABLE OptimizationRun
ADD PARTITION v_PartName
VALUES LESS THAN ( v_PartRange );
END;
а затем необходимый триггер для Перед удалением:
CREATE OR REPLACE TRIGGER Case_BeforeDelete_RemovePartitions
BEFORE DELETE
ON Case
FOR EACH ROW
DECLARE
v_PartName varchar(64) := 'CASE_OPTPART_' || :old.CaseId;
BEGIN
-- Drop the partitions associated with the case
ALTER TABLE OptimizationRun
DROP PARTITION v_PartName;
END;
хорошая идея? Или это идея из рекламы джинсов SNL Bad Idea?
обновление, для справки в размере:
- таблицы данных 1D ~1,7 г
- 2D таблицы данных ~12.5 G
- 3D таблицы данных ~117.3 G
- таблицы данных 4D ~315.2 G
2 ответов
Я уверен, что вы на правильном пути с partitionning, чтобы справиться с проблемой производительности удаления. Однако я не думаю, что вы сможете смешать это с триггерами. Сложная логика с триггерами всегда беспокоила меня, но помимо этого здесь есть проблемы, с которыми вы, вероятно, столкнетесь:
- операторы DDL нарушают логику транзакций, поскольку Oracle выполняет фиксацию текущей транзакции перед любым оператором DDL.
- к счастью, вы не можете фиксация в триггере (поскольку Oracle находится в середине операции, а DB не находится в согласованном состоянии).
- использование автономных транзакций для выполнения DDL было бы (плохо?) обходной путь для вставки, но вряд ли будет работать для удаления, поскольку это, вероятно, помешает логике каскада удаления.
было бы проще кодировать и проще поддерживать процедуры, которые касаются удаления и создания разделов, таких как as:
CREATE PROCEDURE add_case (case_id, ...) AS
BEGIN
EXECUTE IMMEDIATE 'ALTER TABLE OptimizationRun ADD partition...';
/* repeat for each child table */
INSERT INTO Case VALUES (...);
END;
Что касается удаления разделов, вам нужно будет проверить, работает ли это с ссылочной целостностью. Возможно, потребуется отключить ограничения внешнего ключа перед удалением секции родительской таблицы в связи родительская-дочерняя таблица.
также обратите внимание, что глобальные индексы будут оставлены в непригодном состоянии после удаления раздела. Вам придется перестроить их, если вы не укажете UPDATE GLOBAL в своем операторе drop (очевидно, это перестроит их автоматически, но займет больше времени).
невозможно - вы не можете выдавать DDL, как это в триггере уровня строки.
[возможно оформление комментария удалено, а имя]
вы рассматривали возможность распараллеливания вашего сценария? Вместо метельщика, который полагается на delete cascade, вместо этого используйте DBMS_SCHEDULER для параллелизации задания. Можно безопасно запускать параллельные удаления для таблиц на том же уровне дерева зависимостей.
begin
dbms_scheduler.create_program
(program_name => 'snapshot_purge_cases',
program_type => 'PLSQL_BLOCK',
program_action =>
'BEGIN
delete from purge$Case;
insert into purge$Case
select CaseId
from Case
where deleteFlag = 1;
delete from purge$Opt;
insert into purge$Opt
select OptId
from OptimizationRun
where CaseId in (select CaseId from purge$Case);
delete from purge$Step;
insert into purge$Step
select StepId
from OptimizationStep
where OptId in (select OptId from purge$Opt);
commit;
END;',
enabled => true,
comments => 'Program to snapshot keys for purging';
);
dbms_scheduler.create_program
(program_name => 'purge_case',
program_type => 'PLSQL_BLOCK',
program_action => 'BEGIN
loop
delete from Case
where CaseId in (select Case from purge$Case)
where rownum <= 50000;
exit when sql%rowcount = 0;
commit;
end loop;
commit;
END;',
enabled => true,
comments => 'Program to purge the Case Table'
);
-- repeat for each table being purged
end;
/
это только настройка программ. Что нам нужно сделать дальше, так это создать цепочку рабочих мест, чтобы мы могли собрать их вместе.
BEGIN
dbms_scheduler.create_chain
(chain_name => 'purge_case_chain');
END;
/
теперь мы делаем шаги в цепочке заданий, используя программы из прошлого:
BEGIN
dbms_scheduler.define_chain_step
(chain_name => 'purge_case_chain',
step_name => 'step_snapshot_purge_cases',
program_name => 'snapshot_purge_cases'
);
dbms_scheduler.define_chain_step
(chain_name => 'purge_case_chain',
step_name => 'step_purge_cases',
program_name => 'purge_case'
);
-- repeat for every table
END;
/
теперь мы должны связать цепные шаги вместе. Рабочие места будут веером, вот так:
- снимок
CaseIds
,OptIds
иStepIds
очистить. - очистить все таблицы зависели от
OptimizationStep.
- очистить все таблицы зависит от
OptimizationRun.
- очистить все таблицы зависели от
Case.
- очистить
Case.
таким образом, код будет:
begin
dbms_scheduler.define_chain_rule
(chain_name => 'purge_case_chain',
condition => 'TRUE',
action => 'START step_snapshot_purge_cases',
rule_name => 'rule_snapshot_purge_cases'
);
-- repeat for every table dependent on OptimizationStep
dbms_scheduler.define_chain_rule
(chain_name => 'purge_case_chain',
condition => 'step_snapshot_purge_cases COMPLETED',
action => 'START step_purge_TwoDDataY2',
rule_name => 'rule_purge_TwoDDataY2'
);
-- repeat for every table dependent on OptimizationRun
dbms_scheduler.define_chain_rule
(chain_name => 'purge_case_chain',
condition => 'step_purge_TwoDDataY2 COMPLETED and
step_purge_ThreeDDataZ COMPLETED and
... ',
action => 'START step_purge_OnedDataX',
rule_name => 'rule_purge_OnedDataX'
);
-- repeat for every table dependent on Case
dbms_scheduler.define_chain_rule
(chain_name => 'purge_case_chain',
condition => 'step_purge_OneDDataX COMPLETED and
step_purge_TwoDDataY1 COMPLETED and
... ',
action => 'START step_purge_Files',
rule_name => 'rule_purge_Files'
);
dbms_scheduler.define_chain_rule
(chain_name => 'purge_case_chain',
condition => 'step_purge_Files COMPLETED and
step_purge_OptimizationRun COMPLETED and
... ',
action => 'START step_purge_Case',
rule_name => 'rule_purge_Case'
);
-- add a rule to end the chain
dbms_scheduler.define_chain_rule
(chain_name => 'purge_case_chain',
condition => 'step_purge_Case COMPLETED',
action => 'END',
rule_name => 'rule_purge_Case'
);
end;
/
включить цепочку заданий:
BEGIN
DBMS_SCHEDULER.enable ('purge_case_chain');
END;
/
вы можете запустить цепочку вручную:
BEGIN
DBMS_SCHEDULER.RUN_CHAIN
(chain_name => 'chain_purge_case',
job_name => 'chain_purge_case_run'
);
END;
/
или создать задание, чтобы запланировать его:
BEGIN
DBMS_SCHEDULER.CREATE_JOB (
job_name => 'job_purge_case',
job_type => 'CHAIN',
job_action => 'chain_purge_case',
repeat_interval => 'freq=daily',
start_date => ...
end_date => ...
enabled => TRUE);
END;
/