একটি বড় টেবিল একটি নতুন কলাম পপুলেট করার সেরা উপায়?


33

আমাদের পোস্টগ্রিসে একটি 2.2 জিবি টেবিল রয়েছে এতে 7,801,611 সারি রয়েছে। আমরা এটিতে একটি ইউইডি / গাইড গাইড কলাম যুক্ত করছি এবং আমি ভাবছি যে কলামটি জনপ্রিয় করার সর্বোত্তম উপায়টি (আমরা NOT NULLএটিতে একটি সীমাবদ্ধতা যুক্ত করতে চাই )।

যদি আমি পোস্টগ্র্রেসকে সঠিকভাবে বুঝতে পারি তবে একটি আপডেট প্রযুক্তিগতভাবে মুছে ফেলা এবং sertোকানো হয় তাই এটি মূলত পুরো ২.২ জিবি টেবিলটি পুনর্নির্মাণ করছে। এছাড়াও আমাদের একটি দাস চলছে তাই আমরা পিছিয়ে থাকতে চাই না।

কোনও স্ক্রিপ্ট লেখার চেয়ে আরও ভাল উপায় আছে যা সময়ের সাথে ধীরে ধীরে এটিকে জনপ্রিয় করে তোলে?


2
আপনি কি ইতিমধ্যে একটি চালনা করেছেন ALTER TABLE .. ADD COLUMN ...বা সেই অংশটিরও উত্তর দেওয়া হবে?
ypercubeᵀᴹ

কেবল পরিকল্পনার পর্যায়ে এখনও কোনও টেবিল পরিবর্তন চালানো হয়নি। আমি কলামটি যুক্ত করে, পপুলেশন করে, তারপরে সীমাবদ্ধতা বা সূচক যুক্ত করে এটি করেছি। যাইহোক, এই টেবিলটি উল্লেখযোগ্যভাবে বড় এবং আমি লোড, লকিং, প্রতিলিপি ইত্যাদির বিষয়ে উদ্বিগ্ন ...
কলিন পিটারস

উত্তর:


45

এটি আপনার প্রয়োজনীয়তার বিশদটির উপর নির্ভর করে।

