بخش اول: ورود به جهان قدرتمند PostgreSQL
بخش دوم : مکانیزم های حرفه‌ای مدیریت ذخیره سازی و تضمین مقیاس پذیری پستگرس
بخش سوم : تحلیل و بهینه‌سازی اجرای کوئری در PostgreSQL
بخش چهارم: استراتژی‌های پشتیبان‌گیری و بازیابی اطلاعات
بخش پنجم: راهکارهای دسترس‌پذیری بالا و مقیاس‌پذیری
بخش ششم: پایش، مانیتورینگ و عیب‌یابی در PostgreSQL
کارگاه‌ها و مثال‌های کاربردی

آشنایی با جداول Toast – رکوردهای با طول زیاد، چگونه در پستگرس ذخیره می‌شوند ؟ 🎥

در هستهٔ معماری ذخیره‌سازی PostgreSQL، یک محدودیت بنیادی وجود دارد: صفحات داده (Data Pages) اندازهٔ ثابت ۸ کیلوبایت دارند و یک رکورد (tuple) هرگز نمی‌تواند از یک صفحه بیشتر شود . این محدودیت برای سیستم‌های قدیمی که داده‌ها عمدتاً کوچک بودند، طراحی شده بود. اما در دنیای امروز با داده‌های حجیم مانند متن‌های طولانی، اسناد JSON، آرایه‌های بزرگ یا فایل‌های باینری، چه باید کرد؟

اینجاست که TOAST وارد می‌شود؛ یکی از هوشمندترین مؤلفه‌های معماری PostgreSQL که به‌صورت خودکار و شفاف، مشکل داده‌های بزرگ را حل می‌کند. TOAST مخفف The Oversized-Attribute Storage Technique است و در مستندات PostgreSQL با طنزی دوست‌داشتنی «بهترین چیز پس از نان برش‌خورده» نامیده شده .

در این نوشتار، با سفری از لایه‌های بالایی تا عمق ذخیره‌سازی فیزیکی، خواهیم دید که TOAST چگونه این معجزه را ممکن می‌کند.


۱. مسئلهٔ بنیادی: محدودیت اندازهٔ صفحه

برای درک ضرورت TOAST، باید با دو مفهوم پایه آشنا شویم:

  • صفحه (Page): واحد اصلی ذخیره‌سازی در PostgreSQL، بلوکی ۸ کیلوبایتی است که شامل چندین رکورد می‌شود.
  • رکورد (Tuple): هر ردیف از یک جدول، به‌صورت یک tuple در یک صفحه ذخیره می‌شود و نمی‌تواند از یک صفحه بزرگتر باشد.

حال اگر بخواهیم ردیفی با یک ستون TEXT به طول ۱۰۰ کیلوبایت ذخیره کنیم، چه اتفاقی می‌افتد؟ به‌طور سنتی، این کار غیرممکن است. اما داده‌های مدرن اغلب ویژگی‌های زیر را دارند:

  • اندازهٔ بسیار متغیر: از چند کاراکتر تا چند مگابایت
  • رشد غیرقابل پیش‌بینی: کاربران ممکن است داده‌های حجیم وارد کنند
  • الگوهای دسترسی متفاوت: گاهی فقط نیاز به خواندن متادیتا داریم، نه کل داده

اگر بخواهیم همهٔ داده‌ها را در صفحهٔ اصلی ذخیره کنیم:

  • تراکم صفحه به شدت کاهش می‌یابد
  • مصرف I/O افزایش می‌یابد
  • کارایی اسکن‌های ترتیبی افت می‌کند
  • ایندکس‌ها حجیم و کند می‌شوند

راه‌حل معماری PostgreSQL برای این مسئله، جداسازی داده‌های بزرگ از جریان اصلی و مدیریت هوشمند آنهاست.


۲. مفهوم TOAST: جداسازی هوشمند

TOAST یک مکانیزم تطبیقی است که به‌صورت خودکار تصمیم می‌گیرد با هر مقدار بزرگ چگونه رفتار کند:

  • آیا داده فشرده شود؟
  • آیا داده خارج از صفحه ذخیره شود؟
  • چگونه داده به قطعات (chunk) تقسیم شود؟
  • چگونه در زمان خواندن، داده بازسازی شود؟

نکتهٔ مهم این است که TOAST فقط یک جدول کمکی نیست، بلکه یک سیاست مدیریت اندازه در سطح ستون است. هر ستون می‌تواند استراتژی متفاوتی داشته باشد و سیستم به‌صورت هوشمند بهترین تصمیم را می‌گیرد.


۳. مدل عملیاتی TOAST: سه مرحله تا ذخیرهٔ نهایی

مرحلهٔ ۱: فشرده‌سازی (Compression)

