RE:NODE

ბაზები13 წუთის საკითხავი

სქემის მიგრაციები გათიშვის გარეშე: expand და contract

დამატება უსაფრთხოა, გადარქმევა კი ყველაფერს არღვევს. expand and contract პატერნი, Postgres-ის ლოკების ქცევა, ჩანაწერების batch-ებით შევსება და რიგი, რომელიც მუშაობს.

განახლებულია

0 მკითხველი

ნებისმიერი deploy-ის დროს არსებობს ფანჯარა, როცა ძველი და ახალი კოდი ერთსა და იმავე ბაზაზე მუშაობს. rolling deploy-ზე ეს ფანჯარა წუთებია. ერთ პროცესზე - ის რამდენიმე წამია იმ მომენტს შორის, როცა ძველმა პროცესმა ბოლო მოთხოვნა დაამთავრა და როცა ახალმა პირველი მიიღო. ასე თუ ისე ის არსებობს, და ყოველი უსაფრთხო მიგრაცია ამ ფაქტიდან გამომდინარეობს, ყოველი სახიფათო კი მას უგულებელყოფს.

ამ ფაქტიდან მიღებული წესი მოკლეა: მიგრაციამ ბაზა ისეთი უნდა დატოვოს, რომ ის უკვე გაშვებული კოდის ვერსიისთვის იყოს წაკითხვადი და ჩაწერადი. დამატება ამას აკმაყოფილებს. წაშლა აკმაყოფილებს მაშინ, როცა მას აღარაფერი მიუთითებს. გადარქმევა და ტიპის შეცვლა არასოდეს აკმაყოფილებს, ამიტომ ისინი დამატებებისა და წაშლების თანმიმდევრობად კეთდება. ამ პოსტის დანარჩენი ნაწილი სწორედ ეს თანმიმდევრობაა დეტალურად, ასევე ლოკების ქცევა, რომელიც „მყისიერ" ALTER TABLE-ს ოცდაათწამიან გათიშვად აქცევს, და როგორ შეავსო მილიონობით რიგი ისე, რომ ტრანზაქცია საათობით ღია არ გქონდეს.

გადაფარვის ფანჯარა და რას კრძალავს ის#

წარმოიდგინე ორი ვერსია, რომელიც ხუთი წუთის განმავლობაში გვერდიგვერდ მუშაობს. ძველი კოდი ირჩევს email-ს; ახალი ირჩევს email_address-ს. გადაფარვისას რომელი სვეტიც აკლია, მოთხოვნების ნახევარს აგდებს. ეს არის მთელი მარცხის რეჟიმი და ის განმარტავს ყოველ ამ აკრძალვას:

  • გადარქმევა არა. ძველმა კოდმა ახალი სახელი არ იცის.
  • ტიპის შეცვლა, რომელიც მნიშვნელობას ცვლის, არა. ძველი კოდი ძველ ტიპს წერს.
  • არაფრის წაშლა, რასაც ძველი კოდი ჯერ კიდევ ირჩევს - იმ სვეტების ჩათვლით, რომლებსაც არასოდეს იყენებს, თუ ის SELECT *-ს ატარებს მკაცრ row mapper-ში.
  • `NOT NULL` სვეტის დამატება default-ის გარეშე არა, რადგან ძველი კოდი რიგებს მის გარეშე ამატებს.
  • არანაირი შეზღუდვა, რომელსაც არსებული რიგები ან ძველი ჩაწერები დაარღვევდა.

და ორი, რომლებიც კოდზე კი არა, თავად მიგრაციის მექანიკაზეა:

  • არანაირი ოპერაცია, რომელიც ძლიერ ლოკს დიდხანს იჭერს, რადგან ყველაფერი მის უკან ჩადგება რიგში.
  • არანაირი ერთი ტრანზაქცია, რომელიც ყველა რიგს ეხება, იმის გამო, რასაც ის რეპლიკაციას, ლოკებს და bloat-ს უშვება.
