MVCC في PostgreSQL-2. شوك ، ملفات ، صفحات

في آخر مرة تحدثنا فيها عن تناسق البيانات ، درسنا الفرق بين مستويات العزلة في المعاملة من وجهة نظر المستخدم وتوصلنا إلى معرفة أهمية هذا الأمر. بدأنا الآن في استكشاف كيفية تنفيذ PostgreSQL لعزل اللقطة وتزامن التحويلات المتعددة.

في هذه المقالة ، سننظر في كيفية وضع البيانات فعليًا في الملفات والصفحات. هذا يبعدنا عن مناقشة العزلة ، لكن مثل هذا الاستطراد ضروري لفهم ما يلي. سنحتاج إلى معرفة كيفية تنظيم تخزين البيانات على مستوى منخفض.

العلاقات


إذا نظرت داخل الجداول والفهارس ، اتضح أنها منظمة بطريقة مماثلة. كلاهما كائنات قاعدة بيانات تحتوي على بعض البيانات التي تتكون من الصفوف.

ليس هناك شك في أن الجدول يتكون من صفوف ، لكن هذا أقل وضوحًا بالنسبة إلى الفهرس. ومع ذلك ، تخيل شجرة B: تتكون من العقد التي تحتوي على قيم مفهرسة ومراجع إلى العقد الأخرى أو صفوف الجدول. هذه العقد يمكن اعتبارها صفوف فهرس ، وفي الواقع ، فهي كذلك.

في الواقع ، يتم تنظيم عدد قليل من الكائنات بطريقة مماثلة: التسلسلات (جداول أساسها صف واحد بشكل أساسي) وطرق العرض الملموسة (بشكل أساسي ، الجداول التي تتذكر الاستعلام). وهناك أيضًا طرق عرض منتظمة ، لا تخزن البيانات بنفسها ، ولكنها تشبه في جميع الحالات الأخرى الجداول.

تسمى كل هذه الكائنات في PostgreSQL علاقة الكلمات الشائعة. هذه الكلمة غير لائقة للغاية لأنها مصطلح من نظرية العلائقية. يمكنك رسم متوازي بين العلاقة والجدول (العرض) ، ولكن بالتأكيد ليس بين العلاقة والفهرس. ولكن حدث ذلك تمامًا: يتجلى الأصل الأكاديمي لـ PostgreSQL في نفسه. يبدو لي أن الجداول وطرق العرض كانت تسمى أولاً ، والباقي تضخم مع مرور الوقت.

لنكون أكثر بساطة ، سنناقش الجداول والفهارس ، لكن العلاقات الأخرى مرتبة بنفس الطريقة تمامًا.

الشوك والملفات


عادة ما تتوافق عدة شوكات مع كل علاقة. يمكن أن تحتوي الشوكات على عدة أنواع ، ويحتوي كل منها على نوع معين من البيانات.

إذا كان هناك شوكة ، يتم تمثيلها أولاً بالملف الوحيد. اسم الملف هو معرف رقمي ، والذي يمكن إلحاقه بنهاية تتوافق مع اسم الشوكة.

يزداد حجم الملف تدريجياً وعندما يصل حجمه إلى 1 غيغابايت ، يتم إنشاء ملف جديد من نفس الشوكة (تسمى هذه الملفات أحيانًا شرائح ). يتم إلحاق الرقم الترتيبي للجزء في نهاية اسم الملف.

نشأ قيود حجم الملف التي تبلغ 1 غيغابايت تاريخيا لدعم أنظمة الملفات المختلفة ، والتي لا يستطيع بعضها التعامل مع الملفات ذات الحجم الأكبر. يمكنك تغيير هذا القيد عند إنشاء PostgreSQL ( ./configure --with-segsize ).

لذلك ، يمكن أن تتوافق عدة ملفات على القرص مع علاقة واحدة. على سبيل المثال ، بالنسبة لطاولة صغيرة سيكون هناك ثلاثة منهم.

سيتم تخزين جميع ملفات الكائنات التي تنتمي إلى مساحة جدول واحدة وقاعدة بيانات واحدة في دليل واحد. تحتاج إلى وضع ذلك في الاعتبار لأن أنظمة الملفات عادة ما تفشل في العمل بشكل جيد مع عدد كبير من الملفات في الدليل.

لاحظ هنا أن الملفات ، بدورها ، تنقسم إلى صفحات (أو كتل ) ، عادةً بمقدار 8 كيلوبايت. سنناقش البنية الداخلية للصفحات أكثر قليلاً.