سیستم ابتدا تلاش می‌کند داده را فشرده کند تا در همان صفحه باقی بماند. از PostgreSQL 14 به بعد، دو الگوریتم فشرده‌سازی قابل انتخاب هستند :

  • PGLZ: الگوریتم پیش‌فرض با نسبت فشرده‌سازی بالاتر
  • LZ4: الگوریتم جدید با سرعت فوق‌العاده (مناسب برای بارهای کاری سنگین)
-- تنظیم الگوریتم فشرده‌سازی پیش‌فرض برای نشست جاری
SET default_toast_compression = 'lz4';

-- تنظیم برای یک ستون خاص در زمان ایجاد جدول
CREATE TABLE documents (
    id SERIAL,
    content TEXT COMPRESSION lz4
);

مرحلهٔ ۲: ذخیرهٔ خارج از صفحه (Out-of-line Storage)

اگر حتی پس از فشرده‌سازی، داده هنوز بزرگ باشد (بیش از آستانهٔ تعریف‌شده)، به سراغ مرحلهٔ بعد می‌رویم:

  • داده به قطعات ۲۰۰۰ بایتی (TOAST_MAX_CHUNK_SIZE) تقسیم می‌شود
  • قطعات در یک جدول TOAST مخصوص ذخیره می‌شوند
  • در رکورد اصلی، فقط یک اشاره‌گر ۱۸ بایتی قرار می‌گیرد

مرحلهٔ ۳: بازیابی شفاف

در زمان خواندن داده، اگر کاربر ستون بزرگ را انتخاب کرده باشد، PostgreSQL به‌صورت خودکار:

  • اشاره‌گر را دنبال می‌کند
  • قطعات را از جدول TOAST می‌خواند
  • داده را بازسازی می‌کند
  • و در صورت نیاز، آن را از حالت فشرده خارج می‌کند

همهٔ این مراحل برای کاربر و حتی برنامه‌نویس کاملاً شفاف است.


۴. ساختار فیزیکی: معماری داخلی TOAST

۴.۱ نمایش داده در حافظه (varlena representation)

برای درک TOAST، باید با نحوهٔ نمایش داده‌های با طول متغیر در PostgreSQL آشنا شویم. هر مقدار از نوع TEXT یا BYTEA در حافظه با قالبی به نام varlena ذخیره می‌شود .

چهار حالت مختلف برای یک مقدار varlena وجود دارد:

نوعتوضیح
غیر TOAST شدهداده به‌صورت عادی با هدر ۴ بایتی ذخیره شده
یک‌بایتیبرای مقادیر کوچک (زیر ۱۲۷ بایت) با هدر ۱ بایتی
فشرده شدهداده فشرده شده، نیاز به decompress دارد
اشاره‌گر TOASTداده خارج از صفحه ذخیره شده

۴.۲ جدول TOAST

هر جدول که دارای ستون‌های بزرگ باشد، یک جدول TOAST اختصاصی در schema سیستمی pg_toast خواهد داشت. ساختار این جدول ساده اما هوشمندانه است :

ستوننوعتوضیح
chunk_idOIDشناسهٔ یکتای مقدار اصلی
chunk_seqintegerشمارهٔ توالی قطعه
chunk_databyteaدادهٔ واقعی قطعه

یک ایندکس منحصربه‌فرد روی (chunk_id, chunk_seq) بازیابی سریع را ممکن می‌کند.


۵. آزمایش عملی: مشاهدهٔ TOAST در عمل

بیایید با مثال‌های عملی، رفتار TOAST را از نزدیک ببینیم.

۵.۱ ایجاد جدول و درج داده

CREATE TABLE toast_demo (
    id SERIAL PRIMARY KEY,
    title TEXT,  -- ستون کوچک
    content TEXT -- ستون بزرگ
);

-- درج یک ردیف با دادهٔ کوچک
INSERT INTO toast_demo (title, content)
VALUES ('مطلب کوتاه', 'سلام دنیا!');

-- درج یک ردیف با دادهٔ بسیار بزرگ
INSERT INTO toast_demo (title, content)
SELECT 'مطلب بلند', repeat('PostgreSQL TOAST ', 10000);

۵.۲ شناسایی جدول TOAST

-- یافتن OID جدول TOAST مرتبط
SELECT reltoastrelid::regclass AS toast_table
FROM pg_class
WHERE relname = 'toast_demo';

خروجی نمونه:

          toast_table          
-------------------------------
 pg_toast.pg_toast_16395

۵.۳ مشاهدهٔ قطعات ذخیره‌شده

-- مشاهدهٔ چگونگی تقسیم داده به قطعات
SELECT 
    chunk_id,
    chunk_seq,
    pg_size_pretty(length(chunk_data)::bigint) AS chunk_size
FROM pg_toast.pg_toast_16395
ORDER BY chunk_id, chunk_seq
LIMIT 5;