old code unaffectednew writes already correctverify zero mismatchesa release laterDeploy 1add new columnDeploy 2write bothBackfillin batchesDeploy 3read new onlyDeploy 4drop old
ერთი გადარქმევა, ხუთი უსაფრთხო ნაბიჯი

Expand, migrate, contract#

პატერნს სამი ფაზა აქვს და ჩვეულებრივ ოთხი ან ხუთი deploy. სვეტის გადარქმევისთვის ეს ბევრ ცერემონიას ჰგავს, და ასეც არის - სანამ პირველად არ გააკეთებ ამას ერთ ნაბიჯში საღამოს 6 საათზე.

  1. Expand. დაამატე ახალი სვეტი, ცხრილი ან ინდექსი. Nullable, default-ით, თუ ის სჭირდება. ძველი კოდი მას უგულებელყოფს, რადგან არ იცის, რომ არსებობს; ახალ კოდს შეუძლია გამოიყენოს.
  2. Dual write. გაუშვი კოდი, რომელიც წერს ძველ და ახალ ფორმას ორივეს და კვლავ ძველს კითხულობს. არაფერი არ არის დამოკიდებული იმაზე, რომ ახალი მონაცემები სრულია.
  3. Backfill. გადაწერე არსებული რიგები batch-ებით, სანამ ყველაფერი მუშაობს. ახალი რიგები უკვე სწორია მე-2 ნაბიჯის წყალობით, ამიტომ backfill-ს მხოლოდ ისტორიასთან აქვს საქმე.
  4. წაკითხვის გადართვა. გაუშვი კოდი, რომელიც ახალ ფორმას კითხულობს. კვლავ ორივეს წერე. ეს ნაბიჯია, რომლის უკან დაბრუნებაც იაფია, სწორედ ამიტომ არის ის ცალკე deploy.
  5. Contract. მოგვიანებით release-ში, როცა დარწმუნებული ხარ, შეწყვიტე ძველი ფორმის წერა და წაშალე ის.

უფსკრული მე-4 და მე-5 ნაბიჯებს შორის მთელი სავარჯიშოს აზრია. თუ ახალი სვეტის კითხვა ცუდად წავა, ერთ deploy-ს აბრუნებ და ძველი სვეტი ისევ იქაა, ისევ აქტუალური და სწორი. ორი ნაბიჯი რომ შეაერთო, უკან დაბრუნებას restore დასჭირდება.

გადარქმევა, deploy-ებად#

კლასიკური მუშა მაგალითი. users.email ხდება users.email_address, ორი მილიონი რიგის მქონე ცხრილზე, გათიშვის გარეშე.

sql
-- Deploy 1, expand. Instant on PostgreSQL 11 and later:-- a constant default is stored in the catalogue, not written to every row.ALTER TABLE users ADD COLUMN email_address text;
javascript
// Deploy 2, dual write. Every path that writes an email writes both.await db.query(  `UPDATE users SET email = $1, email_address = $1 WHERE id = $2`,  [email, id],);
sql
-- Deploy 3, backfill, run outside the deploy in batches (see below),-- then check it actually finished:SELECT count(*) FROM usersWHERE email IS DISTINCT FROM email_address;-- expect 0
sql
-- Deploy 4, switch reads. Code change only, no DDL.-- Deploy 5, contract, in a later release:ALTER TABLE users DROP COLUMN email;

ხუთი ნაბიჯი, რომელთაგან ოთხი ჩვეულებრივი deploy-ია. ერთადერთი ნამდვილად შეუქცევადი ბოლოა, და მას შემდეგ ახალი სვეტი უკვე ერთი კვირაა production-შია. PostgreSQL-ში DROP COLUMN კატალოგის ცვლილებაა და მყისვე ბრუნდება, თუმცა ადგილს არ ათავისუფლებს, სანამ რიგები არ გადაიწერება - Postgres vacuum და bloat ხსნის, რატომ არ მცირდება ცხრილი.

