مدیریت عملی پارتیشن‌ها در Oracle برای آرشیو داده‌ها

مدیریت داده‌های بزرگ، مخصوصاً لاگ‌ها و تراکنش‌های روزانه، یکی از چالش‌های مهم در دیتابیس‌های امروزی است.
این مقاله آموزش کامل پارتیشن‌بندی نیست، بلکه متمرکز بر مدیریت عملی پارتیشن‌ها شامل:

  • TRUNCATE PARTITION

  • DROP PARTITION

با تمرکز بر سناریوهای واقعی مثل آرشیو داده‌های قدیمی برای بهینه‌سازی هزینه‌ی نگهداری دیتابیس.

بسیاری از جداول لاگ تنها برای چند روز اخیر اهمیت دارند.
با آرشیو و حذف داده‌های قدیمی:

  • فضای مصرفی دیتابیس کاهش می‌یابد

  • زمان Backup و Restore بهبود پیدا می‌کند

  • هزینه‌ی Storage کمتر می‌شود

این دقیقاً همان هدف Information Lifecycle Management (ILM) است.

مرحله ۱: ساخت جدول پارتیشن‌بندی شده روزانه (بر اساس عدد)

توضیح فنی درباره‌ی INTERVAL

در پارتیشن‌بندی RANGE معمولی، باید برای هر بازه‌ی داده یک پارتیشن از قبل تعریف کنیم.
اما وقتی از INTERVAL استفاده می‌کنیم، Oracle به صورت خودکار و دینامیک، برای مقادیر جدید، پارتیشن جدید می‌سازد.

در اینجا، INTERVAL (1) یعنی اگر رکوردی وارد شود که تاریخش بزرگ‌تر از آخرین پارتیشن است، Oracle خودش یک پارتیشن جدید برای آن روز می‌سازد.

ایجاد جدول:

CREATE TABLE vahiddb_daily_data (
    id            NUMBER,
    logical_date  NUMBER,
    data_value    VARCHAR2(100)
)
PARTITION BY RANGE (logical_date)
INTERVAL (1)
(
    PARTITION p_start VALUES LESS THAN (20250101)
);

 

مرحله ۲: ساخت جدول مشابه بر پایه‌ی DATE

توضیح فنی درباره‌ی INTERVAL روی DATE

برای ستون‌هایی از نوع DATE، باید INTERVAL را با تابع NUMTODSINTERVAL تعیین کنیم.
در این مثال، NUMTODSINTERVAL(1, 'DAY') مشخص می‌کند که برای هر روز تقویمی یک پارتیشن جدید ساخته شود.

ایجاد جدول:

CREATE TABLE vahiddb_daily_data_date (
    id            NUMBER,
    logical_date  DATE,
    data_value    VARCHAR2(100)
)
PARTITION BY RANGE (logical_date)
INTERVAL (NUMTODSINTERVAL(1, 'DAY'))
(
    PARTITION p_start VALUES LESS THAN (TO_DATE('2025-01-01', 'YYYY-MM-DD'))
);

 

مرحله ۳: درج داده‌های تستی

برای جدول عددی:

BEGIN
  FOR d IN 20250101 .. 20250110 LOOP
    FOR i IN 1 .. 10000 LOOP
      INSERT INTO vahiddb_daily_data (id, logical_date, data_value)
      VALUES (
        d * 100000 + i,
        d,
        RPAD('DATA_', 100, 'X')
      );
    END LOOP;
    COMMIT;
  END LOOP;
END;

 

برای جدول تاریخ:

BEGIN
  FOR d IN 0 .. 9 LOOP
    FOR i IN 1 .. 10000 LOOP
      INSERT INTO vahiddb_daily_data_date (id, logical_date, data_value)
      VALUES (
        (20250101 + d) * 100000 + i, -- ساخت id یکتا بر اساس تاریخ
        TO_DATE('2025-01-01', 'YYYY-MM-DD') + d, -- تاریخ روزانه
        RPAD('DATA_', 100, 'X') -- داده حجیم
      );
    END LOOP;
    COMMIT;
  END LOOP;
END;

 

مرحله ۴: مشاهده‌ی پارتیشن‌ها و حجم اشغالی

SELECT
  segment_name,
  partition_name,
  bytes / 1024 / 1024 AS size_mb
FROM dba_segments
WHERE segment_name = 'VAHIDDB_DAILY_DATA'
ORDER BY partition_name;

 

مرحله ۵: مدیریت پارتیشن‌ها

ابتدا: پیدا کردن اسم پارتیشن از روی مقدار High Value

با این روش، بدون دانستن اسم پارتیشن می‌توانیم اسم دقیقش را استخراج کنیم:

SELECT
    p.table_name,
    p.partition_name,
    REGEXP_SUBSTR(
        DBMS_XMLGEN.getxmltype(
            'SELECT high_value FROM dba_tab_partitions
             WHERE table_name = ''' || p.table_name || '''
               AND partition_name = ''' || p.partition_name || '''
               AND table_owner = ''' || p.table_owner || ''''
        ).extract('//text()').getStringVal(),
        '\d+'
    ) AS high_value_extracted
