|
Арап Нанда
Выбор схем секционирования
(Partition Decisions, By Arup Nanda)
Источник: журнал Oracle Magazine, September/October 2006,
http://www.oracle.com/technology/oramag/oracle/06-sep/o56partition.html
Понимайте стратегию секционирования, выбирайте наилучшую для ваших данных схему секционирования.
В сервере Oracle Database средства секционирования хранимых сегментов данных, таких как таблицы и индексы, позволяют упрощать управление и повышать производительность системы базы данных. Поддерживается много типов секционирования:
- по диапазонам ключей (range),
- списку значений ключа (list),
- хеш-секционирование (hash),
- диапазонное секционирование со списочным подсекционированием (range/list) и
- диапазонное секционирование с хеш-подсекционированием (range/hash).
Для проектировщиков баз данных создание секционированных объектов не представляет реальных проблем. Гораздо труднее принять решение, которое предшествует их созданию: какой тип секционирования использовать и какой столбец назначить ключом секционирования. В этой статье описывается, как выбрать наилучшую стратегию секционирования, включая выбор наилучшего типа и наилучшего столбца ключа секционирования.
Краткое введение в секционирование
Секционирование, если в двух словах, позволяет хранить сегмент данных, такой как таблица, в виде нескольких сегментов, сохраняя логическую монолитную структуру. Наиболее распространенный метод – диапазонное (range) секционирование, при котором для каждой секции определяется диапазон значений ключа секционирования. Например, можно использовать диапазонное секционирование таблицы сделок с клиентами TRANS, применяя в качестве ключа секционирования столбец TRANS_DT (дата транзакции), чтобы в первой секции хранились записи, которые находятся в диапазоне от 1 января 2005 г. до 31 марта 2005 г. (значения столбца TRANS_DT); во второй секции – от 1 апреля до 30 июня и т.д. На листинге 1 показан скрипт создания этой таблицы.
|
ЛИСТИНГ 1: скрипт создания таблицы TRANS с диапазонным секционированием. |
create table trans (
trans_id number,
trans_dt date,
<11P class=bodycopy > product_code number,
store_id number,
trans_amount number(12,2)
)
partition by range (trans_dt)
(
partition y05q1 values less than (to_date('04/01/2005','mm/dd/yyyy')) tablespace y05q1,
partition y05q2 values less than (to_date('07/01/2005','mm/dd/yyyy')) tablespace y05q2,
partition y05q3 values less than (to_date('10/01/2005','mm/dd/yyyy')) tablespace y05q3,
partition y05q4 values less than (to_date('01/01/2006','mm/dd/yyyy')) tablespace y05q4,
partition pmax values less than (maxvalue) tablespace users
) |
После создания этой таблицы можно проверить представление словаря данных DBA_TABLES. Для этой таблицы столбец PARTITIONED имеет значение "YES". Это и означает, что таблица является секционированной. Дополнительные детали о секционировании, такие как: тип секционирования, число секций, можно получить из представления DBA_PART_TABLES. Из представления DBA_TAB_PARTITIONS можно также получить детали по каждой секции, такие как: имя секции, ее верхняя граница. Это показано на листинге 2. Обратите внимание, в этом представлении показываются верхняя граница секции (столбец High_Value), которая на самом деле является нижней границей следующей секции. В секции хранятся записи до верхней границы, исключая записи самой верхней границы. Например, в секции Y05Q2 значение столбца High_Value "2005-07-01 00:00:00" означает, что в ней содержатся значения до "2005-06-30 23:59:59". Записи со значениями столбца TRANS_DT, равными "2005-07-01 00:00:00" и выше будут храниться в следующей секции Y05Q3.
|
ЛИСТИНГ 2: детали секций таблицы TRANS. |
col partition_position format 999 head "Pos"
col partition_name format a10 head "Name"
col high_value format a50 head "High Value"
select partition_position, partition_name, high_value
from dba_tab_partitions
where table_name = 'TRANS' order by 1;
Pos Name High_Value
--- -------- -----------------------------------------------------------------------
1 Y05Q1 TO_DATE(' 2005-04-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GRE...
2 Y05Q2 TO_DATE(' 2005-07-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GRE...
3 Y05Q3 TO_DATE(' 2005-10-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GRE...
4 Y05Q4 TO_DATE(' 2006-01-01 00:00:00', 'SYYYY-MM-DD HH24:MI:SS', 'NLS_CALENDAR=GRE...
5 PMAX MAXVALUE |
В примере с таблицей TRANS мы смогли найти столбец, который можно использовать для секционирования по диапазонам значений, таким как даты, но не все столбцы можно использовать для такого секционирования. Например, в столбце, в котором хранятся названия штатов США, содержится конечное и небольшое количество значений. Такой тип столбцов подходит для списочного (list) секционирования, когда таблица секционируется не по диапазонам, а по списку дискретных значений ключа. Например:
create table sales (
product_id number,
trans_amt number,
sales_dt date,
state_code varchar2(2)
)
partition by list (state_code)
(
partition ct values ('CT'),
partition ca values ('CA'),
partition def values (default)
);
Иногда для каждой секции невозможно определить диапазон значений или набор известных значений. Типичный пример – таблица клиентов CUST с первичным ключом по столбцу CUST_ID (идентификатор клиента). Значения столбца CUST_ID увеличиваются с определенным шагом, но этот шаг ничего не означает, поэтому диапазонное секционирование по этим значениям может оказаться бессмысленным.
Вы можете использовать для секционирования таблицы CUST хеш-секционирование (hash), когда ключ секционирования каждой строки передается хеш-функции и в зависимости от результата строка помещается в соответствующую секцию. Во всех хеш-секциях хранится одинаковое количество строк. Пример создания таблицы CUST с четырьмя хеш-секциями:
create table cust (
cust_id number,
cust_name varchar2(20)
)
partition by hash (cust_id)
partitions 4;
Индексы
По секционированным таблицам можно создавать индексы двух рассмотренных ниже типов.
Локальный индекс. Такой индекс секционируется точно так же как и базовая таблица. Например, локальный индекс по столбцу TRANS_AMOUNT таблицы TRANS создается следующим образом:
create index in_trans_01
on trans (trans_amount)
local;
Созданный секционированный по диапазонам индекс секционируется по столбцу TRANS_DT точно так же, как и таблица TRANS. Все записи индекса для конкретной секции, наприер, Y05Q1 будут находиться только внутри соответствующей секции этого индекса.
Глобальный индекс. Такой индекс охватывает все секции базовой таблицы. Предположим, у таблицы TRANS есть первичный ключ по столбцу TRANS_ID. Первичный ключ может находиться где-то внутри определения таблицы, захватывая все секции. В таком случае индексные записи для секции таблицы будут находиться за пределами соответствующей секции индекса. В этой ситуации создается глобальный индекс:
alter table trans
add constraint pk_trans
primary key (trans_id)
using index global;
Выбор схем секционирования
Теперь, когда мы рассмотрели основы секционирования, перейдем к более трудной части: как выбирать тип секционирования и столбцы, используемые в ключе секционирования? Ваш выбор схем секционирования должен быть направлен на достижение ключевых целей секционирования – упрощение управления и повышение производительности. Этот выбор будет зависеть от приоритетов, которые вы устанавливаете для ваших целей. Я разбил эти цели на следующие категории:
- производительность,
- легкость администрирования,
- очистка данных,
- архивирование данных,
- перемещение данных,
- управление жизненным циклом данных,
- эффективность резервирования.
Производительность. Это, конечно, основная цель многих схем секционирования. Повышение производительности достигается в результате отсечения секций (partition pruning) или соединения по секциям (partition-wise join), если в ваших запросах выполняется много полных просмотров таблиц. Секционирование очень помогает, поскольку секции ограничивают область поиска. Рассмотрим сценарий с использованием таблицы TRANS. Предположим, всех интересуют общие объемы продаж, а менеджеров продуктов – только объемы продаж соответствующих продуктов. Пусть менеджер продукта, код которого (product code) равен "8", несколько раз в час выполняет следующий SQL-оператор:
select trans_dt, sum(trans_amount), avg (trans_amount), max(trans_amount), min(trans_amount)
from trans
where product_code = 8
group by trans_dt;
Вы можете увеличить производительность выполнения этого запроса, создав индекс по столбцу PRODUCT_CODE, но это увеличит время выполнения операторов INSERT и может также неблагоприятно повлиять на планы выполнения других запросов к этой же самой таблице. Следовательно, разумнее, наверно, не создавать этот индекс, а выполнить списочное секционирование таблицы по столбцу PRODUCT_CODE, по одной секции на каждое значение столбца. В этом запросе менеджера продукта по-прежнему будет выполняться полный просмотр таблицы, но этой "полной таблицей" на самом деле будет секция. Это позволит существенно повысить производительность. Напоминаю, в этом случае списочное секционирование предпочтительнее диапазонного секционирования, поскольку код продукта имеет дискретное значение.
Если в SQL-запросах часто соединяются две довольно большие таблицы, то потенциально могут быть выгодными соединения по секциям. Предположим, у вас есть еще одна таблица ADVERTISING, в которой содержится подробная информация о рекламе компании. Вот описание этой таблицы:
Name Null? Type
--------- --------- ---------------------
AD_ID NUMBER
AD_DT DATE
PRODUCT_CODE NUMBER
AD_COST NUMBER
CHANNEL VARCHAR2(10)
Предположим, менеджеры продуктов для проверки эффективности рекламных кампаний часто выполняют запросы к таблицам ADVERTISING и TRANS, чтобы посмотреть на выручку по отношению к различным затратам на рекламирование. Для определения выручки и рекламных издержек для каждого продукта в течение указанного периода можно использовать следующий запрос:
select t.product_code, sum(trans_amount), sum(ad_cost)
from trans t, advertising a
where t.trans_dt = a.ad_dt
and t.product_code = a.product_code
and trans_dt between '1-aug-05' and
'1-sep-05'
group by t.product_code;
Здесь таблицы соединяются по столбцам TRANS_DT и AD_DT, тип данных обоих столбцов – DATE. Для повышения производительности вы можете решить секционировать обе таблицы одинаково. В данном случае наиболее эффективным будет диапазонное секционирование по столбцам соединения с одинаковыми диапазонами ключей. Скрипт создания таблицы ADVERTISING показан на листинге 3.
|
ЛИСТИНГ 3: скрипт создания таблицы ADVERTISING. |
create table advertising (
ad_id number,
ad_dt date,
product_code number,
ad_cost number,
channel varchar2(10)
)
partition by range (ad_dt)
(
partition y05q1 values less than (to_date('04/01/2005','mm/dd/yyyy')) tablespace y05q1,
partition y05q2 values less than (to_date('07/01/2005','mm/dd/yyyy')) tablespace y05q2,
partition y05q3 values less than (to_date('10/01/2005','mm/dd/yyyy')) tablespace y05q3,
partition y05q4 values less than (to_date('01/01/2006','mm/dd/yyyy')) tablespace y05q4,
partition pmax values less than (maxvalue) tablespace y05q4
); |
Аналогичным образом, если вы часто соединяете эти таблицы по столбцу PRODUCT_CODE, то в качестве ключа секционирования целесообразно использовать его. Значения кодов продуктов находятся в фиксированном наборе, поэтому схема секционирования должна быть списочной.
Если таблицы ADVERTISING и TRANS не соединяются по каким-то конкретным датам или полям дат, а по какому-то последовательному номеру, такому как значения столбцов AD_ID или TRANS_ID, вам следует пересмотреть стратегию секционирования. Записи в этих столбцах имеют уникальные значения, поэтому вы не можете использовать списочное секционирования (для него требуется дискретный, конечный набор значений). Диапазонное секционирование
будет неэффективным, поскольку для последовательно увеличивающихся значений в этих двух столбцах потребуется создавать многочисленные секции, которые не будут иметь какого-то особого смысла.
В таких случаях оказывается полезным хеш-секционирование. Оно обеспечивает равномерное распределение записей по всем секциям. Вы можете создать эти таблицы следующим образом:
create table trans (
trans_id number,
trans_dt date,
product_code number,
store_id number,
trans_amount number(12,2)
)
partition by hash (trans_id)
(
partition trans1 tablespace trans1,
partition trans2 tablespace trans2,
partition trans3 tablespace trans3,
partition trans4 tablespace trans4
)
/
create table advertising (
ad_id number,
ad_dt date,
product_code number,
ad_cost number,
channel varchar2(10)
)
partition by hash (ad_id)
(
partition ad1 tablespace ad1,
partition ad2 tablespace ad2,
partition ad3 tablespace ad3,
partition ad4 tablespace ad4
)
/
После создания этих таблиц для проверки надлежащего использования секций вы можете воспользоваться пакетом DBMS_XPLAN. Сначала вы должны сгенерировать план выполнения запроса менеджеров продуктов:
explain plan for
select t.product_code, sum(trans_amount), sum(ad_cost)
from trans t, advertising a
where t.trans_id = a.ad_id
and t.trans_id = 101
and t.product_code = a.product_code
group by t.product_code;
Затем вы можете проверить этот план:
select * from table(dbms_xplan.display);
Результаты этого запроса показаны на листинге 4. Обратите внимание на столбцы PSTART и PSTOP, которые показывают, какая была выбрана секция таблицы. Для четвертой строки плана значения этих столбцов – 4 и 4, которые означают, что была выбрана четвертая секция таблицы TRANS; именно в ней находится запись, у которой TRANS_ID=101. Обратите внимание на шестую строку плана, она показывает, что при соединении с таблицей ADVERTISING будет также использоваться только секция 4, а не вся таблица. В случае отсутствия индексов эта стратегия хеш-секционирования позволяет выполнять запросы менеджеров продуктов намного быстрее полного просмотра всей таблицы (как для таблицы TRANS, так и для таблицы ADVERTISING).
|
ЛИСТИНГ 4: план запроса к хеш-секционированым таблицам. |
PLAN_TABLE_OUTPUT
-----------------------------------------------------------------------------------------------------------------
Plan hash value: 2303213640
----------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU) | Time | Pstart | Pstop |
----------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 78 | 11 (19) | 00:00:01 | | |
| 1 | HASH GROUP BY | | 1 | 78 | 11 (19) | 00:00:01 | | |
| * 2 | HASH JOIN | | 1 | 78 | 10 (10) | 00:00:01 | | |
| 3 | PARTITION HASH SINGLE | | 1 | 39 | 4 (0) | 00:00:01 | 4 | 4 |
| * 4 | TABLE ACCESS FULL |TRANS | 1 | 39 | 4 (0) | 00:00:01 | 4 | 4 |
| 5 | PARTITION HASH SINGLE | | 1 | 39 | 5 (0) | 00:00:01 | 4 | 4 |
| * 6 | TABLE ACCESS FULL |ADVERTISING| 1 | 39 | 5 (0) | 00:00:01 | 4 | 4 |
----------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
----------------------------------------------------------------------------------------------------------------
2 - access("T"."TRANS_ID"="A"."AD_ID" AND "T"."PRODUCT_CODE"="A"."PRODUCT_CODE")
4 - filter("T"."TRANS_ID"=101)
6 - filter("A"."AD_ID"=101) |
Легкость администрирования. Секционирование всегда позиционируется как технология управления большими объектами. Хотя оно подходит для объектов любого размера, преимущества более очевидны при администрировании больших таблиц. Когда вы перестраиваете индекс по несекционированной таблице, вам доступен только один вариант – перестройка всего индекса с помощью одного оператора. Если же таблица секционирована, вы можете перестраивать секции локального индекса по очереди. Например, в случае с таблицей TRANS вы можете перестроить секцию Y05Q1 локального индекса IN_TRANS_01:
alter index in_trans_01 rebuild partition y05q1;
В противном случае вам бы пришлось перестраивать весь индекс IN_TRANS_01. Преимущества этого подхода становятся более явными, когда вы загружаете конкретную секцию и требуется перестроить индекс только для нее, а не для всей таблицы. Кроме перестроения индексов вы можете посекционно перемещать таблицы по табличным пространствам, экспортировать таблицы, удалять данные и т.д.
Очистка данных. Предположим, таблица TRANS используется в OLTP-системе, и вы хотите удалить старые записи, у которых, например, значения столбца TRANS_DT относятся к первому кварталу 2005 г. Традиционный способ – удаление этих записей, но при таком удалении генерируется undo- и redo-информация, что влияет на производительность сервера базы данных. Этот способ также может оказаться очень трудоемкой операцией – в зависимости от объема данных, подлежащих очистке. Альтернатива удалению записей – удаление секции (drop partition). В случае с таблицей TRANS, если вы решили удалить все записи за первый квартал 2005 г., вы выполняете оператор:
alter table trans drop partition y05q1;
И это все. После выполнения этого оператора секция исчезает из таблицы. Этот оператор на самом деле ничего не удаляет из таблицы; он просто обновляет словарь данных, чтобы указать, что секция Y05Q1 больше не принадлежит таблице. Отсутствуют какие-либо перемещения данных (исключая небольшой объем информации словаря данных), поэтому генерируется минимальный объем undo- и redo-информации, что позволяет выполнять эту операцию очень быстро, по сравнению с построчным удалением, фактически без всякого воздействия на производительность.
Обратите внимание, обычный оператор DELETE удаляет строки таблицы там, где они находятся, оставляя в блоках пустое пространство. Несмотря на то, что табличные данные могут быть разреженными, общий размер сегмента по-прежнему может быть большим и иметь очень высокое значение маркера максимального уровня заполнения (HWM, high-water mark) – наибольшего размера, когда-либо занимаемого таблицей. Высокое значение маркера HWM замедляет полные просмотры таблицы, поскольку сервер Oracle Database должен посматривать таблицу до маркера HWM, даже если в ней вообще нет никаких строк. Когда же удаляется секция, пропадает сам сегмент, а маркеры HWM других сегментов не изменяются, что ускоряет выполнение запросов.
Во время удаления строк оператором DELETE также удаляются соответствующие индексные записи, что приводит к фрагментации индексов, которая может влиять на производительность.
Когда же удаляется секция, удаляется также соответствующая секция любого локального индекса. Однако, если есть глобальный индекс, он во время удаления секции может стать неиспользуемым (unusable). Для предотвращения этого в сервере Oracle9i Database и более поздних версиях вы во время удаления секции можете обновлять глобальный индекс. Следующий оператор удаляет секцию Y05Q1 и обновляет глобальные индексы:
alter table trans drop partition y05q1 update global indexes;
Если вы решили не обновлять глобальный индекс во время удаления секции, то он станет неиспользуемым. Вы должны будете перестроить этот индекс, чтобы сделать его используемым, но вы можете отложить эту задачу на более позднее время – после удаления других секций или выполнения каких-то других операций.
Вы можете вручную перестроить глобальный индекс по таблице TRANS:
alter index pk_trans rebuild;
Архивирование данных. Если вы решили очищать данные без их сохранения, вы можете воспользоваться способом, рассмотренным в предыдущем разделе. В состав очищаемых без сохранения данных входит протокольная и отладочная информация, которую можно просто уничтожать. Однако большинство информации нужно, вероятно, архивировать, сохраняя ее для доступа в будущем. Если в примере с таблицей TRANS вы хотите сохранить содержимое секции Y05Q1, вы можете использовать следующий метод преобразования секций (partition exchange):
1. Создадим таблицу, структура которой почти идентична структуре таблицы TRANS (за исключением того, что она не секционирована):
create table trans_y05q1 as
select * from trans
where 1=2;
2. Создалась пустая несекционированная таблица TRANS_Y05Q1, структура которой идентична структуре таблицы TRANS. Предикат 1=2 возвращает FALSE, так что не вставляются никакие строки таблицы TRANS. Перенесем содержимое нашей секции в эту новую таблицу:
alter table trans
exchange partition y05q1
with table trans_y05q1;
В результате выполнения этой операции данные секции Y05Q1 стали данными таблицы TRANS_Y05Q1, а сама секция была очищена. Физически данные не перемещаются из секции в таблицу. Этот оператор преобразования секций просто обновил словарь данных, изменив указатель на секцию на указатель на таблицу (возможна и обратная операция). Здесь нет никакого физического перемещения данных, поэтому не генерируется никакая redo- и undo-информация, что делает ее быстрее с намного меньшим влиянием на производительность по сравнению с традиционными подходами к перемещению данных, такими как использование операторов INSERT.
После выполнения оператора преобразования секций наша секция становится пустой и ее можно удалить, как это было показано ранее. После того как таблица была создана, ее можно перенести за пределы базы данных и заархивировать для дальнейшего использования.
Перемещение данных. Одним из наиболее перспективных аспектов администрирования хранилищ данных является разработка процессов ETL (Extraction, Transformation and Loading, извлечение преобразование и загрузка данных), которые загружают данные из OLTP-систем в базы данных хранилищ. В число других примеров перемещения больших объемов данных входят тиражирование данных из промышленных баз данных в базы данных контроля качества, заполнение витрин данных из главных хранилищ данных и наоборот, заполнение таблиц итогов из таблиц транзакций. В таких процессах секции могут оказаться очень полезными. Возьмем, например, таблицу TRANS и другую таблицу TRANS_SUMMARY, в которую записывается общий объем ежедневных транзакций для конкретных продуктов. Таблица TRANS_SUMMARY выглядит так:
SQL> desc trans_summary
Name Null? Type
--------- --------- ---------------------
PRODUCT_CODE NUMBER
TOT_AMOUNT NUMBER(12,2)
Традиционный подход к заполнению таких таблиц хранилищ данных может быть следующим:
insert into trans_summary
select product_code, sum(trans_amount)
from trans
group by product_code;
Этот подход работает, но возникают некоторые проблемы:
1. Оператор INSERT будет генерировать в транзакции большой объем undo- и redo-информации, что будет влиять на производительность сервера базы данных. Вы можете уменьшить этот объем, используя в цикле множественные операторы фиксации данных, но существенно возрастет общее затраченное время. Вы можете использовать режим прямой вставки (подсказка APPEND) с запретом журнализации (подсказка NOLOGGING):
insert /*+ APPEND NOLOGGING */
into trans_summary
select product_code, sum(trans_amount)
from trans
group by product_code;
Однако эти данные, загружаемые в таблицу TRANS_SUMMARY, будут недоступны до завершения загрузки.
2. Перед ежедневной загрузкой данных процесс загрузки должен очищать таблицу TRANS_SUMMARY, иначе в ней появятся дубликаты. Эта таблица недоступна с момента ее очистки до завершения полной загрузки. В некоторых очень больших системах этот процесс может выполняться часами (в зависимости от сложности запроса, размера таблицы и общей нагрузки как в исходной системе базы данных, так и в целевой); в это время пользователи не могут просматривать даже старые данные (они были удалены перед началом загрузки). Если выполнение оператора INSERT завершится со сбоем из-за нехватки пространства или ошибок в данных, то пользователи вынуждены будут ждать загрузки новых данных, на что опять могут потребоваться часы.
Использование секций устраняет или в значительной степени смягчает эти две проблемы. Таблица итогов TRANS_SUMMARY группируется по столбцу PRODUCT_CODE, поэтому вы можете создать ее со списочным секционированием по столбцу PRODUCT_CODE:
create table trans_summary (
product_code number,
tot_amount number(12,2)
)
partition by list (product_code)
(
partition p1 values (1),
partition p2 values (2),
partition p3 values (3),
partition p4 values (4),
partition p5 values (5),
partition p6 values (6),
partition p7 values (7),
partition p8 values (8),
partition p9 values (9),
partition p10 values (10)
)
Вместо полной загрузки вы можете загружать данные из таблицы TRANS в таблицу TRANS_SUMMARY посекционно. Рассмотрим пример загрузки данных, у которых PRODUCT_CODE=1:
1. Сначала создадим временную таблицу, структура которой идентична структуре таблицы TRANS (за исключением того, что она не секционирована):
create table trans_summary_temp
nologging as
select
cast(1 as number) product_code,
cast(sum(trans_amount) as number(12,2)) tot_amt
from trans
where product_code = 1
group by 1;
Обратите внимание, эта таблица создана с опцией запрета журнализации NOLOGGING, что существенно уменьшает объем генерируемой undo- и redo-информации. Во время динамического создания этой таблицы исходные таблицы TRANS и TRANS_SUMMARY полностью доступны для пользователей.
2. После создания этой таблицы вы можете перенести ее в секцию p1 таблицы TRANS_SUMMARY:
alter table trans_summary
exchange partition p1
with table trans_summary_temp
including indexes;
Эта операция мгновенно делает данные таблицы TRANS_SUMMARY_TEMP доступными как данные секции p1 таблицы TRANS_SUMMARY. Это "переключение" просто изменяет в словаре данных указатели; на самом деле никакие данные не перемещаются. Так что, этот процесс выполняется очень быстро (обычно несколько секунд), а таблица блокируется только на этот период времени. Во все остальное время выполнения процесса данные таблицы доступны. Итак, этот подход имеет четыре основных преимущества по сравнению с прямой загрузкой таблицы:
1. Низкое влияние на производительность.
2. Главная таблица всегда доступна (за исключением нескольких секунд).
3. В случае сбоя во время загрузки, старые данные остаются доступными.
4. Обновлять данные можно выборочно.
Управление жизненным циклом данных. Жизненный цикл большинства бизнес-данных предсказуем: сначала к ним обращаются сравнительно умеренно; затем они интенсивно обновляются; и, в конце концов, количество обращений к ним падает почти до нуля. Нормативные требования могут диктовать обязательное сохранение в базе данных даже таких данных, к которым обращаются очень редко. Частота обращения к более старым данным уменьшается, поэтому, используя секционирование, можно разработать стратегию доступа к дискам, которая снижает общую стоимость владения без потери производительности. Разместите секции данных, которые наиболее часто выбираются или обновляются, на самых быстрых дисках, а более старые секции данных – на более медленных (и более дешевых) дисках.
Например, в таблице TRANS наиболее интенсивно обращаются к записям самых последних транзакций, таким как в текущей секции Y05Q4, затем к записям в предшествующей секции Y05Q3 и т.д. Частота доступа к более старым секциям – низкая, поэтому может быть целесообразно переместить их в более медленную внешнюю память. Для этого на более дешевом диске нужно создать новое табличное пространство и переместить (move) в него нужную секцию:
alter table trans move partition y05q1 tablespace y05q1_inexpensive;
Во время такого перемещения можно выполнять операторы SELECT, но не операторы UPDATE.
Эффективность резервирования. Рассмотрим пример с таблицей TRANS, представленный в начале этой статьи. В этом примере каждая секция размещается в отдельном табличном пространстве с таким же именем. Если вы можете с уверенностью сказать, что записи столбца TRANS_DT, которые имеют более раннее значение, чем текущий день (или более ранее, чем какая-то конкретная дата), не будут изменяться, то тогда можно счесть целесообразным перевести соответствующую секцию в режим "только для чтения". Для этого следует перевести в этот режим табличное пространство, в котором находится эта секция:
alter tablespace y05q1 read only;
Если табличное пространство находится в режиме "только для чтения", диспетчер восстановления Oracle Recovery Manager (RMAN) может во время резервирования не создавать его резервные копии; достаточно одной резервной копии. Чем больше табличных пространств переведено в режим "только для чтения", тем короче будет продолжительность выполнения задания диспетчера RMAN, тем ниже его нагрузка на сервер базы данных. Это преимущество заметно в системах баз данных, в которых содержатся хронологические данные, особенно в хранилищах данных, в которых общий объем данных, требующих резервирования, обычно довольно велик, а вы, в свою очередь, можете перевести в режим "только для чтения" много табличных пространств.
Решения, решения
Чтобы принять решение о выборе схемы секционирования, основанное на имеющейся информации, сначала следует назначить приоритеты вашим целям секционирования. Предположим, вы решили, используя некоторые рассмотренные в этой статье альтернативы, задать следующий порядок приоритетов:
- Архивирование данных.
- Очистка данных.
- Эффективность резервирования.
- Легкость администрирования.
- Производительность.
Вы осознанно поставили производительность на последнее место; однако в других ситуациях выбора схемы секционирования производительность может иметь высший приоритет.
Архивирование и очистка данных. Для достижения этих целей, имеющих высший приоритет, нужно выбрать столбец разграничения данных, подлежащих архивированию и очистке. Имеет ли этот столбец тип данных DATE, как это было у столбца TRANS_DT в примере с таблицей TRANS? В этом примере в качестве ключа секционирования таблицы с диапазонным секционирования используется столбец TRANS_DT, но предположим, что есть похожая таблица со столбцом EXPIRY_DT, который указывает дату, после которой запись точно не будет изменяться. В этом случае в качестве ключа секционирования таблицы с диапазонным секционирования лучше (для целей архивирования и очистки данных) использовать этот столбец.
Предположим, в столбце разграничения данных содержатся дискретные значения, такие как в столбце CAMPAIGN_CODE (таблица ADVERTISING), обозначающие рекламные кампании. После завершения каждой кампании ее записи архивируются и очищаются. В этом случае лучше (для целей архивирования и очистки данных) выбрать списочное секционирование, используя в качестве ключа секционирования столбец CAMPAIGN_CODE. Диапазонная схема здесь будет бесполезной, поскольку нужно удалять секции с кодами конкретных рекламных кампаний, и никаких диапазонов здесь нет.
Теперь предположим, что в большинстве запросов к таблице TRANS в предложении WHERE не используется столбец TRANS_DT. Выбор этого столбца ключом секционирования не поможет улучшить производительность. Хорошим ли будет такой выбор? Ответ зависит от ваших приоритетов. В этом примере приоритет производительности ниже приоритетов архивирования и очистки данных, так что эта схема секционирования представляет собой наилучшее решение. Если бы приоритет производительности был выше приоритетов архивирования и очистки данных, вы должны были бы выбрать другую схему.
Эффективность резервирования. Если это – ваша основная цель, задайте себе вопрос: какой столбец позволяет узнать, что записи можно перевести в режим "только для чтения"? Предположим, ответ – столбец TRANS_DT. Записи, у которых значения столбца TRANS_DT показывают, что они не обновлялись больше месяца и их следует считать записями, предназначенными только для чтения. В таком случае выберите диапазонное секционирование таблицы по столбцу TRANS_DT, когда каждая секция размещается в отдельном табличном пространстве, как это показано на листинге 1. Когда полностью завершается обновление записей секции, переведите соответствующее табличное пространство в режим "только для чтения". Утилита RMAN во время резервирования будет пропускать такие табличные пространства, процесс резервирования будет выполняться быстрее.
Легкость администрирования. Если это – ваша основная цель, то нужно исходить из того, как модифицируются данные. Предположим, вы часто перестраиваете индексы из-за частого выполнения загрузки данных. Вам следует обратить внимание на столбец, который определяет, какие данные будут загружаться. Имеет ли этот столбец тип данных DATE, как это было у столбца TRANS_DT в примере с таблицей TRANS? Если это так, наиболее подходящим будет диапазонное секционирование со столбцом TRANS_DT в качестве ключа секционирования. А если же в столбце разграничения данных содержатся дискретные значения, как это было в случае со столбцом PRODUCT_CODE? Если это так, полезным будет списочное секционирование. Если никаких шаблонов не существует, вы можете использовать хеш-секционирование и после генерации секций перестривать индексы.
Производительность. Если производительность имеет самый высокий приоритет, выберите столбец, который наиболее часто используется в условиях предложения WHERE для фильтрации строк и соединений с другими таблицами. Если у этого столбца тип данных DATE, как это было у столбца TRANS_DT в примере с таблицей TRANS, то очень полезным будет диапазонное секционирование. Даже если у этого столбца другой тип данных, поможет наличие некоторого логического диапазона. Например, предположим, у значений столбца PRODUCT_CODE (тип данных NUMBER) есть шаблон: значения от 1000 до 1999 обозначают потребительские товары, от 2000 до 2999 – товары производственного назначения и т.д. Когда пользователи выполняют запросы к таблице, фокусируются ли они на конкретном типе продукции, например, на промышленной категории? Если это так, можно использовать диапазонное секционирование по столбцу PRODUCT_CODE с диапазонами ключей 1000-1999, 2000-2999 и т.д.
Комбинирование схем секционирования. Вы также можете объединять отдельные наиболее важные цели, используя в сервере Oracle Database смешанные стратегии секционирования. Например, вы можете создать таблицу ADVERTISING, используя диапазонное секционирование со списочным подсекционированием, когда для удовлетворения требований производительности вы используете диапазонное секционирование по столбцу AD_DT, а для удовлетворения требований очистки данных – списочное секционирование по столбцу PRODUCT_CODE.
Заключение
Наиболее важные решения при проектировании секций – выбор схем секционирования и столбцов, по которым выполняется секционирование. Эти решения в большой степени зависят от приоритетов потенциально противоречивых целей. После изучения преимуществ каждого типа схем секционирования можно выбирать такую схему и те столбцы, что наиболее подходят для удовлетворения ваших потребностей в секционировании.
===================================================================
Арап Нанда (Arup Nanda) (
arup@proligence.com) – администратор баз данных Oracle с двенадцатилетним стажем. Он занимается всеми аспектами администрирования – от оптимизации производительности до информационной безопасности и аварийного восстановления. Арап – соавтор книги "PL/SQL for DBAs" (O'Reilly Media, 2005). В 2003 г. он был удостоен награды журнала Oracle Magazine "Oracle's DBA of the Year" (администратор года баз данных Oracle).
|