იგივე ფორმა ფარავს ერთი სვეტის ორად გაყოფას, ტიპის შეცვლას, ველის სხვა ცხრილში გადატანას და საძიებო ცხრილის გამოყოფას. მხოლოდ შუა ნაწილი განსხვავდება.

ლოკები: რას იღებს რეალურად თითოეული ოპერაცია#

აი რა იჭერს გამოცდილ ადამიანებსაც. ALTER TABLE ADD COLUMN მყისიერია, მაგრამ მაინც იღებს ACCESS EXCLUSIVE ლოკს, რომელიც ყველაფერთან კონფლიქტობს, უბრალო SELECT-ის ჩათვლით. თუ ის ამ ლოკს მაშინვე ვერ იღებს - რადგან ანგარიში ორმოცი წამია გრძელდება, ან უქმი ტრანზაქცია კითხვის ლოკს ინახავს - ის ელოდება. და სანამ ელოდება, ყველა მოთხოვნა, რომელიც მის შემდეგ მოდის, მის უკან ჩადგება რიგში, რადგან ლოკის მოთხოვნები დალაგებულია. ერთმილიწამიანმა მიგრაციამ შეიძლება ამიტომ შენი საიტი იმდენ ხანს გააჩეროს, რამდენიც მის წინ ყველაზე ნელ მოთხოვნას სჭირდება.

გამოსავალი ორი ხაზია და ისინი ყოველი მიგრაციის თავში უნდა იყოს:

sql
SET lock_timeout = '3s';SET statement_timeout = '30s';

ახლა მიგრაცია ან სამ წამში იღებს ლოკს, ან სუფთად ჩერდება, არაფრის წაღებით, და შენ ხელახლა ცდი. მიგრაცია, რომელიც ჩერდება და იმეორებს, არაფერია. მიგრაცია, რომელიც ელოდება, ინციდენტია.

ოპერაციალოკიპრაქტიკული ფასი
ADD COLUMN (default-ის გარეშე, ან მუდმივი default)ACCESS EXCLUSIVE, მყისიერიუსაფრთხოა lock timeout-ით
ADD COLUMN არამუდმივი default-ითACCESS EXCLUSIVE, სრული გადაწერაერიდე: დაამატე nullable, შემდეგ backfill
DROP COLUMNACCESS EXCLUSIVE, მყისიერიუსაფრთხოა, ადგილი მოგვიანებით თავისუფლდება
RENAME COLUMN / RENAME TABLEACCESS EXCLUSIVE, მყისიერიბაზისთვის უსაფრთხოა, ძველი კოდისთვის - საბედისწერო
ALTER COLUMN TYPEACCESS EXCLUSIVE, სრული გადაწერაvarchar-ის გაფართოება ან text-ზე გადასვლა უფასოა
SET NOT NULLACCESS EXCLUSIVE, სრული სკანიგამოიყენე CHECK ... NOT VALID გზა ქვემოთ
CREATE INDEXბლოკავს ჩაწერებს მთელი აგების განმავლობაშიგამოიყენე CONCURRENTLY
CREATE INDEX CONCURRENTLYუშვებს წაკითხვებს და ჩაწერებსორი სკანი, უფრო ნელი, ტრანზაქციაში ვერ გაეშვება
ADD FOREIGN KEYორივე ცხრილს ბლოკავს სკანირებისასგამოიყენე NOT VALID, შემდეგ VALIDATE
VALIDATE CONSTRAINTუშვებს წაკითხვებს და ჩაწერებსამიტომ არსებობს NOT VALID

კიდევ ორი ჩვევა, რაც ღირს. სადაც შეგიძლია, თითო მიგრაცია ერთ ცხრილზე შეინახე, რომ მარცხი პატარა იყოს. და არასოდეს დაუშვა მიგრაცია გრძელ ტრანზაქციაში, რომელიც აპლიკაციის სამუშაოსაც აკეთებს - მიგრაცია, რომელიც ACCESS EXCLUSIVE-ს იმავე ტრანზაქციის ნელ SELECT-ს ელოდებისას ინახავს, ორივეს ყველაზე ცუდია.