যদি আপনি যথেষ্ট মুক্ত স্থান (অন্তত 110% pg_size_pretty((pg_total_relation_size(tbl))ডিস্কে) এবং একটি সামর্থ কিছু সময়ের জন্য ভাগ লক এবং একটি খুব স্বল্প সময়ের জন্য একচেটিয়া লক , তারপর তৈরি নতুন টেবিল সহ uuidকলাম ব্যবহার করে CREATE TABLE AS। কেন?

নীচের কোডটি অতিরিক্ত uuid-ossমডিউল থেকে একটি ফাংশন ব্যবহার করে ।

  • SHAREমোডে সমবর্তী পরিবর্তনগুলির বিরুদ্ধে টেবিলটি লক করুন (এখনও সমবর্তী পাঠগুলি মঞ্জুরি দেয়)। টেবিলে লেখার চেষ্টা অপেক্ষা করবে এবং শেষ পর্যন্ত ব্যর্থ হবে। নিচে দেখ.

  • ফ্লাইতে নতুন কলামটি বসানোর সময় পুরো টেবিলটি অনুলিপি করুন - সম্ভবত সেখানে থাকা অবস্থায় সারিগুলি অনুকূলভাবে অর্ডার করুন।
    আপনি যদি সারিগুলি পুনরায় অর্ডার করতে চলেছেন তবে আপনি work_memযতটা সামর্থ্য করতে পারেন তত বেশি সেট করার বিষয়টি নিশ্চিত করুন (কেবল আপনার সেশনের জন্য, বিশ্বব্যাপী নয়)।

  • তারপরে নতুন সারণীতে সীমাবদ্ধতা, বিদেশী কী, সূচকগুলি, ট্রিগার ইত্যাদি যুক্ত করুন। কোনও সারণীর বড় অংশ আপডেট করার সময় পুনরাবৃত্তভাবে সারি যুক্ত করার চেয়ে স্ক্র্যাচ থেকে সূচকগুলি তৈরি করা আরও দ্রুত।

  • নতুন টেবিলটি প্রস্তুত হয়ে গেলে, পুরানোটি ফেলে দিন এবং একটি ড্রপ-ইন প্রতিস্থাপনের জন্য নতুনটির নামকরণ করুন। কেবলমাত্র এই শেষ পদক্ষেপটি বাকী লেনদেনের জন্য পুরানো টেবিলের একচেটিয়া লক অর্জন করে - যা এখন খুব ছোট হওয়া উচিত।
    এটিরও দরকার যে আপনি টেবিলের ধরণের উপর নির্ভর করে যে কোনও বস্তু মুছে ফেলুন (স্বাক্ষরে টেবিলের ধরণ ব্যবহার করে দেখুন, ফাংশন ...) এবং সেগুলি পরে পুনরায় তৈরি করুন।

  • অসম্পূর্ণ অবস্থা এড়াতে এটি একটি লেনদেনে সমস্ত করুন।

BEGIN;
LOCK TABLE tbl IN SHARE MODE;

SET LOCAL work_mem = '???? MB';  -- just for this transaction

CREATE TABLE tbl_new AS 
SELECT uuid_generate_v1() AS tbl_uuid, <list of all columns in order>
FROM   tbl
ORDER  BY ??;  -- optionally order rows favorably while being at it.

ALTER TABLE tbl_new
   ALTER COLUMN tbl_uuid SET NOT NULL
 , ALTER COLUMN tbl_uuid SET DEFAULT uuid_generate_v1()
 , ADD CONSTRAINT tbl_uuid_uni UNIQUE(tbl_uuid);

-- more constraints, indices, triggers?

DROP TABLE tbl;
ALTER TABLE tbl_new RENAME tbl;

-- recreate views etc. if any
COMMIT;

এটি দ্রুত হওয়া উচিত। জায়গায় আপডেট করার অন্য যে কোনও পদ্ধতিতে কেবল আরও ব্যয়বহুল ফ্যাশনে পুরো টেবিলটিও আবার লিখতে হবে। আপনি কেবলমাত্র সেই পথে যেতে পারেন যদি আপনার ডিস্কে পর্যাপ্ত ফাঁকা জায়গা না থাকে বা পুরো টেবিলটি লক করতে না পারে বা সমবর্তী লেখার চেষ্টার জন্য ত্রুটি উত্পন্ন করতে না পারে।

একযোগে লেখার কি হয়?

অন্যান্য লেনদেন (অন্যান্য সেশনে) আপনার লেনদেনটি লকটি নেওয়ার পরে একই টেবিলে INSERT/ UPDATE/ করার চেষ্টা করছে , লকটি প্রকাশ না হওয়া অবধি অপেক্ষা করবে বা একটি টাইমআউট শুরু হবে, যেটি প্রথমে আসে। তারা যে কোনও উপায়ে ব্যর্থ হবে , যেহেতু তারা যে টেবিলটিতে লেখার চেষ্টা করেছিল তারা তাদের নীচে থেকে মুছে ফেলা হয়েছে।DELETESHARE

নতুন টেবিলটিতে একটি নতুন টেবিলের ওআইডি রয়েছে, তবে সমবর্তী লেনদেন ইতিমধ্যে পূর্বের সারণির ওআইডিতে টেবিলের নামটি সমাধান করেছে । লকটি অবশেষে প্রকাশিত হলে, তারা টেবিলটি লেখার আগে নিজেই লক করে দেখার চেষ্টা করে যে এটি গেছে gone পোস্টগ্রাগেস উত্তর দেবে:

ERROR: could not open relation with OID 123456

123456পুরানো টেবিলের ওআইডি কোথায় । আপনার এপস কোডটি এড়াতে আপনার ব্যতিক্রমটি ধরতে হবে এবং কোয়েরিগুলি আবার চেষ্টা করতে হবে।

যদি আপনি এটির সামর্থ্য না করেন তবে আপনাকে আপনার মূল টেবিলটি রাখতে হবে

বিদ্যমান টেবিল রাখার দুটি বিকল্প

  1. NOT NULLসীমাবদ্ধতা যুক্ত করার আগে জায়গায় আপডেট (সম্ভবত একসময় ছোট বিভাগগুলিতে আপডেট চালানো) । NULL মান সহ এবং কোনও NOT NULLবাধা ছাড়াই একটি নতুন কলাম যুক্ত করা সস্তা।
    যেহেতু পোস্টগ্রেস 9.2 আপনি এতে CHECKসীমাবদ্ধতাNOT VALID তৈরি করতে পারেন :

    এই সীমাবদ্ধতাগুলি পরবর্তী সন্নিবেশ এবং আপডেটের বিরুদ্ধে এখনও প্রয়োগ করা হবে

    আপডেট সারি আপনি করতে পারবেন peu à peu মধ্যে - একাধিক পৃথক লেনদেনের । এটি সারি লকগুলিকে বেশি দিন ধরে রাখা এড়িয়ে যায় এবং এটি মৃত সারিগুলিকে পুনরায় ব্যবহার করতে দেয়। ( VACUUMঅটোভ্যাকুমের জন্য লাথি মারার জন্য যদি পর্যাপ্ত সময় না থাকে তবে আপনাকে ম্যানুয়ালি দৌড়াতে হবে)) অবশেষে, NOT NULLসীমাবদ্ধতাটি যোগ করুন এবং সীমাবদ্ধতাটি সরিয়ে দিন NOT VALID CHECK:

    ALTER TABLE tbl ADD CONSTRAINT tbl_no_null CHECK (tbl_uuid IS NOT NULL) NOT VALID;
    
    -- update rows in multiple batches in separate transactions
    -- possibly run VACUUM between transactions
    
    ALTER TABLE tbl ALTER COLUMN tbl_uuid SET NOT NULL;
    ALTER TABLE tbl ALTER DROP CONSTRAINT tbl_no_null;
    

    সম্পর্কিত উত্তর NOT VALIDআরও বিস্তারিত আলোচনা :

  2. অস্থায়ী সারণীতে নতুন রাজ্যটি প্রস্তুত করুন , টেম্প টেবিল থেকে TRUNCATEআসল এবং পুনরায় পূরণ করুন । সমস্ত একটি লেনদেন । সমবর্তী লেখাগুলি হারানো রোধ করতে নতুন টেবিল প্রস্তুত করার আগে আপনাকে এখনও একটি SHAREলক নিতে হবে ।

    এসও সম্পর্কিত এই সম্পর্কিত উত্তরের বিবরণ:


চমত্কার উত্তর! ঠিক যে তথ্যটি আমি সন্ধান করছিলাম দুটি প্রশ্ন ১. এরকম কোনও পদক্ষেপ নিতে কত সময় লাগবে তা পরীক্ষা করার কোনও সহজ উপায় সম্পর্কে আপনার কি ধারণা আছে? ২. যদি এটি 5 মিনিট সময় নেয়, তবে এই 5 মিনিটের সময় সেই টেবিলের মধ্যে একটি সারি আপডেট করার চেষ্টা করার ক্রিয়াগুলির কী হবে?
কলিন পিটারস

@ কলিনপিটার্স: ১. সময়ের সিংহের অংশটি বড় টেবিলটি অনুলিপি করতে পারে - এবং সম্ভবত সূচক এবং সীমাবদ্ধতা পুনরুদ্ধার করতে পারে (এটি নির্ভর করে)। ড্রপিং এবং নামকরণ সস্তা। পরীক্ষা করতে আপনি নিজের তৈরি এসকিউএল স্ক্রিপ্টটি LOCKআপ এবং বাদ দিয়ে চালাতে পারেন DROP। আমি কেবল বন্য এবং অকেজো অনুমানই বলতে পারি। ২ হিসাবে হিসাবে, দয়া করে আমার উত্তর সংযোজন বিবেচনা করুন।
এরউইন ব্র্যান্ডসটেটার

@ এরউইন ব্র্যান্ডস্টেটর পুনরুদ্ধার করা ভিউগুলিতে চালিয়ে যান, সুতরাং আমার কাছে যদি এমন এক ডজন ভিউ থাকে যা এখনও টেবিলের নাম পরিবর্তনের পরে পুরানো টেবিল (ওআইডি) ব্যবহার করে। পুরো ভিউ রিফ্রেশ / ক্রিয়েশন পুনরায় চালু করার চেয়ে গভীর প্রতিস্থাপন করার কোনও উপায় আছে কি?
কোডফর্মার

@ কোডএফার্মার: আপনি যদি কেবল একটি টেবিলটির নাম পরিবর্তন করেন তবে পুনরায় নামকরণ করা টেবিলের সাথে মতামত কাজ করে। পরিবর্তে নতুন টেবিলটি দেখতে ভিউগুলি তৈরি করতে , আপনাকে নতুন টেবিলের উপর ভিত্তি করে সেগুলি পুনরায় তৈরি করতে হবে। (এছাড়াও পুরানো টেবিলটি মোছার অনুমতি দেওয়ার জন্য)) এটির চারপাশে কোনও (ব্যবহারিক) উপায় নেই।
এরউইন ব্র্যান্ডসটেটার