الآن دعونا نلقي نظرة على أنواع الشوكة.

الشوكة الرئيسية هي البيانات بحد ذاتها: صفوف الجدول والفهرس ذاتها. الشوكة الرئيسية متاحة لأي علاقات (باستثناء طرق العرض التي لا تحتوي على بيانات).

تتكون أسماء ملفات الشوكة الرئيسية من المعرف الرقمي الوحيد. على سبيل المثال ، هذا هو المسار إلى الجدول الذي أنشأناه آخر مرة:

 => SELECT pg_relation_filepath('accounts'); 
  pg_relation_filepath ---------------------- base/41493/41496 (1 row) 

من أين تنشأ هذه المعرفات؟ يتوافق دليل "الأساس" مع مساحة الجداول "pg_default". الدليل الفرعي التالي ، المتوافق مع قاعدة البيانات ، هو موقع ملف الاهتمام:

 => SELECT oid FROM pg_database WHERE datname = 'test'; 
  oid ------- 41493 (1 row) 

 => SELECT relfilenode FROM pg_class WHERE relname = 'accounts'; 
  relfilenode ------------- 41496 (1 row) 

المسار نسبي ، يتم تحديده بدءًا من دليل البيانات (PGDATA). علاوة على ذلك ، يتم تحديد جميع المسارات في PostgreSQL تقريبًا بدءًا من PGDATA. بفضل هذا ، يمكنك نقل PGDATA بأمان إلى موقع مختلف - لا شيء يحصره (باستثناء أنه قد يكون مطلوبًا تعيين المسار إلى المكتبات في LD_LIBRARY_PATH).

علاوة على ذلك ، النظر في نظام الملفات:

 postgres$ ls -l --time-style=+ /var/lib/postgresql/11/main/base/41493/41496 
 -rw------- 1 postgres postgres 8192 /var/lib/postgresql/11/main/base/41493/41496 

يتوفر تفرع التهيئة فقط للجداول غير المقيدة (التي تم إنشاؤها باستخدام UNLOGGED المحددة) وفهارسها. كائنات مثل هذه لا تختلف بأي حال عن الكائنات العادية باستثناء أن العمليات معهم لم يتم تسجيلها في سجل الكتابة المسبق (WAL). لهذا السبب ، يكون التعامل معهم أسرع ، لكن من المستحيل استرداد البيانات في الحالة الثابتة في حالة حدوث عطل. لذلك ، أثناء الاسترداد ، يقوم PostgreSQL فقط بإزالة كافة شوكات هذه الكائنات ويكتب شوكة التهيئة بدلاً من الشوكة الرئيسية. هذا ينتج كائن فارغ. سنناقش قطع الأشجار بالتفصيل ، ولكن في سلسلة أخرى.

يتم تسجيل جدول "الحسابات" ، وبالتالي ، لا يحتوي على تفرع تهيئة. ولكن للتجربة ، يمكننا إيقاف تسجيل الدخول:

 => ALTER TABLE accounts SET UNLOGGED; => SELECT pg_relation_filepath('accounts'); 
  pg_relation_filepath ---------------------- base/41493/41507 (1 row) 

يوضح المثال أن إمكانية تشغيل تسجيل الدخول وإيقاف تشغيله أثناء الطيران مرتبطة بإعادة كتابة البيانات إلى ملفات بأسماء مختلفة.

شوكة التهيئة لها نفس اسم الشوكة الرئيسية ، ولكن باستخدام لاحقة "_init":

 postgres$ ls -l --time-style=+ /var/lib/postgresql/11/main/base/41493/41507_init 
 -rw------- 1 postgres postgres 0 /var/lib/postgresql/11/main/base/41493/41507_init 

خريطة المساحة الحرة عبارة عن شوكة تقوم بتتبع مدى توفر مساحة خالية داخل الصفحات. تتغير هذه المساحة باستمرار: إنها تتناقص عندما يتم إضافة إصدارات جديدة من الصفوف وتزيد أثناء الفراغ. يتم استخدام خريطة المساحة الحرة أثناء إدراج إصدارات صف جديدة للعثور بسرعة على صفحة مناسبة ، حيث تتناسب البيانات المراد إضافتها.

