ترحيل مخطط قاعدة البيانات دون توقف عن العمل لـ postgresql باستخدام django كمثال

مقدمة


مرحبا يا هبر!


أريد مشاركة تجربة كتابة عمليات الترحيل لـ postgres و django. سيكون هذا بشكل أساسي حول postgres ، django هو إضافة جيدة هنا ، نظرًا لأنه يحتوي على ترحيل تلقائي لمخطط البيانات لتغييرات النموذج خارج الصندوق ، أي أنه يحتوي على قائمة كاملة إلى حد ما من عمليات العمل لتغيير المخطط. يمكن استبدال Django بأي إطار / مكتبة مفضلة - من المرجح أن تكون المقاربات متشابهة.


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


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


يمكنك تقسيم منطق العمل مع قاعدة بيانات معظم التطبيقات إلى 3 أجزاء:


  1. عمليات الترحيل - تغيير مخطط قاعدة البيانات (الجداول) ، لنفترض أننا نديرها دائمًا في سلسلة محادثات واحدة.
  2. منطق الأعمال - العمل المباشر مع البيانات (في جداول المستخدم) ، يعمل بنفس البيانات بشكل مستمر وتنافسي.
  3. ترحيل البيانات - لا تقم بتغيير مخططات البيانات ، فهي تعمل بشكل أساسي مثل منطق الأعمال ، افتراضيًا ، عندما نتحدث عن منطق الأعمال ، سنعني أيضًا عمليات ترحيل البيانات.

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


يمكن أن يكون غياب وقت التوقف عن العمل شرطًا حرجًا للأعمال التجارية ، والذي يجب الالتزام به من خلال أي جهود.


طرح العملية


المتطلبات الرئيسية عند طرحها:


  1. لدينا قاعدة عمل واحدة.
  2. لدينا العديد من الآلات حيث يدور منطق الأعمال.
  3. السيارات ذات منطق الأعمال مخفية خلف الموازن.
  4. يعمل تطبيقنا جيدًا قبل وأثناء وبعد الترحيل المتداول (يعمل الرمز القديم بشكل صحيح مع مخطط قاعدة البيانات القديم والجديد).
  5. يعمل تطبيقنا جيدًا قبل وأثناء وبعد تحديث الرمز على السيارات (يعمل الرمز القديم والجديد بشكل صحيح مع مخطط قاعدة البيانات الحالي).

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


طلب طرح مباشر:


  1. غمرت الهجرة.
  2. إزالة جهاز واحد من الموازن ، وتحديث الجهاز وإعادة تشغيله ، وإعادة الجهاز إلى الموازن ؛
  3. كرر الخطوة السابقة لتحديث جميع السيارات.

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


  1. إزالة جهاز واحد من الموازن ، وتحديث الجهاز وإعادة تشغيله ، وإعادة الجهاز إلى الموازن ؛
  2. كرر الخطوة السابقة لتحديث جميع السيارات ؛
  3. غمرت الهجرة.

النظرية


Postgres هي قاعدة بيانات ممتازة ، يمكننا كتابة تطبيق سيكتب ويقرأ نفس البيانات في مئات وآلاف التدفقات ، ومع احتمالية عالية يمكننا التأكد من أن بياناتنا ستبقى صالحة ولن تتلف بشكل عام ، ACID الكامل. تطبق Postgres عدة آليات لتحقيق ذلك ؛ إحداها هي الحجب.


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


أقفال مستوى الجدول


على مستوى الجدول ، تحتوي postgres على عدة أنواع من الأقفال ، والميزة الرئيسية هي أن لديهم تعارضات ، أي أنه لا يمكن إجراء عمليتين مع أقفال متضاربة في وقت واحد:


ACCESS SHAREROW SHAREROW EXCLUSIVESHARE UPDATE EXCLUSIVESHARESHARE ROW EXCLUSIVEEXCLUSIVEACCESS EXCLUSIVE
ACCESS SHAREX
ROW SHAREXX
ROW EXCLUSIVEXXXX
SHARE UPDATE EXCLUSIVEXXXXX
SHAREXXXXX
SHARE ROW EXCLUSIVEXXXXXX
EXCLUSIVEXXXXXXX
ACCESS EXCLUSIVEXXXXXXXX