۵.۴ تحلیل اندازه

SELECT
    pg_size_pretty(pg_relation_size('toast_demo')) AS main_table_size,
    pg_size_pretty(pg_total_relation_size('toast_demo')) AS total_size,
    pg_size_pretty(pg_total_relation_size('toast_demo') - pg_relation_size('toast_demo')) AS toast_size;

این تفاوت نشان می‌دهد چه حجمی از داده به TOAST منتقل شده است.


۶. استراتژی‌های ذخیره‌سازی: کنترل هوشمند

چهار استراتژی برای مدیریت ستون‌های بزرگ وجود دارد که می‌توانید بسته به نیاز خود انتخاب کنید :

استراتژیفشرده‌سازیذخیرهٔ خارج از صفحهکاربرد
PLAINستون‌های همیشه کوچک
EXTENDED (پیش‌فرض)تعادل بین فضا و کارایی
EXTERNALدسترسی سریع به بخشی از داده (مثل substring)
MAIN❌ (فقط در آخرین راه)اولویت نگهداری داده در صفحه

تغییر استراتژی یک ستون

-- تغییر به استراتژی EXTERNAL برای دسترسی سریع‌تر به substring
ALTER TABLE toast_demo 
ALTER COLUMN content SET STORAGE EXTERNAL;

تنظیم آستانهٔ TOAST

-- تنظیم آستانه برای یک جدول خاص
ALTER TABLE toast_demo SET (toast_tuple_target = 4096); -- 4KB

۷. تحلیل عملکرد: PGLZ در مقابل LZ4

با معرفی LZ4 در PostgreSQL 14، انتخاب الگوریتم فشرده‌سازی اهمیت بیشتری یافته است .

ویژگیPGLZLZ4
سرعت فشرده‌سازیمتوسطبسیار بالا
سرعت decompressمتوسطفوق‌العاده بالا
نسبت فشرده‌سازیبهترکمی پایین‌تر
مصرف CPUبیشترکمتر

آزمایش benchmark: در یک تست روی مجموعه‌ای از ایمیل‌ها با ستون‌های متنی عریض، استفاده از LZ4 باعث بهبود ۳۷٪ در کارایی کلی و ۷۲٪ در بعضی کوئری‌های خاص شد .

-- تنظیم LZ4 به عنوان پیش‌فرض سراسری
ALTER SYSTEM SET default_toast_compression = 'lz4';
SELECT pg_reload_conf();

۸. ابزارهای پیشرفته برای تحلیل

pageinspect: مشاهدهٔ صفحات

CREATE EXTENSION pageinspect;

-- مشاهدهٔ محتوای صفحه ۰
SELECT * FROM heap_page_items(get_raw_page('toast_demo', 0));

pgstattuple: تحلیل فضای مصرفی

CREATE EXTENSION pgstattuple;

SELECT * FROM pgstattuple('toast_demo');

pg_freespacemap: نقشهٔ فضای آزاد

CREATE EXTENSION pg_freespacemap;

SELECT * FROM pg_freespace('toast_demo') LIMIT 10;

۹. اثرات عملکردی TOAST

✅ مزایا

  • تراکم صفحه افزایش می‌یابد: رکوردهای اصلی کوچک می‌مانند
  • ایندکس‌ها کوچک‌تر می‌شوند: چون ایندکس فقط روی دادهٔ اصلی ساخته می‌شود
  • کش مؤثرتر عمل می‌کند: تعداد بیشتری رکورد در حافظه جای می‌گیرد
  • دسترسی انتخابی: فقط در صورت نیاز دادهٔ بزرگ خوانده می‌شود

⚠️ هزینه‌ها

  • UPDATE دادهٔ TOAST شده گران است: اگر دادهٔ بزرگ تغییر کند، کل قطعات بازنویسی می‌شوند
  • فضای اضافی برای اشاره‌گرها: هر رکورد با دادهٔ TOAST شده، ۱۸ بایت اضافی دارد
  • پیچیدگی مدیریت: نیاز به نظارت بر bloat در جداول TOAST

۱۰. چالش‌ها و راهکارها

مشکل ۱: TOAST bloat

اگر زیاد دادهٔ بزرگ به‌روزرسانی شود، نسخه‌های قدیمی در جدول TOAST باقی می‌مانند.

راه‌حل: نظارت و VACUUM منظم

-- مشاهدهٔ تعداد نسخه‌های مرده
SELECT 
    relname,
    n_live_tup,
    n_dead_tup,
    last_vacuum,
    last_autovacuum
FROM pg_stat_user_tables
WHERE relname LIKE 'pg_toast%';

مشکل ۲: انتخاب اشتباه استراتژی

انتخاب استراتژی نامناسب می‌تواند کارایی را کاهش دهد.