يحتوي اسم خريطة المساحة الحرة على لاحقة "_fsm". لكن هذا الملف لا يظهر على الفور ، ولكن فقط عند الحاجة. أسهل طريقة لتحقيق ذلك هي تفريغ طاولة (سنشرح لماذا عندما يحين الوقت):

 => VACUUM accounts; 

 postgres$ ls -l --time-style=+ /var/lib/postgresql/11/main/base/41493/41507_fsm 
 -rw------- 1 postgres postgres 24576 /var/lib/postgresql/11/main/base/41493/41507_fsm 

خريطة الرؤية عبارة عن شوكة حيث يتم تمييز الصفحات التي تحتوي فقط على إصدارات صف محدثة بت واحد. تقريبًا ، فهذا يعني أنه عندما تحاول معاملة قراءة صف من هذه الصفحة ، يمكن إظهار الصف دون التحقق من ظهوره. في المقالات التالية ، سنناقش بالتفصيل كيف يحدث هذا.

 postgres$ ls -l --time-style=+ /var/lib/postgresql/11/main/base/41493/41507_vm 
 -rw------- 1 postgres postgres 8192 /var/lib/postgresql/11/main/base/41493/41507_vm 

صفحات


كما ذكرنا سابقًا ، يتم تقسيم الملفات بشكل منطقي إلى صفحات.

عادةً ما يكون حجم الصفحة 8 كيلو بايت. يمكن تغيير الحجم ضمن حدود معينة (16 كيلو بايت أو 32 كيلو بايت) ، ولكن فقط أثناء ./configure --with-blocksize ( ./configure --with-blocksize ). يمكن أن يعمل مثيل مدمج وتشغيل مع صفحات من نفس الحجم.

بغض النظر عن الشوكة التي تنتمي إليها الملفات ، يستخدمها الخادم بطريقة مشابهة إلى حد ما. تتم قراءة الصفحات أولاً في ذاكرة التخزين المؤقت ، حيث يمكن للعمليات قراءتها وتغييرها ؛ ثم كلما دعت الحاجة ، يتم إخلاؤهم إلى القرص.

تحتوي كل صفحة على قسم داخلي وبصفة عامة تحتوي على الأقسام التالية:

        0 + ----------------------------------- +
           |  رأس |
       24 + ----------------------------------- +
           |  مجموعة من المؤشرات إلى إصدارات الصف |
    أقل + ----------------------------------- +
           |  مساحة حرة |
    العلوي + ----------------------------------- +
           |  إصدارات الصف |
  خاص + ----------------------------------- +
           |  مساحة خاصة |
 pageize + ----------------------------------- +

يمكنك بسهولة معرفة أحجام هذه الأقسام باستخدام pageinspect "extension":

 => CREATE EXTENSION pageinspect; => SELECT lower, upper, special, pagesize FROM page_header(get_raw_page('accounts',0)); 
  lower | upper | special | pagesize -------+-------+---------+---------- 40 | 8016 | 8192 | 8192 (1 row) 

نحن هنا ننظر إلى رأس الصفحة الأولى (صفر) من الجدول. بالإضافة إلى أحجام المناطق الأخرى ، يحتوي الرأس على معلومات مختلفة حول الصفحة ، والتي لسنا مهتمين بها حتى الآن.

يوجد أسفل الصفحة مساحة خاصة ، وهي فارغة في هذه الحالة. يتم استخدامه فقط للفهارس ، وحتى ليس لكل منهم. "في الأسفل" هنا يعكس ما يوجد في الصورة ؛ قد يكون أكثر دقة ليقول "في عناوين عالية".

بعد المساحة الخاصة ، توجد إصدارات الصف ، أي تلك البيانات التي نخزنها في الجدول بالإضافة إلى بعض المعلومات الداخلية.

في أعلى الصفحة ، مباشرة بعد الرأس ، يوجد جدول المحتويات: صفيف المؤشرات إلى إصدارات الصف المتاحة في الصفحة.

يمكن ترك مساحة حرة بين إصدارات الصفوف والمؤشرات (يتم الاحتفاظ بهذه المساحة الحرة في خريطة المساحة الحرة). لاحظ أنه لا يوجد تجزئة للذاكرة داخل الصفحة - حيث يتم تمثيل كل المساحة الخالية بمساحة واحدة متجاورة.

مؤشرات