على سبيل المثال ، ALTER TABLE tablename ADD COLUMN newcolumn integer و SELECT COUNT(*) FROM tablename يجب أن يتم تنفيذهما بدقة واحدة تلو الأخرى ، وإلا فلن نتمكن من معرفة الأعمدة التي سيتم الرجوع إليها إلى COUNT(*) .


في عمليات ترحيل django (القائمة الكاملة أدناه) ، توجد العمليات التالية وأقفالها المقابلة:


حجبالعمليات
ACCESS EXCLUSIVECREATE SEQUENCE ، DROP SEQUENCE ، CREATE TABLE ، DROP TABLE ، ALTER TABLE ، DROP INDEX
SHARECREATE INDEX
SHARE UPDATE EXCLUSIVECREATE INDEX CONCURRENTLY ، DROP INDEX CONCURRENTLY ، ALTER TABLE VALIDATE CONSTRAINT

من بين التعليقات ، لا تحتوي جميع ALTER TABLE على تأمين ACCESS EXCLUSIVE ALTER TABLE ، كما أن عمليات ترحيل django لا تحتوي على CREATE INDEX CONCURRENTLY و ALTER TABLE VALIDATE CONSTRAINT ، ولكن ستكون هناك حاجة إليها كبديل أكثر أمانًا للعمليات القياسية بعد ذلك بقليل.


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


حجبالعملياتيتعارض مع الأقفاليتعارض مع العمليات
ACCESS SHARESELECTACCESS EXCLUSIVEALTER TABLE DROP INDEX ، DROP INDEX
ROW SHARESELECT FOR UPDATEACCESS EXCLUSIVE EXCLUSIVEALTER TABLE DROP INDEX ، DROP INDEX
ROW EXCLUSIVEINSERT ، UPDATE ، DELETEACCESS EXCLUSIVE EXCLUSIVE SHARE ROW EXCLUSIVE SHAREALTER TABLE DROP INDEX ، DROP INDEX ، CREATE INDEX

يمكن تلخيص نقطتين هنا:


  1. إذا كان هناك بديل مع قفل أسهل ، يمكنك استخدامه كـ CREATE INDEX و CREATE INDEX CONCURRENTLY .
  2. تتعارض معظم عمليات الترحيل لتغيير مخطط البيانات مع منطق الأعمال ، علاوة على ذلك ، فإنها تتعارض مع ACCESS EXCLUSIVE ، أي أننا لن نتمكن حتى من تحديد SELECT أثناء الضغط على هذا القفل ومن المحتمل أن نتوقف عن العمل هنا ، باستثناء الحالة التي لا تعمل فيها هذه العملية على الفور وسيكون وقت التوقف بضع ثوان.

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


أقفال قياسية


على مستوى التسجيل ، هناك أيضًا أقفال https://www.postgresql.org/docs/current/static/explicit-locking.html#LOCKING-ROWS ، فهي تتعارض أيضًا ، ولكنها تؤثر فقط على منطق أعمالنا:


FOR KEY SHAREFOR SHAREFOR NO KEY UPDATEFOR UPDATE
FOR KEY SHAREX
FOR SHAREXX
FOR NO KEY UPDATEXXX
FOR UPDATEXXXX

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


ترتيب العمليات


معرفة مهمة أخرى هي كيف سيتم تنفيذ العمليات ، ومتى وكيف تأخذ الأقفال وتحررها:


الصورة