Backfill გრძელი ტრანზაქციის გარეშე#

ერთი UPDATE users SET email_address = email ორ მილიონ რიგზე არის ერთი ტრანზაქცია, რომელიც ორ მილიონ ახალ რიგის ვერსიას წერს, გიგაბაიტობით write-ahead log-ს ქმნის, ლოკებს მთელი ხანგრძლივობით ინახავს, VACUUM-ს ხელს უშლის რამის გასუფთავებაში და შენს რეპლიკებს უკან სტოვებს. თუ ის 90 პროცენტზე ჩავარდა, ყველაფერი უკან ბრუნდება და თავიდან იწყებ.

ამის ნაცვლად batch-ებად წაიყვანე. გაიარე primary key-ზე, დააკომიტე ყოველი batch და მოკლედ შეაჩერე, რომ ჩვეულებრივ ტრაფიკს და autovacuum-ს რიგი ერგოს.

sql
-- One batch. Run in a loop from a script, not from psql by hand.WITH batch AS (  SELECT id FROM users  WHERE email_address IS NULL AND email IS NOT NULL  ORDER BY id  LIMIT 5000  FOR UPDATE SKIP LOCKED)UPDATE users uSET email_address = u.emailFROM batch bWHERE u.id = b.id;
bash
# The loop, with a pause between batches$ while true; do    rows=$(psql -qtAX -f backfill_batch.sql)    [ "$rows" = "UPDATE 0" ] && break    sleep 0.2  done

რაც backfill-ს აკეთებს მოსაწყენს საინტერესოს ნაცვლად:

  • Batch-ის ზომა 1 000-დან 10 000 რიგამდე. საკმარისად დიდი ეფექტურობისთვის, საკმარისად პატარა, რომ თითოეული ტრანზაქცია მილიწამები იყოს.
  • გახადე განახლებადი. WHERE პირობამ უნდა აღწეროს ჯერ არშესრულებული სამუშაო, რომ სკრიპტის გაჩერება და ხელახლა გაშვება უფასო იყოს.
  • გახადე იდემპოტენტური. ორჯერ გაშვება უვნებელი უნდა იყოს.
  • `FOR UPDATE SKIP LOCKED` ნიშნავს, რომ რიგი, რომელსაც ახლა აპლიკაცია წერს, გამოტოვებულია და არ ელოდება, და მოგვიანებით გავლაზე აიღება.
  • უყურე რეპლიკაციის ჩამორჩენას და დისკს მუშაობისას. თუ ჩამორჩენა იზრდება, გაზარდე პაუზა და არა batch-ის ზომა.
  • შემდეგ `ANALYZE` გაუკეთე ცხრილს, რომ planner-მა ახალი სვეტის სტატისტიკა იცოდეს, სანამ შენი ახალი მოთხოვნები მას გამოიყენებს.

დიდ ცხრილზე backfill deploy-ის დროს გასაშვები არ არის. დაიწყე, დაე საათი ან დღე იმუშაოს და შეამოწმე დარჩენილი რიგების რაოდენობა, სანამ მასზე დამოკიდებულ კოდს გაუშვებ.

ინდექსები და შეზღუდვები, concurrent ვარიანტები#

ახალი ინდექსები ახალი სვეტების შემდეგ ყველაზე ხშირი მიგრაციაა, და ნაგულისხმევი live სისტემისთვის არასწორია. CREATE INDEX ლოკს იჭერს, რომელიც მთელი აგების განმავლობაში ჩაწერებს ბლოკავს, რაც დიდ ცხრილზე წუთებია.

sql
-- Outside a transaction. Most migration tools wrap statements in one,-- so this usually needs an explicit escape hatch.CREATE INDEX CONCURRENTLY idx_users_email_address ON users (email_address);-- If it fails, it leaves an invalid index behind. Find it:SELECT indexrelid::regclass AS index, indisvalidFROM pg_index WHERE NOT indisvalid;-- Drop it and try again:DROP INDEX CONCURRENTLY idx_users_email_address;