14

আমার কাছে "সেরা" উত্তর নেই, তবে আমার কাছে একটি "ন্যূনতম খারাপ" উত্তর আছে যা আপনাকে যুক্তিসঙ্গতভাবে দ্রুত সম্পন্ন করতে দেয়।

আমার টেবিলটিতে 2 মিমি সারি ছিল এবং আমি যখন প্রথমটিতে ডিফল্ট হয়েছিল এমন একটি গৌণ টাইমস্ট্যাম্প কলাম যুক্ত করার চেষ্টা করছিলাম তখন আপডেটের পারফরম্যান্সটি খনন করা হয়েছিল।

ALTER TABLE mytable ADD new_timestamp TIMESTAMP ;
UPDATE mytable SET new_timestamp = old_timestamp ;
ALTER TABLE mytable ALTER new_timestamp SET NOT NULL ;

এটি 40 মিনিটের জন্য স্থির থাকার পরে, এটি কতটা সময় নিতে পারে তার ধারণা পেতে আমি একটি ছোট ব্যাচে চেষ্টা করেছিলাম - পূর্বাভাসটি প্রায় 8 ঘন্টা ছিল।

গৃহীত উত্তর অবশ্যই ভাল - তবে এই টেবিলটি আমার ডেটাবেজে প্রচুর ব্যবহার করা হয়। এটিতে কয়েক ডজন টেবিল রয়েছে যা এফকেইওয়াই; আমি অনেকগুলি টেবিলে বিদেশী কীগুলি স্যুইচ করা এড়াতে চেয়েছিলাম। এবং তারপরে মতামত রয়েছে।

কিছুটা দস্তাবেজ, কেস-স্টাডি এবং স্ট্যাক ওভারফ্লো অনুসন্ধান করছে এবং আমার কাছে "এ-হা!" মুহূর্ত। ড্রেনটি মূল আপডেটে ছিল না, তবে সমস্ত আইএনডেক্স অপারেশনে ছিল। আমার টেবিলটিতে এটিতে 12 টি সূচক ছিল - অনন্য বাধার জন্য কয়েকটি, ক্যোয়ারী পরিকল্পনাকারীকে গতি বাড়ানোর জন্য কয়েকটি এবং পুরো পাঠ্য অনুসন্ধানের জন্য কয়েকটি।

প্রতিটি সারি যা আপডেট করা হয়েছিল কেবল একটি মুছে ফেলা / INSERT এ কাজ করে না, প্রতিটি সূচকে পরিবর্তন করে ও সীমাবদ্ধতাগুলি পরীক্ষা করার ওভারহেডও ছিল।

আমার সমাধানটি ছিল প্রতিটি সূচি এবং সীমাবদ্ধতা বাদ দেওয়া, সারণি আপডেট করা এবং তারপরে সমস্ত সূচি / সীমাবদ্ধতাগুলি আবার যুক্ত করে।

এসকিউএল লেনদেন লিখতে প্রায় 3 মিনিট সময় লেগেছিল যা নিম্নলিখিতগুলি করেছে:

  • শুরু;
  • ছাড়ানো সূচক / স্থিরতা
  • আপডেট টেবিল
  • সূচি / সীমাবদ্ধতা পুনরায় যুক্ত করুন
  • কমিট;

স্ক্রিপ্টটি চালাতে 7 মিনিট সময় নিয়েছিল।

গৃহীত উত্তরটি অবশ্যই আরও ভাল এবং আরও সঠিক ... এবং ডাউনটাইমের প্রয়োজন কার্যত অপসারণ করে। আমার ক্ষেত্রে যদিও, এটি সমাধানটি ব্যবহার করতে উল্লেখযোগ্যভাবে আরও "বিকাশকারী" কাজটি গ্রহণ করতে পারত এবং আমাদের কাছে নির্ধারিত ডাউনটাইমের 30 মিনিটের উইন্ডো ছিল যা এটি সম্পন্ন হতে পারে Our আমাদের সমাধান এটি 10 ​​এ সম্বোধন করেছিল।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.