অলটার কলাম নুল না করার জন্য কেন বিশাল লগ ফাইলের বৃদ্ধির কারণ হয়?


56

আমার কাছে একটি টেবিল রয়েছে যার m৪ মিটার সারি এর ডেটার জন্য ডিস্কে ৪.৩ গিগাবাইট নিয়ে থাকে।

প্রতিটি সারিটি প্রায় 30 বাইট পূর্ণসংখ্যা কলাম, এবং NVARCHAR(255)পাঠ্যের জন্য একটি পরিবর্তনশীল কলাম।

আমি ডেটা টাইপের সাথে এএ নুল্ল্যাবল কলাম যুক্ত করেছি Datetimeoffset(0)

আমি তারপরে প্রতিটি সারির জন্য এই কলামটি আপডেট করেছি এবং নিশ্চিত করেছি যে সমস্ত নতুন সন্নিবেশকরা এই কলামে একটি মান রাখবে।

একবার যখন কোনও NULL এন্ট্রি ছিল না তখন আমি আমার নতুন ক্ষেত্রটি বাধ্যতামূলক করার জন্য এই আদেশটি চালিয়েছি:

ALTER TABLE tblCheckResult 
ALTER COLUMN [dtoDateTime] [datetimeoffset](0) NOT NULL

ফলাফলটি লেনদেনের লগের আকারে একটি বিশাল বৃদ্ধি ছিল - 6 জিবি থেকে 36 গিগাবাইটেরও বেশি স্থান অবধি শেষ না হওয়া পর্যন্ত!

পৃথিবীর এসকিউএল সার্ভার ২০০৮ আর 2 এত সাধারণ বৃদ্ধির ফলে এই সাধারণ কমান্ডটির জন্য কী করছে তার কোনও ধারণা আছে?


7
এসকিউএল সার্ভার 2012 এন্টারপ্রাইজ একটি মেটাডেটা অপারেশন হিসাবে একটি ডিফল্ট সাথে কলাম যুক্ত করার ক্ষমতা যুক্ত NOT NULLকরে। ডকুমেন্টেশনে "একটি অনলাইন অপারেশন হিসাবে নট কুল কলামগুলি যোগ করা" দেখুন ।
পল হোয়াইট

উত্তর:


48

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


একটি উদাহরণ. সাধারণ টেবিল:

DROP TABLE dbo.floob;
GO

CREATE TABLE dbo.floob
(
  id INT IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED, 
  bar INT NULL
);

INSERT dbo.floob(bar) SELECT NULL UNION ALL SELECT 4 UNION ALL SELECT NULL;

ALTER TABLE dbo.floob ADD CONSTRAINT df DEFAULT(0) FOR bar

এখন, পৃষ্ঠাগুলি বিশদটি দেখুন। প্রথমে আমাদের কী পৃষ্ঠা এবং ডিবি_আইডি নিয়ে কাজ করছি তা খুঁজে বের করতে হবে। আমার ক্ষেত্রে আমি একটি ডেটাবেস তৈরি করেছি fooএবং ডিবি_আইডি হ'ল 5।

DBCC TRACEON(3604, -1);
DBCC IND('foo', 'dbo.floob', 1);
SELECT DB_ID();

আউটপুট ইঙ্গিত দেয় যে আমি 159 পৃষ্ঠাতে আগ্রহী (এর DBCC INDসাথে আউটপুটে একমাত্র সারি PageType = 1)।

এখন, আসুন আমরা ওপির দৃশ্যের মাধ্যমে পদক্ষেপ নেওয়ার সাথে সাথে কয়েকটি নির্বাচিত পৃষ্ঠাগুলির বিবরণটি দেখি।

DBCC PAGE(5, 1, 159, 3);

এখানে চিত্র বর্ণনা লিখুন

UPDATE dbo.floob SET bar = 0 WHERE bar IS NULL;    
DBCC PAGE(5, 1, 159, 3);

এখানে চিত্র বর্ণনা লিখুন

ALTER TABLE dbo.floob ALTER COLUMN bar INT NOT NULL;
DBCC PAGE(5, 1, 159, 3);

এখানে চিত্র বর্ণনা লিখুন