هنا يمكنك تسليط الضوء على العناصر التالية:


  1. وقت تنفيذ العملية - للترحيل هو وقت الاحتفاظ بالقفل ، إذا تم تعليق القفل الثقيل لفترة طويلة ، فسيكون لدينا وقت تعطل ، على سبيل المثال ، يمكن أن يكون مع CREATE INDEX أو ALTER TABLE ADD COLUMN SET DEFAULT (في postgres 11 هذا أفضل).
  2. وقت الانتظار لأقفال متضاربة - أي أن الترحيل ينتظر حتى تنجح جميع الطلبات المتضاربة ، وفي هذا الوقت ستنتظر الطلبات الجديدة الترحيل لدينا ، يمكن أن تكون الطلبات البطيئة شديدة الخطورة هنا ، إما ببساطة ليست مثالية أو تحليلية ، لذلك لا ينبغي أن تكون الطلبات بطيئة أثناء الهجرة.
  3. عدد الطلبات في الثانية - إذا كان لدينا العديد من الطلبات التي تعمل لفترة طويلة ، عندها يمكن أن تنتهي الاتصالات المجانية بسرعة وبدلاً من مكان واحد مزعج ، يمكن لقاعدة البيانات بأكملها أن تتوقف عن العمل (لن يكون هناك سوى حد اتصال للمستخدم الخارق) ، هنا تحتاج إلى تجنب الطلبات البطيئة ، وتقليل عدد الطلبات على سبيل المثال ، ابدأ عمليات الترحيل أثناء الحمل الأدنى ، وافصل المكونات المهمة إلى خدمات مختلفة باستخدام قواعد البيانات الخاصة بها.
  4. هناك العديد من عمليات الترحيل في معاملة واحدة - فكلما زاد عدد العمليات في معاملة واحدة ، كلما تم تعليق القفل الثقيل لفترة أطول ، وبالتالي من الأفضل فصل العمليات الثقيلة ، أو عدم وجود ALTER TABLE VALIDATE CONSTRAINT أو ترحيل البيانات في معاملة واحدة باستخدام قفل كثيف.

المهلات


يحتوي lock_timeout على إعدادات مثل lock_timeout و statement_timeout ، والتي يمكن أن تحمي بداية عمليات الترحيل ، سواء من الترحيل المكتوب بشكل سيء أو من الظروف السيئة التي يمكن فيها تشغيل الترحيل. يمكن تثبيتها عالميًا وللاتصال الحالي.


SET lock_timeout TO '2s' التوقف عن العمل عند انتظار الطلبات / المعاملات البطيئة قبل الترحيل: https://www.postgresql.org/docs/current/static/runtime-config-client.html#GUC-LOCK-TIMEOUT .


سيتجنب SET statement_timeout TO '2s' التوقف عن العمل عند بدء الترحيل المكثف بقفل ثقيل: https://www.postgresql.org/docs/current/static/runtime-config-client.html#GUC-STATEMENT-TIMEOUT .


جمود


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


تخزين التسجيلات


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


التحكم في التزامن المتعدد (MVCC)


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


مثال جانغو