CONCURRENTLY ცხრილზე ორ გავლას აკეთებს და სულ უფრო ნელია, რაც გაცვლაა, რომელიც გინდა. ის ტრანზაქციის ბლოკში ვერ გაეშვება, ამიტომ Alembic-ში ის autocommit_block-შია, Django-ში - AddIndexConcurrently atomic = False-ით, ხოლო Rails-ში სჭირდება disable_ddl_transaction! algorithm: :concurrently-თან ერთად. ამის არასწორად გაკეთება ყველაზე ხშირი მიზეზია, რის გამოც მიგრაციის ხელსაწყო ინსტრუქციას პირდაპირ უარყოფს. რომელი ინდექსი შექმნა პირველ რიგში - ეს Postgres ინდექსები ახსნილი-ია, ხოლო დაგეხმარა თუ არა - EXPLAIN ANALYZE.

შეზღუდვებს იგივე გამოსავალი აქვს. NOT NULL-ის ან foreign key-ის დამატება ჩვეულებრივ მთელ ცხრილს სკანირებს ძლიერი ლოკის ქვეშ; ორ ნაბიჯად გაყოფა ნიშნავს, რომ სკანი ხდება ლოკის ქვეშ, რომელიც წაკითხვებსა და ჩაწერებს უშვებს:

sql
-- Step 1: brief strong lock, no scanALTER TABLE users  ADD CONSTRAINT users_email_address_not_null  CHECK (email_address IS NOT NULL) NOT VALID;-- Step 2: the scan, without blocking trafficALTER TABLE users VALIDATE CONSTRAINT users_email_address_not_null;-- Step 3: PostgreSQL 12 and later can now use that constraint-- to set NOT NULL without scanning againALTER TABLE users ALTER COLUMN email_address SET NOT NULL;ALTER TABLE users DROP CONSTRAINT users_email_address_not_null;

იგივე NOT VALID, შემდეგ VALIDATE წყვილი foreign key-ებზეც მუშაობს, და ის არის განსხვავება deploy-სა და გათიშვას შორის ნებისმიერ ცხრილზე, რომელსაც რამდენიმე მილიონზე მეტი რიგი აქვს.

MongoDB: იგივე პატერნი DDL-ის გარეშე#

დიდი ცდუნებაა იფიქრო, რომ უსქემო ბაზა ამას აქრობს. ის ALTER TABLE-ს აქრობს და არა გადაფარვის ფანჯარას. დოკუმენტის ორი ფორმა მაინც თანაარსებობს და ძველ კოდს მაინც უნდა გაუმკლავდეს.

javascript
// Expand: nothing to do, the field simply starts appearing.// Dual write:db.users.updateOne({ _id: id }, { $set: { email: e, emailAddress: e } })// Backfill in batches, resumable by _id:db.users.updateMany(  { emailAddress: { $exists: false }, email: { $exists: true } },  [{ $set: { emailAddress: "$email" } }],)// Contract, later:db.users.updateMany({}, { $unset: { email: "" } })

სამი MongoDB-სპეციფიკური შენიშვნა. $rename update ოპერატორი არსებობს, და მისი ერთ გავლაში გამოყენება ზუსტად ის შეცდომაა, რომელზეც მთელი ეს პოსტია - ის ძველ კოდს ტოვებს ველზე, რომელიც წავიდა. ინდექსის აგებები ვერსია 4.2-დან ერთი აგების ტიპს იყენებს, რომელიც ექსკლუზიურ ლოკს მხოლოდ დასაწყისსა და ბოლოში ინახავს მოკლედ, ამიტომ ინდექსის შექმნა live კოლექციაზე გაცილებით ნაკლებ საშიშია, ვიდრე ადრე, მაგრამ მაინც ჭამს მეხსიერებას და I/O-ს, ამიტომ გააკეთე ის პიკის გარეთ. და თუ სქემის ვალიდაციას იყენებ, ჯერ გააძლიერე ის validationLevel: "moderate"-ით, რომ ახალი წესი იმ დოკუმენტებზე გავრცელდეს, რომლებსაც ეხები, და ყველა ძველ დოკუმენტს ერთდროულად არ უარყოფდეს. MongoDB ინდექსები და სქემის დიზაინი უფრო მეტს ამბობს ფორმის გადაწყვეტილებებზე მის უკან, ხოლო PostgreSQL თუ MongoDB - იმაზე, რომელი ორიდან უნდა გქონოდა.