এখন, আমার কাছে এর সমস্ত উত্তর নেই, কারণ আমি কোনও গভীর ইন্টার্নাল লোক নই। তবে এটি পরিষ্কার যে - হালনাগাদ অপারেশন এবং নূন্যতম সীমাবদ্ধতার সংযোজন উভয়ই অনিচ্ছাকৃতভাবে পৃষ্ঠাতে লিখুন - পরেরটি সম্পূর্ণ ভিন্ন উপায়ে এটি করে। এটি কেবলমাত্র বিটগুলি দিয়ে মুরগির পরিবর্তে রেকর্ডের কাঠামোটিকে পরিবর্তিত বলে মনে হচ্ছে, একটি অ-শর্তযুক্ত কলামের জন্য অযোগ্য কলামটি অদলবদল করে। কেন এটি করতে হবে, আমি বেশ নিশ্চিত নই - স্টোরেজ ইঞ্জিন দলের জন্য একটি ভাল প্রশ্ন , আমার ধারণা। আমি বিশ্বাস করি যে এসকিউএল সার্ভার ২০১২ এফএনডাব্লুআইডাব্লু - এর মধ্যে আরও কিছু পরিস্থিতি আরও ভালভাবে পরিচালনা করে তবে আমার এখনও কোনও পরিসমাপ্তি পরীক্ষা করতে হয়নি।


4
এই আচরণটি এসকিউএল সার্ভারের পরবর্তী সংস্করণগুলিতে যথেষ্ট পরিবর্তন হয়েছে। আমি ২০১ R সালের আরসি 2 পরীক্ষা করে দেখেছি যে এই সঠিক দৃশ্যের জন্য এবং টেবিলের 1 মিলিয়ন সারিগুলির জন্য কেবল 29 টি লগ রেকর্ড তৈরি হয়েছে যখন সমস্ত মান ইতিমধ্যে কলামের জন্য নির্দিষ্ট করা থাকে তবে NULL থেকে NULL পরিবর্তন করা হয়নি।
এন্ডারজু

32

আদেশ বহন করার সময়

ALTER COLUMN ... NOT NULL

এটি অ্যাড কলাম, আপডেট, ড্রপ কলাম অপারেশন হিসাবে প্রয়োগ করা হবে বলে মনে হচ্ছে।

  • sys.sysrscolsএকটি নতুন কলামটি একটি নতুন কলামটি উপস্থাপন করতে প্রবেশ করানো হয়েছে । statusজন্য বিট 128কলাম ইঙ্গিত অনুমতি দেয় না সেট করা হয় NULLগুলি
  • পুরানো কলমের মানটির সাথে নতুন কলামের মান সেট করে টেবিলের প্রতিটি সারিটিতে একটি আপডেট করা হয়। যদি সারিটির "পূর্বে" এবং "পরে" সংস্করণগুলি একই রকম হয় তবে এটি লেনদেনে লগতে কোনও জিনিস লেখার কারণ না করে অন্যথায় আপডেটটি লগ হয়।
  • মূল কলাম হিসাবে বাদ চিহ্নিত করা হয় (এটি মেটাডেটা শুধুমাত্র পরিবর্তন হয় sys.sysrscolsrscolidবৃহৎ পূর্ণসংখ্যা এবং আপডেট করা statusথেকে নির্দেশিত বাদ উপর বিট 2 Set)
  • sys.sysrscolsপুরানো কলামটির জন্য নতুন কলামে প্রবেশের পরিবর্তন করা হয় rscolid

যে ক্রিয়াকলাপে প্রচুর লগিং হওয়ার সম্ভাবনা রয়েছে তা হ'ল UPDATEটেবিলের সমস্ত সারি হ'ল এর অর্থ এই নয় যে এটি সর্বদা ঘটবে। যদি সারিটির "পূর্বে" এবং "পরে" চিত্রগুলি অভিন্ন হয় তবে এটি একটি অ-আপডেট আপডেট হিসাবে গণ্য হবে এবং এখনও পর্যন্ত আমার পরীক্ষায় লগ করা হবে না।

সুতরাং আপনি কেন প্রচুর লগিং পাচ্ছেন তার ব্যাখ্যা নির্ভর করবে কেন সারিটির "আগে" এবং "পরে" সংস্করণগুলি ঠিক একই নয়।