FROM dba_tab_partitions p
WHERE p.table_owner = 'SYS'
  AND p.table_name  = 'VAHIDDB_DAILY_DATA'
ORDER BY high_value_extracted;

 

 

توضیح کامل درباره‌ی استخراج HIGH_VALUE از DBA_TAB_PARTITIONS با XMLGEN

در جداول پارتیشن‌بندی شده، اطلاعات مرزبندی (Boundary) هر پارتیشن در ستون HIGH_VALUE در ویوی DBA_TAB_PARTITIONS ذخیره می‌شود.
اما مشکل اینجاست که مقدار HIGH_VALUE در این ویو به صورت کد SQL (String Expression) ذخیره شده، نه به صورت مقدار ساده.

مثلاً برای یک جدول که بر اساس تاریخ پارتیشن شده، مقدار HIGH_VALUE ممکن است اینطور باشد:

TO_DATE('2025-01-08 00:00:00', 'YYYY-MM-DD HH24:MI:SS')

یا اگر بر اساس عدد باشد:

TO_NUMBER('20250108')

بنابراین، اگر بخواهیم مقدار واقعی پارتیشن (مثلاً 20250108 یا تاریخ معادل) را به صورت مستقیم استخراج کنیم، نیاز به پردازش بیشتری داریم.

از DBMS_XMLGEN برای اجرای یک ساب‌کوئری دینامیک استفاده می‌کند

DBMS_XMLGEN.getxmltype(
  'SELECT high_value FROM dba_tab_partitions WHERE ...'
)

این بخش خروجی کوئری را به فرمت XML تبدیل می‌کند.

از EXTRACT('//text()') برای گرفتن مقدار خام HIGH_VALUE از XML استفاده می‌کند

.extract('//text()').getStringVal()

این کار باعث می‌شود که مقدار واقعی (نه ساختار XML) را استخراج کنیم.

از REGEXP_SUBSTR برای پیدا کردن عدد از متن استخراج‌شده استفاده می‌شود

REGEXP_SUBSTR(..., '\d+')

این عبارت منظم (\d+) فقط اعداد پشت سر هم را از متن جدا می‌کند.

مثلاً اگر HIGH_VALUE این باشد:

TO_NUMBER('20250108')

عبارت REGEXP_SUBSTR فقط 20250108 را جدا می‌کند.

 

TABLE_NAME                     PARTITION_NAME       HIGH_VALUE_EXTRACTED
------------------------------ -------------------- ------------------------------
VAHIDDB_DAILY_DATA             P_START              20250101
VAHIDDB_DAILY_DATA             SYS_P791             20250102
VAHIDDB_DAILY_DATA             SYS_P792             20250103
VAHIDDB_DAILY_DATA             SYS_P793             20250104
VAHIDDB_DAILY_DATA             SYS_P794             20250105
VAHIDDB_DAILY_DATA             SYS_P795             20250106
VAHIDDB_DAILY_DATA             SYS_P796             20250107
VAHIDDB_DAILY_DATA             SYS_P797             20250108
VAHIDDB_DAILY_DATA             SYS_P798             20250109
VAHIDDB_DAILY_DATA             SYS_P799             20250110
VAHIDDB_DAILY_DATA             SYS_P800             20250111

حالا داده ها را باید به جایی که مدنظر دارید با روش خودتان انتقال دهید. روشها می تواند شامل گرفتن یک DUMP یا INSERT به جدول دیگری یا EXCHANGE باشد. 

بعد از پیدا کردن اسم پارتیشن، با دستور زیر TRUNCATE یا DROP انجام می‌دهیم:

ALTER TABLE VAHIDDB_DAILY_DATA DROP PARTITION SYS_P799;

 

این روش سنتی است و همچنان معتبر.

 

حالا استفاده از روش مدرن PARTITION FOR (...)

بدون نیاز به دانستن اسم پارتیشن. فرض کنیم که میخواهیم با پارتیشنی کار کنیم که اسم پارتیشن را نمی دانیم. کافیست از روش زیر استفاده کنیم.

برای جدول عددی:

ALTER TABLE VAHIDDB_DAILY_DATA TRUNCATE PARTITION FOR (20250108);

 

برای جدول تاریخ:

ALTER TABLE VAHIDDB_DAILY_DATA_DATE TRUNCATE PARTITION FOR (TO_DATE('2025-01-08', 'YYYY-MM-DD'));

 

 Oracle خودش پارتیشن مناسب را پیدا و عمل می‌کند.

 

نکته مهم:
دستور TRUNCATE PARTITION FOR (...) در داکیومنت رسمی Oracle (تا تاریخ تهیه این مقاله) پیدا نکردم.
اگر کسی اطلاعات دقیقی از نسخه رسمی یا مستند آن دارد، خوشحال می‌شویم اعلام کند تا مقاله کامل‌تر شود.

 

نکات فنی مهم:

ایندکس های global در نسخه 11g ممکن است بعد از truncate/drop نیاز به rebuild داشته باشد