მისი გამეორებადად გახდომა#

პატერნი იმდენად კარგია, რამდენადაც მის გარშემო პროცესი.

  • გამოიყენე მიგრაციის ხელსაწყო, ერთი ფაილი ცვლილებაზე, ვერსიების კონტროლში. Alembic, Django migrations, Flyway, Liquibase, golang-migrate, რასაც შენი სტეკი იყენებს. ხელით აკრეფილი SQL production-ში მიგრაცია არ არის, ის ანეკდოტია.
  • მხოლოდ წინ. დაწერე down მიგრაცია, თუ შენი ხელსაწყო ამას ითხოვს, მაგრამ მისი გამოყენება ნუ გეგმავ. ცუდი მიგრაციიდან აღდგენა ნიშნავს კოდის უკან დაბრუნებას და ახალი მიგრაციის წინ გაშვებას, რადგან down მიგრაცია, რომელიც სვეტს წაშლის, წაშლის მას შემდეგ ჩაწერილ მონაცემებს.
  • down მიგრაციას ნუ ენდობი. ის, რომელიც სვეტს წაშლის, შლის ყველა მნიშვნელობას, რომელიც მასში deploy-ის შემდეგ ჩაიწერა, ზუსტად იმ მონაცემებს, რომელთა გადარჩენასაც უკან დაბრუნებით ცდილობდი.
  • ერთდროულად ერთი მიგრაციის runner. თუ ორი ინსტანცია ერთდროულად deploy-დება, ორ runner-ს შეიძლება ერთმანეთი დაეწიოს. ზოგი ხელსაწყო ლოკს ავტომატურად იღებს; თუ შენი არა, თავად აიღე SELECT pg_advisory_lock(...)-ით გაშვების გარშემო.
  • გაიმეორე production-ის ასლზე. აღადგინე ახალი dump სატესტო ბაზაში, გაუშვი მიგრაცია და გაზომე დრო. განცხადება, რომელიც შენი ლეპტოპის 5 000 რიგზე 200 ms გრძელდება, ნამდვილ ცხრილზე ოთხ წუთს შეიძლება გაგრძელდეს, და ეს რიცხვია, რომელიც გჭირდება, სანამ რამეს დაგეგმავ. Staging და production ერთ ანგარიშზე ამ ასლის შენახვის იაფი გზაა, ხოლო pg_dump და pg_restore ფარავს მონაცემების იქ მიტანას.
  • გააკეთე backup contract ნაბიჯამდე. Expand შექცევადია, წაშლა - არა. RE:NODE-ზე backup slot-ები ყველა გეგმას მოჰყვება, შეგიძლია მოთხოვნით აიღო ან Schedules ჩანართზე cron გამოსახულებით დააყენო, და აღდგენა ღილაკია და არა მხარდაჭერასთან საუბარი - მაგრამ backup, რომელიც არავის აღუდგენია, ჰიპოთეზაა, ამიტომ დაამტკიცე, რომ მუშაობს, იმ დღეს, როცა არაფერი არ არის გაფუჭებული.
  • კოდი და მიგრაციები ცალკე ნაბიჯებზე გაუშვი. აქ არსებულ აპლიკაციის გეგმებზე deploy-on-push მხოლოდ ისეთ სერვერს რესტარტავს, რომელიც უკვე გაშვებული იყო, და თითო deploy-ზე ერთ რიგს ინახავს, ამიტომ ზემოთ მოცემული თანმიმდევრობა ერთი იდუმალი მოვლენის ნაცვლად ოთხ-ხუთ ნათლად გამოყოფილ მოვლენად ჩანს. იმავე პრობლემის აპლიკაციის მხარე - კავშირების დაცლა, health check-ები, ძველი და ახალი პროცესების გადაფარვა - არის zero-downtime deploy-ები პატარა სერვერზე.