FixedVarবিন্যাসে সঞ্চিত ভেরিয়েবল দৈর্ঘ্যের কলামগুলির জন্য আমি দেখতে পেয়েছি যে সেটিংটি NOT NULLসর্বদা সারিতে পরিবর্তিত হয় যা লগ করা দরকার। কলামের গণনা এবং ভেরিয়েবল দৈর্ঘ্যের কলাম গণনা উভয়ই বর্ধিত হয় এবং নতুন কলামটি ভেরিয়েবল দৈর্ঘ্যের বিভাগের শেষে ডেটাটিকে নকল করে যোগ করা হয়।

datetimeoffset(0)তবে দৈর্ঘ্য নির্দিষ্ট করা হয়েছে এবং FixedVarপুরানো এবং নতুন কলাম উভয়ই সারিটির নির্দিষ্ট দৈর্ঘ্যের ডেটা অংশে একই স্লট দেওয়া হবে বলে মনে হচ্ছে এবং উভয়টির দৈর্ঘ্য একই এবং "পূর্বে" মান এবং সারিটির "পরে" সংস্করণগুলি একই । এটি @ হারুনের উত্তরে দেখা যাবে। সারিটির উভয় সংস্করণ আগে এবং পরে ALTER TABLE dbo.floob ALTER COLUMN bar INT NOT NULL;রয়েছে

0x10000c00 01000000 00000000 020000

এটি লগ করা হয়নি।

যৌক্তিকভাবে আমার ইভেন্টগুলির বিবরণ থেকে সারিটি এখানে অবশ্যই আলাদা 02হওয়া উচিত কারণ কলামের গণনা বাড়ানো উচিত 03তবে বাস্তবে এরকম কোনও পরিবর্তন ঘটে না।

এটি একটি নির্দিষ্ট দৈর্ঘ্যের কলামে কেন ঘটতে পারে তার কয়েকটি সম্ভাব্য কারণ

  • কলামটি যদি প্রাথমিকভাবে ঘোষণা করা হয় SPARSEতবে নতুন কলামটি মূল থেকে সারিটির একটি পৃথক অংশে সংরক্ষণ করা হবে যার ফলে সারিগুলির চিত্রগুলি আগের এবং পরে পৃথক হবে।
  • আপনি যদি কোনও সংক্ষেপণ বিকল্প ব্যবহার করে থাকেন তবে সিডি অ্যারেতে কলাম গণনা বিভাগটি বর্ধিত হওয়ায় সারিটির আগের এবং পরে সংস্করণগুলি আলাদা হবে।
  • স্ন্যাপশট বিচ্ছিন্নকরণের বিকল্পগুলির একটি সহ ডাটাবেসে সক্ষম হয় তারপরে প্রতিটি সারিতে সংস্করণ তথ্য আপডেট করা হয় (@ এসকিউএল কিভি উল্লেখ করে যে এটি এসআই সক্ষম না করে ডাটাবেসেও বর্ণিত আছে ) enabled
  • পূর্ববর্তী কিছু ALTER TABLEঅপারেশন হতে পারে যা কেবলমাত্র মেটাডেটা পরিবর্তন হিসাবে প্রয়োগ করা হয়েছিল এবং এখনও সারিটিতে প্রয়োগ করা হয়নি। উদাহরণস্বরূপ যদি কোনও নতুন অযোগ্য পরিবর্তনশীল দৈর্ঘ্যের কলামটি যুক্ত করা হয় তবে এটি মূলত কেবলমাত্র মেটাডেটা পরিবর্তন হিসাবে প্রয়োগ করা হয় এবং পরবর্তী সময়ে আপডেট হওয়ার পরে এটি কেবল সারিগুলিতে লেখা হয় (এই শেষ উদাহরণটিতে লেখাটি আসলে আপডেট হওয়া মাত্র কলাম গণনা বিভাগ এবং সারির শেষে NULL_BITMAPএকটি NULL varcharকলাম হিসাবে কোনও স্থান নেয় না)

5