لماذا هي مؤشرات إلى الصف الإصدارات اللازمة؟ الشيء هو أن صفوف الفهرس يجب أن تشير بطريقة ما إلى إصدارات الصف في الجدول. من الواضح أن المرجع يجب أن يحتوي على رقم الملف ورقم الصفحة في الملف وبعض مؤشرات إصدار الصف. يمكننا استخدام الإزاحة من بداية الصفحة كمؤشر ، لكنه غير مريح. لن نتمكن من نقل إصدار صف داخل الصفحة لأنه سيؤدي إلى كسر المراجع المتاحة. وهذا من شأنه أن يؤدي إلى تجزئة المساحة داخل الصفحات وغيرها من العواقب المزعجة. لذلك ، يشير الفهرس إلى رقم المؤشر ، ويشير المؤشر إلى الموقع الحالي لإصدار الصف في الصفحة. وهذا هو معالجة غير مباشرة.

يشغل كل مؤشر أربعة بايت بالضبط ويحتوي على:

  • إشارة إلى إصدار الصف
  • حجم هذا الإصدار الصف
  • عدة بايت لتحديد حالة إصدار الصف

تنسيق البيانات


تنسيق البيانات على القرص هو بالضبط نفس تمثيل البيانات في ذاكرة الوصول العشوائي. تتم قراءة الصفحة في ذاكرة التخزين المؤقت "كما هي" ، دون أي تحويلات. لذلك ، تتعارض ملفات البيانات من نظام أساسي واحد مع الأنظمة الأساسية الأخرى.

على سبيل المثال ، في بنية X86 ، يكون ترتيب البايت من الأقل أهمية إلى البايت الأكثر أهمية (القليل-endian) ، z / Architecture يستخدم الترتيب العكسي (big-endian) ، وفي ARM يمكن تبديل الترتيب.

توفر العديد من الهياكل لمحاذاة البيانات على حدود الكلمات الجهاز. على سبيل المثال ، على نظام 32 بت x 86 ، سيتم محاذاة أرقام عدد صحيح (اكتب "عدد صحيح" ، والتي تشغل 4 بايت) على حدود الكلمات ذات 4 بايت ، بنفس طريقة الأرقام المزدوجة الدقة (اكتب "الدقة المزدوجة" ، والتي تحتل 8 بايت). وعلى نظام 64 بت ، سيتم محاذاة أرقام الدقة المزدوجة على حدود من 8 بايت الكلمات. هذا سبب واحد لعدم التوافق.

بسبب المحاذاة ، يعتمد حجم صف الجدول على ترتيب الحقل. عادة لا يكون هذا التأثير ملحوظًا جدًا ، ولكن في بعض الأحيان ، قد يؤدي إلى نمو كبير في الحجم. على سبيل المثال ، إذا كانت الحقول من الأنواع "char (1)" و "integer" متشابكة ، فعادةً ما تضيع 3 بايت بينهما. لمزيد من التفاصيل حول هذا ، يمكنك الاطلاع على عرض نيكولاي شابلوف " Tuple internals ".

إصدارات الصف والخبز


سنناقش تفاصيل الهيكل الداخلي لإصدارات الصف في المرة القادمة. في هذه المرحلة ، من المهم فقط أن نعرف أن كل إصدار يجب أن يناسب صفحة واحدة تمامًا: ليس لدى PostgreSQL أي طريقة "لتوسيع" الصف إلى الصفحة التالية. يتم استخدام تقنية تخزين السمات كبيرة الحجم (TOAST) بدلاً من ذلك. يشير الاسم نفسه إلى أنه يمكن تقسيم صف إلى خبز محمص.

المزاح بصرف النظر ، توست ينطوي على العديد من الاستراتيجيات. يمكننا نقل قيم السمات الطويلة إلى جدول داخلي منفصل بعد تقسيمها إلى أجزاء توست صغيرة. هناك خيار آخر وهو ضغط القيمة بحيث يتناسب إصدار الصف مع صفحة عادية. ويمكننا القيام بالأمرين التاليين: أولاً ضغط ثم تفكيك ونقل.

لكل جدول أساسي ، يمكن إنشاء جدول TOAST منفصل إذا لزم الأمر ، واحد لجميع السمات (مع فهرس على ذلك). توافر سمات يحتمل أن تكون طويلة يحدد هذه الحاجة. على سبيل المثال ، إذا كان الجدول يحتوي على عمود من النوع "رقمي" أو "نص" ، فسيتم إنشاء جدول TOAST على الفور حتى إذا لم يتم استخدام القيم الطويلة.

نظرًا لأن جدول TOAST عبارة عن جدول عادي ، فإنه يحتوي على نفس مجموعة الشوك. وهذا يضاعف عدد الملفات التي تتوافق مع جدول.