راهنما:

  • اگر زیاد از substring روی ستون‌های بزرگ استفاده می‌کنید → EXTERNAL
  • اگر فضای دیسک اولویت است → EXTENDED با PGLZ
  • اگر سرعت اولویت است → EXTENDED با LZ4
  • اگر داده همیشه کوچک است → PLAIN

۱۱. معماری یکپارچه: هماهنگی مؤلفه‌ها

TOAST به تنهایی کار نمی‌کند، بلکه با سایر مؤلفه‌های معماری PostgreSQL هماهنگ است:

لایهنقش
Heapذخیرهٔ رکوردهای اصلی و اشاره‌گرها
TOASTذخیرهٔ داده‌های بزرگ قطعه‌قطعه شده
FSM (Free Space Map)مدیریت فضای آزاد در صفحات
VM (Visibility Map)بهینه‌سازی VACUUM

این تفکیک وظایف، نمونه‌ای از طراحی ماژولار در سیستم‌های پایگاه داده است.


۱۲. نکات عملی برای طراحان و مدیران

طراحی schema

  • ستون‌هایی که به‌ندرت خوانده می‌شوند اما بزرگ هستند، در جدول جداگانه قرار دهید
  • اگر دادهٔ بزرگ مکرراً به‌روزرسانی می‌شود، آن را از TOAST خارج کنید
  • از EXTERNAL برای ستون‌هایی که دسترسی جزئی دارند استفاده کنید

نظارت

-- مشاهدهٔ ۱۰ جدول با بیشترین حجم TOAST
SELECT
    nspname AS schema,
    relname AS table,
    pg_size_pretty(pg_total_relation_size(c.oid)) AS total,
    pg_size_pretty(pg_relation_size(c.oid)) AS main,
    pg_size_pretty(pg_total_relation_size(c.oid) - pg_relation_size(c.oid)) AS toast
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind = 'r' AND nspname NOT IN ('pg_catalog', 'information_schema')
ORDER BY (pg_total_relation_size(c.oid) - pg_relation_size(c.oid)) DESC
LIMIT 10;

تنظیمات پیشنهادی

-- فعال‌سازی LZ4 برای ستون‌های متنی جدید
ALTER DATABASE mydb SET default_toast_compression = 'lz4';

-- افزایش آستانه برای جدولی با رکوردهای نسبتاً یکسان
ALTER TABLE logs SET (toast_tuple_target = 8192);

۱۳. جمع‌بندی: چرا TOAST اهمیت دارد؟

TOAST یک نمونهٔ عالی از مهندسی هوشمندانه در PostgreSQL است. این مکانیزم:

  • محدودیت فیزیکی صفحهٔ ۸ کیلوبایتی را دور می‌زند
  • داده‌های بزرگ را به‌صورت خودکار و شفاف مدیریت می‌کند
  • بین فشرده‌سازی و ذخیرهٔ خارج از صفحه تعادل برقرار می‌کند
  • heap اصلی را کوچک و کارآمد نگه می‌دارد
  • امکان انتخاب استراتژی مناسب برای هر سناریو را فراهم می‌کند

از دیدگاه معماری داده، TOAST نمونۀ indirection-based storage optimization است: با افزودن یک لایهٔ انتزاع (اشاره‌گرها)، محدودیت‌های فیزیکی را به‌شکل منطقی مدیریت می‌کند.


۱۴. چشم‌انداز آینده

جامعهٔ PostgreSQL همواره در حال بهبود TOAST است. از جمله پروژه‌های در حال بحث:

  • Direct TOAST: استفاده از اشاره‌گر مستقیم به موقعیت فیزیکی (TID) به جای OID برای سرعت بیشتر
  • پشتیبانی از chunk_size پویا: تنظیم هوشمند اندازهٔ قطعات
  • فشرده‌سازی سطح صفحه: استفاده از دیکشنری‌های مشترک برای فشرده‌سازی بهتر

نتیجه‌گیری نهایی

درک TOAST برای هر کسی که با PostgreSQL کار می‌کند ضروری است. این مؤلفه، مانند MVCC، بخشی از DNA این پایگاه داده است. شاید هرگز مستقیماً با آن کار نکنید، اما رفتار آن بر عملکرد، فضای دیسک و طراحی schema شما تأثیر می‌گذارد.

TOAST به ما می‌آموزد که گاهی بهترین راه برای مدیریت محدودیت‌ها، پذیرش آنها و ایجاد لایه‌ای هوشمند برای دور زدنشان است. همان‌طور که در ابتدا گفتیم، واقعاً «بهترین چیز پس از نان برش‌خورده» است.

🎥 محتوای ویدئویی کارگاه در بخش زیر قابل مشاهده است.

فروشگاه
جستجو
دوره ها

لطفا کلمات کلیدی را وارد کنید