200.000.000 সারি থাকা একটি টেবিলের বিষয়ে আমি একই সমস্যার মুখোমুখি হয়েছি। শুরুর দিকে আমি কলামটি শোধনযোগ্য যুক্ত করেছি, তারপরে সমস্ত সারি আপডেট করেছি এবং শেষ পর্যন্ত NOT NULLএকটি ALTER TABLE ALTER COLUMNবিবৃতি দিয়ে কলামটি পরিবর্তন করেছি । এর ফলে দুটি বিশাল লেনদেনের ফলে অবিশ্বাস্যভাবে লগফিলটি উড়ে গেছে (170 গিগাবাইট বৃদ্ধি)।

আমি যে দ্রুততম পন্থাগুলি খুঁজে পেয়েছি তা হল:

  1. ডিফল্ট মান ব্যবহার করে কলাম যুক্ত করুন

    ALTER TABLE table1 ADD column1 INT NOT NULL DEFAULT (1)
  2. গতিশীল এসকিউএল ব্যবহার করে ডিফল্ট সীমাবদ্ধতা ফেলে দিন কারণ এই সীমাবদ্ধতার নাম আগে বলা হয়নি:

    DECLARE 
        @constraint_name SYSNAME,
        @stmt NVARCHAR(510);
    
    SELECT @CONSTRAINT_NAME = DC.NAME
    FROM SYS.DEFAULT_CONSTRAINTS DC
    INNER JOIN SYS.COLUMNS C
        ON DC.PARENT_OBJECT_ID = C.OBJECT_ID
        AND DC.PARENT_COLUMN_ID = C.COLUMN_ID
    WHERE
        PARENT_OBJECT_ID = OBJECT_ID('table1')
        AND C.NAME = 'column1';

লেনদেনের সময়টি 30 মিনিট থেকে 10 মিনিটে নেমে গেছে, এতে ট্রানজেকশনাল প্রতিরূপের মাধ্যমে পরিবর্তনগুলি প্রতিলিপি করা সহ। আমি একটি এসকিউএল সার্ভার 2008 ইনস্টলেশন (এসপি 2) চালাচ্ছি।


2

আমি নিম্নলিখিত পরীক্ষা চালিয়েছি:

create table tblCheckResult(
        ColID   int identity
    ,   dtoDateTime Datetimeoffset(0) null
    )

 go

insert into tblCheckResult (dtoDateTime)
select getdate()
go 10000

checkpoint 

ALTER TABLE tblCheckResult 
ALTER COLUMN [dtoDateTime] [datetimeoffset](0) NOT NULL

select * from fn_dblog(null,null)

আমি বিশ্বাস করি যে লেনদেনটি রোলব্যাকের ক্ষেত্রে লগটি কেবলমাত্র সংরক্ষিত জায়গার সাথে করতে হবে। LOP_BEGIN_XACT সারির জন্য 'লগ রিজার্ভ' কলামে fn_dblog ফাংশনটি দেখুন এবং দেখুন যে এটি কতটা জায়গা সংরক্ষণের চেষ্টা করছে।


যদি আপনি চেষ্টা করেন select * FROM fn_dblog(null, null) where AllocUnitName='dbo.tblCheckResult' AND Operation = 'LOP_MODIFY_ROW'তবে 10000 সারি আপডেটগুলি দেখতে পাবেন।
মার্টিন স্মিথ

-2

এই জন্য আচরণ SQL সার্ভার 2012 দেখুন ভিন্ন http://rusanu.com/2011/07/13/online-non-null-with-values-column-add-in-sql-server-11/

এসকিউএল সার্ভার ২০০৮ আর 2 এর জন্য উত্পন্ন লগ রেকর্ডের সংখ্যা এবং নীচে প্রকাশগুলি এসকিউএল সার্ভার ২০১২-এর লগ রেকর্ডারের সংখ্যার তুলনায় উল্লেখযোগ্যভাবে বেশি হবে।


2
NOT NULLলগিংয়ের কারণে বিদ্যমান কলামটি কেন পরিবর্তন করা হচ্ছে তা প্রশ্ন । ২০১২ সালের পরিবর্তনটি NOT NULLএকটি ডিফল্ট সহ একটি নতুন কলাম যুক্ত করার বিষয়ে ।
মার্টিন স্মিথ
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.