يتم تعريف الاستراتيجيات الأولية حسب أنواع بيانات الأعمدة. يمكنك إلقاء نظرة عليها باستخدام الأمر \d+ في psql ، ولكن نظرًا لأنه يؤدي إلى إخراج الكثير من المعلومات الأخرى ، فإننا سوف نستفسر عن كتالوج النظام:

 => SELECT attname, atttypid::regtype, CASE attstorage WHEN 'p' THEN 'plain' WHEN 'e' THEN 'external' WHEN 'm' THEN 'main' WHEN 'x' THEN 'extended' END AS storage FROM pg_attribute WHERE attrelid = 'accounts'::regclass AND attnum > 0; 
  attname | atttypid | storage ---------+----------+---------- id | integer | plain number | text | extended client | text | extended amount | numeric | main (4 rows) 

أسماء الاستراتيجيات تعني:

  • عادي - TOAST غير مستخدم (يستخدم لأنواع البيانات المعروفة بأنها قصيرة ، مثل "عدد صحيح").
  • ممتد - كل من الضغط والتخزين في جدول TOAST منفصل مسموح به
  • يتم تخزين القيم الخارجية الطويلة في جدول TOAST دون ضغط.
  • الرئيسية - يتم ضغط القيم الطويلة أولاً والوصول فقط إلى جدول TOAST إذا لم يساعد الضغط.

بشكل عام ، الخوارزمية هي كما يلي. يهدف PostgreSQL إلى احتواء أربعة صفوف على الأقل على صفحة واحدة. لذلك ، إذا تجاوز حجم الصف واحدًا خارج الصفحة ، مع مراعاة الرأس (2040 بايت لصفحة 8K عادية) ، يجب تطبيق TOAST على جزء من القيم. نتبع الأمر الموضح أدناه ونتوقف بمجرد أن يتجاوز الصف الحد الأدنى:

  1. أولاً ننتقل إلى السمات باستخدام الاستراتيجيات "الخارجية" و "الموسعة" بدءًا من أطول سمة إلى الأقصر. يتم ضغط السمات "الموسعة" (إذا كانت فعالة) وإذا تجاوزت القيمة نفسها واحداً من الصفحة ، فإنها تدخل على الفور في جدول TOAST. تتم معالجة السمات "الخارجية" بنفس الطريقة ، ولكنها غير مضغوطة.
  2. إذا لم يكن إصدار الصف بعد الصفحة الأولى مناسبًا للصفحة ، فنحن نرسل السمات المتبقية باستخدام الاستراتيجيات "الخارجية" و "الموسعة" إلى جدول TOAST.
  3. إذا لم يساعد ذلك أيضًا ، فسنحاول ضغط السمات باستخدام الاستراتيجية "الرئيسية" ، ولكن نتركها في صفحة الجدول.
  4. وفقط إذا كان ذلك بعد ذلك ، فإن الصف ليس قصيرًا بما فيه الكفاية ، وتدخل السمات "الرئيسية" في جدول TOAST.

قد يكون من المفيد في بعض الأحيان تغيير الإستراتيجية لبعض الأعمدة. على سبيل المثال ، إذا كان من المعروف مسبقًا أنه لا يمكن ضغط البيانات الموجودة في عمود ما ، فيمكننا تعيين استراتيجية "خارجية" لها ، والتي تمكننا من توفير الوقت عن طريق تجنب محاولات الضغط غير المجدية. ويتم ذلك على النحو التالي:

 => ALTER TABLE accounts ALTER COLUMN number SET STORAGE external; 

عند إعادة تشغيل الاستعلام ، نحصل على:

  attname | atttypid | storage ---------+----------+---------- id | integer | plain number | text | external client | text | extended amount | numeric | main 

توجد جداول وفهارس TOAST في مخطط pg_toast المنفصل ، وبالتالي ، عادة ما تكون غير مرئية. بالنسبة للجداول المؤقتة ، يتم استخدام مخطط "pg_toast_temp_ N " بشكل مشابه للمخطط المعتاد "pg_temp_ N ".

بالطبع ، إذا أعجبك ، فلن يعيقك التجسس على الآليات الداخلية للعملية. لنفترض أنه في جدول "الحسابات" هناك ثلاث سمات طويلة محتملة ، وبالتالي ، يجب أن يكون هناك جدول TOAST. ومن هنا:

 => SELECT relnamespace::regnamespace, relname FROM pg_class WHERE oid = ( SELECT reltoastrelid FROM pg_class WHERE relname = 'accounts' ); 
  relnamespace | relname --------------+---------------- pg_toast | pg_toast_33953 (1 row) 

 => \d+ pg_toast.pg_toast_33953 
 TOAST table "pg_toast.pg_toast_33953" Column | Type | Storage ------------+---------+--------- chunk_id | oid | plain chunk_seq | integer | plain chunk_data | bytea | plain 