ხუთი მოსაწყენი deploy იაფია, ვიდრე ერთი საინტერესო. არავის ჰქონია სინანული დამატებითი release-ის გამო.

FAQ#

რატომ ვერ გადავარქვა უბრალოდ სვეტს სახელი?

იმიტომ, რომ ამჟამად გაშვებულმა კოდმა ახალი სახელი არ იცის. თავად გადარქმევა მყისიერი და ბაზისთვის უსაფრთხოა; ტყდება გაშვებული აპლიკაცია, იმ წამებსა თუ წუთებში, როცა ძველი და ახალი ვერსიები ერთმანეთს ფარავს. დაამატე, ორივეს ჩაწერე, backfill გააკეთე, გადართე, წაშალე.

ჩემი მიგრაცია მყისიერია, მაშ რატომ გაჩერდა საიტი?

იმიტომ, რომ მას ლოკის დალოდება მოუწია. ALTER TABLE იღებს ლოკს, რომელიც ყველაფერთან კონფლიქტობს, და სანამ ელოდება, ყოველი ახალი მოთხოვნა მის უკან ჩადგება რიგში. დააყენე lock_timeout რამდენიმე წამზე მიგრაციის თავში, რომ ის ჩავარდეს და გამეორდეს და არა ტრაფიკი დაბლოკოს.

როგორ დავამატო NOT NULL სვეტი დიდ ცხრილს?

დაამატე ის nullable, გაუშვი კოდი, რომელიც მას ყოველთვის ადგენს, ძველი რიგები batch-ებით შეავსე, შემდეგ დაამატე CHECK (col IS NOT NULL) NOT VALID შეზღუდვა, გაატარე ვალიდაცია და დააყენე NOT NULL. PostgreSQL 11 და შემდგომზე სვეტი მუდმივი default-ით პირდაპირ შეიძლება დაემატოს ცხრილის გადაწერის გარეშე.

შემიძლია მიგრაციის გაშვება პიკური ტრაფიკის დროს?

პატარებისთვის დიახ, lock timeout-ით. ყველაფერი, რაც ცხრილს გადაწერს, ინდექსს ააგებს ან მილიონობით რიგს შეავსებს, უნდა გაეშვას, როცა ბაზა წყნარია - არა იმიტომ, რომ ჩავარდება, არამედ იმიტომ, რომ ის შენს მომხმარებლებთან ერთ CPU-სა და დისკს ეჯიბრება.

მჭირდება თუ არა ეს MongoDB-ზეც?

დიახ. ALTER TABLE არ არსებობს, მაგრამ გადაფარვის ფანჯარა იდენტურია: დოკუმენტის ორი ფორმა ერთდროულად არსებობს და ძველმა კოდმა ორივე უნდა აიტანოს. განსხვავება ისაა, რომ MongoDB დისციპლინის გამოტოვების საშუალებას ჩუმად გაძლევს, და ამას მოგვიანებით იგებ.

რა ვქნა, თუ მიგრაცია ნახევრად დასრულდა?

გაჩერდი, შეამოწმე, რა მდგომარეობაშია ბაზა, და წინ წადი. გახადე მიგრაცია განახლებადი და იდემპოტენტური, რომ ხელახლა გაეშვას, და ამჯობინე ახალი მიგრაციით წინ გასწორება down მიგრაციის გაშვებას, რომელიც ხალისით წაშლის იმ დროს ჩაწერილ მონაცემებს.


კომენტარები

სრულიად ანონიმურად: ანგარიშის, ელფოსტის და cookie-ის გარეშე. ინახება მხოლოდ სახელი, ტექსტი და დრო - სხვა არაფერი. ბმულების რაოდენობა ლიმიტირებულია.

0/2000