لدينا الآن فكرة عما قد يعتمد عليه وقت التوقف وكيفية تجنبه ، ولكن قبل تطبيق المعرفة ، يمكنك إلقاء نظرة على ما يقدمه django من الصندوق ( https://github.com/django/django/blob/2.1.2/django /db/backends/base/schema.py و https://github.com/django/django/blob/2.1.2/django/db/backends/postgresql/schema.py ):


العملية
1CREATE SEQUENCE
2DROP SEQUENCE
3CREATE TABLE
4DROP TABLE
5ALTER TABLE RENAME TO
6ALTER TABLE SET TABLESPACE
7ALTER TABLE ADD COLUMN [SET DEFAULT] [SET NOT NULL] [PRIMARY KEY] [UNIQUE]
8ALTER TABLE ALTER COLUMN [TYPE] [SET NOT NULL|DROP NOT NULL] [SET DEFAULT|DROP DEFAULT]
9ALTER TABLE DROP COLUMN
10ALTER TABLE RENAME COLUMN
11ALTER TABLE ADD CONSTRAINT CHECK
12ALTER TABLE DROP CONSTRAINT CHECK
13ALTER TABLE ADD CONSTRAINT FOREIGN KEY
14ALTER TABLE DROP CONSTRAINT FOREIGN KEY
15ALTER TABLE ADD CONSTRAINT PRIMARY KEY
16ALTER TABLE DROP CONSTRAINT PRIMARY KEY
17ALTER TABLE ADD CONSTRAINT UNIQUE
18ALTER TABLE DROP CONSTRAINT UNIQUE
19CREATE INDEX
20DROP INDEX

يغطي Django احتياجات الهجرة الخاصة بي بشكل جيد للغاية ، والآن يمكننا مناقشة العمليات الآمنة والخطيرة لعمليات الترحيل دون توقف عن العمل بمعرفتنا.


سنطلق على عمليات الترحيل الأكثر أمانًا من خلال SHARE UPDATE EXCLUSIVE locking or ACCESS EXCLUSIVE ، والتي تعمل على الفور.
سنطلق على عمليات الترحيل الخطيرة باستخدام أقفال SHARE و ACCESS EXCLUSIVE ، والتي تستغرق وقتًا طويلاً.


سأترك رابطًا مفيدًا للوثائق مقدمًا مع أمثلة رائعة.


إنشاء وحذف جدول


CREATE SEQUENCE ، DROP SEQUENCE ، CREATE TABLE ، DROP TABLE يمكن تسميته آمنًا ، لأن منطق الأعمال إما لم يعد يعمل مع الجدول الذي تم ترحيله بعد الآن ، فإن سلوك حذف جدول باستخدام FOREIGN KEY سيكون بعد ذلك بقليل.


عمليات ورقة العمل المدعومة بشكل كبير


ALTER TABLE RENAME TO - لا يمكنني تسميته بأمان ، لأنه من الصعب كتابة منطق يعمل مع مثل هذا الجدول قبل وبعد الترحيل.


ALTER TABLE SET TABLESPACE - غير آمن ، لأنه يحرك اللوحة جسديًا ، ويمكن أن يستغرق ذلك وقتًا طويلاً على حجم كبير.


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


إنشاء وحذف الأعمدة


ALTER TABLE ADD COLUMN ، ALTER TABLE DROP COLUMN - يمكن أن يطلق عليه آمنًا (الإنشاء بدون الافتراضي / ليس فارغًا / مفتاح أساسي / فريد) ، لأن منطق الأعمال إما لم يعد يعمل مع عمود تم ترحيله بعد الآن ، فإن سلوك حذف عمود باستخدام مفتاح FOREIGN ، ستأتي الثوابت والفهارس الأخرى لاحقًا.


ALTER TABLE ADD COLUMN SET DEFAULT ، ALTER TABLE ADD COLUMN SET NOT NULL ، ALTER TABLE ADD COLUMN PRIMARY KEY ، ALTER TABLE ADD COLUMN UNIQUE - عمليات غير آمنة ، لأنها تضيف عمودًا ، دون تحرير الأقفال ، تحديث البيانات مع الإعدادات الافتراضية أو إنشاء بنى كبدائل ، أعمدة قابلة للإلغاء والمزيد من التغيير.


من الجدير بالذكر أن SET DEFAULT الأسرع في postgres 11 ، يمكن اعتباره آمنًا ، لكنه لا يصبح مفيدًا جدًا في django ، لأن django يستخدم SET DEFAULT فقط لملء العمود ثم يجعل DROP DEFAULT ، وفي الفاصل الزمني بين الترحيل وتحديث الأجهزة مع منطق الأعمال ، يمكن إنشاء السجلات التي يكون فيها الغياب الافتراضي ، أي ، ثم كل نفس ، القيام بترحيل البيانات.


العمليات المدعومة بشكل كبير على ورقة عمل


ALTER TABLE RENAME COLUMN - لا يمكنني أيضًا تسميته بأمان ، لأنه من الصعب كتابة منطق يعمل مع مثل هذا العمود قبل وبعد الترحيل. بدلاً من ذلك ، لن تكون هذه العملية متكررة أيضًا ، حيث يمكن اقتراح بديل لإنشاء عمود جديد ونسخ البيانات إليه.


تغيير العمود


ALTER TABLE ALTER COLUMN TYPE - يمكن أن تكون العملية خطيرة وآمنة. آمن إذا قام postgres بتغيير المخطط فقط ، وتم تخزين البيانات بالفعل بالتنسيق المطلوب ولم تعد هناك حاجة إلى عمليات فحص إضافية للنوع ، على سبيل المثال:


  • اكتب التغيير من varchar(LESS) إلى varchar(MORE) ؛
  • اكتب التغيير من varchar(ANY) إلى text ؛
  • اكتب التغيير من numeric(LESS, SAME) إلى numeric(MORE, SAME) .

ALTER TABLE ALTER COLUMN SET NOT NULL أمر خطير لأنه يمر عبر البيانات الموجودة بالداخل ويتحقق من NULL ، لحسن الحظ يمكن استبدال هذا البناء بآخر CHECK IS NOT NULL . تجدر الإشارة إلى أن هذا الاستبدال سيؤدي إلى مخطط مختلف ، ولكن بخصائص متطابقة.


ALTER TABLE ALTER COLUMN DROP NOT NULL ، ALTER TABLE ALTER COLUMN SET DEFAULT ، ALTER TABLE ALTER COLUMN DROP DEFAULT - عمليات آمنة.


إنشاء وحذف الفهارس والثوابت


ALTER TABLE ADD CONSTRAINT CHECK و ALTER TABLE ADD CONSTRAINT FOREIGN KEY غير آمنين ، ولكن يمكن NOT VALID ثم ALTER TABLE VALIDATE CONSTRAINT .


ALTER TABLE ADD CONSTRAINT PRIMARY KEY و ALTER TABLE ADD CONSTRAINT UNIQUE غير آمنين ، لأنهم ينشئون فهرسًا فريدًا بالداخل ، ولكن يمكنك إنشاء فهرس فريد باسم CONCURRENTLY ، ثم إنشاء ثابت مطابق باستخدام فهرس جاهز عبر USING INDEX .


CREATE INDEX هي عملية غير آمنة ، ولكن يمكن إنشاء فهرس بشكل CONCURRENTLY .


ALTER TABLE DROP CONSTRAINT CHECK ، ALTER TABLE DROP CONSTRAINT FOREIGN KEY ، ALTER TABLE DROP CONSTRAINT PRIMARY KEY ، ALTER TABLE DROP CONSTRAINT UNIQUE ، DROP INDEX - عمليات آمنة.


من الجدير بالذكر أن ALTER TABLE ADD CONSTRAINT FOREIGN KEY و ALTER TABLE DROP CONSTRAINT FOREIGN KEY جدولين في وقت واحد.


تطبيق المعرفة في جانغو


لدى Django عملية في عمليات الترحيل لتنفيذ أي SQL: https://docs.djangoproject.com/en/2.1/ref/migration-operations/#django.db.migrations.operations.RunSQL . من خلاله ، يمكنك تعيين المهلات اللازمة وتطبيق عمليات بديلة لعمليات الترحيل ، مع الإشارة إلى state_operations - الترحيل الذي state_operations .


يعمل هذا بشكل جيد لرمزه ، على الرغم من أنه يتطلب كتابة إضافية ، ولكن يمكنك ترك العمل القذر على خلفية ديسيبل ، على سبيل المثال ، https://github.com/tbicr/django-pg-zero-downtime-migrations/blob/master/django_zero_downtime_migrations_postgres_backend/schema يجمع .py الممارسات الموضحة ويستبدل العمليات غير الآمنة بنظراء آمنين ، وسيعمل ذلك مع مكتبات الطرف الثالث.


في النهاية


سمحت لي هذه الممارسات بالحصول على مخطط متطابق تم إنشاؤه بواسطة django خارج منطقة الجزاء ، باستثناء استبدال CHECK IS NOT NULL بدلاً من NOT NULL وبعض أسماء البناء (على سبيل المثال ، لـ ALTER TABLE ADD COLUMN UNIQUE وبديل). قد يكون هناك مفاضلة أخرى تتمثل في عدم وجود معاملات لعمليات ترحيل بديلة ، خاصة حيث تظهر CREATE INDEX CONCURRENTLY و ALTER TABLE VALIDATE CONSTRAINT .


إذا لم تتجاوز postgres ، فقد يكون هناك العديد من الخيارات لتغيير مخطط البيانات ، ويمكن أن تتنوع في تركيبة في ظل ظروف محددة:


  • باستخدام jsonb كحل schamaless
  • الفرصة للذهاب إلى التوقف
  • شرط القيام بعمليات الترحيل بدون وقت تعطل

على أي حال ، آمل أن تكون المادة مفيدة إما لزيادة وقت التشغيل أو لتوسيع الوعي.

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


All Articles