من المعقول أن يتم تطبيق الإستراتيجية "البسيطة" على الخبز المحمص الذي تم تقطيع الصف إليه: لا يوجد توست من المستوى الثاني.

يخفي PostgreSQL الفهرس بشكل أفضل ، لكن ليس من الصعب العثور على أي من:

 => SELECT indexrelid::regclass FROM pg_index WHERE indrelid = ( SELECT oid FROM pg_class WHERE relname = 'pg_toast_33953' ); 
  indexrelid ------------------------------- pg_toast.pg_toast_33953_index (1 row) 

 => \d pg_toast.pg_toast_33953_index 
 Unlogged index "pg_toast.pg_toast_33953_index" Column | Type | Key? | Definition -----------+---------+------+------------ chunk_id | oid | yes | chunk_id chunk_seq | integer | yes | chunk_seq primary key, btree, for table "pg_toast.pg_toast_33953" 

يستخدم عمود "العميل" استراتيجية "موسعة": سيتم ضغط قيمه. لنفحص:

 => UPDATE accounts SET client = repeat('A',3000) WHERE id = 1; => SELECT * FROM pg_toast.pg_toast_33953; 
  chunk_id | chunk_seq | chunk_data ----------+-----------+------------ (0 rows) 

لا يوجد شيء في جدول TOAST: يتم ضغط الأحرف المكررة بشكل جيد وبعد الضغط ، تناسب القيمة صفحة الجدول المعتادة.

والآن دع اسم العميل يتكون من أحرف عشوائية:

 => UPDATE accounts SET client = ( SELECT string_agg( chr(trunc(65+random()*26)::integer), '') FROM generate_series(1,3000) ) WHERE id = 1 RETURNING left(client,10) || '...' || right(client,10); 
  ?column? ------------------------- TCKGKZZSLI...RHQIOLWRRX (1 row) 

لا يمكن ضغط هذا التسلسل ، ويتم إدخاله في جدول TOAST:

 => SELECT chunk_id, chunk_seq, length(chunk_data), left(encode(chunk_data,'escape')::text, 10) || '...' || right(encode(chunk_data,'escape')::text, 10) FROM pg_toast.pg_toast_33953; 
  chunk_id | chunk_seq | length | ?column? ----------+-----------+--------+------------------------- 34000 | 0 | 2000 | TCKGKZZSLI...ZIPFLOXDIW 34000 | 1 | 1000 | DDXNNBQQYH...RHQIOLWRRX (2 rows) 

يمكننا أن نرى أن البيانات مقسمة إلى أجزاء 2000 بايت.

عند الوصول إلى قيمة طويلة ، يقوم PostgreSQL تلقائيًا وشفافًا للتطبيق باستعادة القيمة الأصلية وإعادتها إلى العميل.

بالتأكيد ، من الضروري جدًا ضغط الموارد وتقسيمها ثم استعادتها. لذلك ، فإن تخزين البيانات الضخمة في PostgreSQL ليس هو أفضل فكرة ، خاصةً إذا تم استخدامها بشكل متكرر ولا يتطلب الاستخدام منطقًا للمعاملات (على سبيل المثال: عمليات المسح للمستندات المحاسبية الأصلية). البديل الأكثر فائدة هو تخزين هذه البيانات على نظام الملفات مع أسماء الملفات المخزنة في DBMS.

يتم استخدام جدول TOAST فقط للوصول إلى قيمة طويلة. بالإضافة إلى ذلك ، يتم دعم التزامن التحويل الخاص بجدول TOAST: ما لم يمس تحديث البيانات قيمة طويلة ، فإن إصدار صف جديد سيشير إلى نفس القيمة في جدول TOAST ، مما يوفر مساحة.

لاحظ أن TOAST يعمل فقط للجداول ، ولكن ليس للفهارس. هذا يفرض قيودًا على حجم المفاتيح المراد فهرستها.
لمزيد من التفاصيل حول بنية البيانات الداخلية ، يمكنك قراءة الوثائق .

اقرأ على .

Source: https://habr.com/ru/post/ar469087/


All Articles