📍 ধানমন্ডি, ঢাকা-১২০৫🇬🇧 English

Oracle SQL Plan Management: কাল দ্রুত, আজ ধীর

যে রিপোর্ট কয়েক সেকেন্ডে চলত সেটি আজ বিশ মিনিট নিচ্ছে অথচ কেউ কিছু deploy করেনি, এমন ক্ষেত্রে SQL টেক্সট সাধারণত অপরিবর্তিত থাকে আর বদলে যায় execution plan। Oracle SQL Plan Management (SPM) ঠিক এই পুনরাবৃত্তি ঠেকায়: optimizer নতুন plan খুঁজতে পারে, কিন্তু চালাতে পারে কেবল সেগুলোই যেগুলো আপনি accept করেছেন। Oracle-এর নিজের ভাষায়: "SQL plan management prevents performance regressions caused by plan changes."

মূল কথাগুলো

  • Baseline ব্যবহার আগে থেকেই চালু: OPTIMIZER_USE_SQL_PLAN_BASELINES-এর ডিফল্ট true। কিন্তু ধরা চালু নয়: OPTIMIZER_CAPTURE_SQL_PLAN_BASELINES-এর ডিফল্ট false, আর দুটি সেটিং একে অন্যের থেকে স্বাধীন।
  • Baseline মানে accepted plan-এর একটি সেট। Hard parse-এ optimizer নিজের সবচেয়ে কম খরচের plan বানায়, তারপর মিল খোঁজে; না মিললে সেই plan ইতিহাসে unaccepted হিসেবে জমা থাকে, চলে না।
  • ENABLED, ACCEPTED ও FIXED আলাদা তিনটি flag। প্রার্থী হতে plan-কে enabled এবং accepted দুটোই হতে হয়, আর FIXED plan non-fixed plan-এর চেয়ে অগ্রাধিকার পায়।
  • জরুরি pin: ভালো plan_hash_value খুঁজে বের করুন, DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE দিয়ে লোড করুন, fixed => 'YES' দিন, তারপর plan-এর নিচের Note লাইন থেকে প্রমাণ নিন।
  • Evolution-ই baseline-কে স্ট্রেটজ্যাকেট হতে দেয় না। SYS_AUTO_SPM_EVOLVE_TASK maintenance window-এ রোজ চলে; high-frequency টাস্কটির ডিফল্ট OFF এবং চালু থাকলে ঘণ্টায় একবার, সর্বোচ্চ ৩০ মিনিট।
  • SQL management base থাকে SYSAUX-এ, ওই tablespace-এর ১০% ডিফল্ট বাজেট আর অব্যবহৃত plan-এর জন্য ৫৩ সপ্তাহের ডিফল্ট retention নিয়ে।
  • SPM plan জমিয়ে রাখে। এটি খারাপ পরিসংখ্যান, অনুপস্থিত index বা দুর্বল ডেটা মডেল সারায় না, আর Oracle বলে baseline "cannot help when an event has caused irreversible execution plan changes, such as dropping an index."
দুই সেট রেললাইনের মাঝখানে দাঁড়ানো একটি লাল সিগন্যাল বাতি, পাশে নম্বর লেখা সাদা ও হলুদ প্লেট এবং স্লিপারে বসানো ধূসর point machine
Point machine ট্রেনকে কয়েক দিকে পাঠাতে পারে। কোন দিকটি আজ অনুমোদিত, তা ঠিক করে সিগন্যাল। Photo: Martijn Stoof / Pexels

১. কোয়েরিটি কাল দ্রুত আর আজ ধীর কেন?

কারণ optimizer একই statement-এর জন্য একটি নতুন execution plan বেছেছে। পরিসংখ্যান নতুন করে gather হয়েছে, টেবিল বেড়েছে, কোনো bind value বদলেছে, অথবা ডেটাবেজ আপগ্রেড হয়েছে। SQL একই আছে; তার plan আর একই নেই।

একই SQL_ID-র নিচে একাধিক PLAN_HASH_VALUE দেখলেই বোঝা যায় এটি ঘটেছে। Plan flip নিজে থেকে ঘোষণা দেয় না; এটি আসে এমন ফোনকলের রূপে যেখানে কেউ বলছে আগে ঠিক চলা একটি স্ক্রিন আজ আটকে যাচ্ছে।

Bind peeking এর একটি সাধারণ উৎস। Optimizer প্রথম execution-এ bind value দেখে সেই মান অনুযায়ী plan বানায়; পরের execution-এ অন্য মান এলে সেই plan আর উপযুক্ত নাও হতে পারে। Adaptive cursor sharing এই অবস্থা লক্ষ করে, যা কাজে লাগে, আর এ কারণেই একটি statement চুপচাপ দুটি ভিন্ন plan ধরে রাখতে পারে।

আপগ্রেড একই ঘটনার পূর্বনির্ধারিত সংস্করণ। নতুন optimizer সংস্করণে কিছু statement-এর plan বদলায়, আর Oracle নিজেই আপগ্রেডের আগে baseline ধরে রাখাকে regression কমানোর উপায় বলে।

পরিকল্পনা করছেন? দেখুন ১৯c থেকে ২৬ai আপগ্রেড গাইড।

২. SQL plan baseline আসলে কী?

SQL plan baseline হলো একটি statement-এর জন্য accepted execution plan-এর সেট, যা SQL management base-এ সংরক্ষিত থাকে। প্রতিটি plan hint আকারে জমা থাকে, যা plan-টিকে সম্পূর্ণভাবে নির্দিষ্ট করে, যাতে পরে সেটি হুবহু পুনরুৎপাদন করা যায়।

Plan বসে থাকে একটি normalized SQL signature-এর বিপরীতে। Normalization হয় কেবল case ও whitespace-এর জন্য, literal-এর জন্য নয়। অর্থাৎ WHERE status = 'A' আর WHERE status = 'B' SPM-এর কাছে দুটি আলাদা statement, যদিও আপনার কাছে তারা এক।

এই একটি বাক্য পরে অনেক বিভ্রান্তি ঠেকায়: bind variable ব্যবহার না করা অ্যাপ্লিকেশনে প্রতিটি মানের জন্য আলাদা signature তৈরি হয়।

৩. Baseline, SQL profile না stored outline - কোনটি কী?

যেটিযা করেকখন ব্যবহার করবেন
SQL plan baselineAccepted plan-এর সেট; optimizer-কে এর বাইরে যেতে দেয় নাচলমান একটি ভালো plan ধরে রাখতে
SQL profileবাড়তি পরিসংখ্যান; optimizer-এর হিসাব সংশোধন করে, plan ঠিক করে দেয় নাহিসাব ভুল হলে, যখন নমনীয়তা দরকার
Stored outlineপুরনো প্রযুক্তি; baseline-এ migrate করা যায়নতুন কাজে নয়; উত্তরাধিকারে পেলে migrate করুন

Oracle-এর নিজের সারসংক্ষেপটি মনে রাখার মতো: "in general, SQL plan baselines are proactive, whereas SQL profiles are reactive." Baseline হলো দুর্ঘটনার আগে পরা সিটবেল্ট; profile হলো দুর্ঘটনার পরের চিকিৎসা।

৪. Parse-এর সময় optimizer baseline কীভাবে ব্যবহার করে?

Hard parse-এ optimizer আগে নিজের সবচেয়ে কম খরচের plan বানায়। তারপর দেখে ওই statement-এর কোনো baseline আছে কি না। থাকলে সে খোঁজে তার বানানো plan baseline-এর কোনো accepted plan-এর সঙ্গে মেলে কি না।

মিললে সেটিই চলে। না মিললে নতুন plan ইতিহাসে unaccepted হিসেবে যোগ হয় এবং optimizer baseline-এর সবচেয়ে কম খরচের accepted plan চালায়। এভাবেই নতুন plan আপনার জানার আগে প্রোডাকশনে ঢুকতে পারে না।

একটি গুরুত্বপূর্ণ ব্যতিক্রম আছে। Baseline-এর কোনো plan যদি আর পুনরুৎপাদন করা না যায়, যেমন প্রতিটি plan এমন index ব্যবহার করে যা drop হয়ে গেছে, তখন optimizer নতুন cost-based plan-ই ব্যবহার করে। EXPLAIN PLAN দিয়ে পরীক্ষা করলেও সাবধান: Oracle-এর বক্তব্যের সারাংশ: শুধু EXPLAIN PLAN দিয়ে plan দেখেছেন কিন্তু statement execute করেননি—এমন statement-কে SPM সুরক্ষা দেয় না।

ফ্যাকাশে নীল একটি শিল্প কন্ট্রোল ডেস্কে সারি সারি জোড়া লাল ও সবুজ পুশ বোতাম এবং একটি ছোট অ্যাম্বার বাতি
ENABLED, ACCEPTED ও FIXED এক প্যানেলের তিনটি আলাদা সুইচ, একই জিনিসের তিন নাম নয়। Photo: Fernando Narvaez / Pexels

৫. Baseline প্রথমে ধরবেন কীভাবে?

দুটি পথ: স্বয়ংক্রিয় capture আর হাতে লোড করা।

Automatic capture চালু করেন OPTIMIZER_CAPTURE_SQL_PLAN_BASELINES দিয়ে, যার ডিফল্ট false। চালু করার আগে Oracle-এর সতর্কবার্তাটি পড়ুন: এটি অত্যন্ত বেশি সংখ্যক baseline তৈরি করতে পারে, কারণ recursive ও monitoring SQL-ও ধরা পড়ে। তাই DBMS_SPM.CONFIGURE দিয়ে filter বসান, আর মনে রাখুন একই procedure-এ একই parameter-এর একাধিক মান দেওয়া যায় না; প্রতিটি মানের জন্য আলাদা কল করুন।

হাতে লোড করা আলাদা ব্যাপার, আর আউটেজে এটিই কাজে লাগে: "manually loaded plans are always marked accepted." Oracle পাঁচটি উৎস নথিভুক্ত করে - AWR, একটি SQL tuning set, shared SQL area, একটি staging table আর stored outline। নিশ্চিত না হলে নিষ্ক্রিয় অবস্থায় লোড করা যায়: Oracle বলে "load plans without enabling them by setting the enabled parameter to NO in the DBMS_SPM.LOAD_PLANS_FROM_% functions."

AWR থেকে লোডের basic_filter নিয়ে একটি কথা, কারণ Oracle এটিকে দুই জায়গায় বর্ণনা করে। SQL Tuning Guide বলে এটি "any WHERE clause predicate that can be specified against the column DBA_HIST_SQLTEXT.SQL_TEXT"; PL/SQL packages reference বলে এটি "defined on attributes of the SQLSET_ROW", আর সেই row-এর attribute-এর মধ্যে sql_id ও plan_hash_value দুটিই আছে।

এই লেখা আউটেজের মধ্যে ঠিক একটি plan বাছতে ওই filter-এর ওপর ভরসা করে না। ধাপ ৪ AWR থেকে enabled => 'NO' দিয়ে লোড করে, যা এল সব দেখে, তারপর শুধু যে plan-টি চাই সেটিকেই চালু করে।

৬. ENABLED, ACCEPTED ও FIXED-এর আসল মানে কী?

FlagOracle-এর সংজ্ঞাকখন বদলাবেন
ENABLED"YES means the plan is available for use by the optimizer."Drop না করে ফিরিয়ে আনা যায় এমনভাবে plan সরাতে
ACCEPTEDPlan baseline accepted (YES) না unaccepted (NO)Evolution-এর মাধ্যমে, অথবা হাতে লোড করে
FIXED"YES means the SQL plan baseline is not evolved over time. A fixed plan takes precedence over a non-fixed plan."কেবল জরুরি অবস্থায়, পুনর্বিবেচনার তারিখ ডায়েরিতে লিখে
REPRODUCEDOptimizer plan-টি পুনরুৎপাদন করতে পেরেছিল কি নাকখনও নয়। এটি উপসর্গ, আর NO সবার আগে দেখার জিনিস

সতর্কতা: ALTER_SQL_PLAN_BASELINE-এ plan_name না দিলে পরিবর্তনটি ওই handle-এর প্রতিটি plan-এ প্রয়োগ হয়। কাজে লাগে, আবার ভুল করাও সহজ।

অন্ধকার ঘরে নিচ থেকে রঙিন আলোয় আলোকিত একটি মেকানিক্যাল কি-বোর্ডের উপর দুটি হাত
Pin এই ঘণ্টার জন্য। Pin পুনর্বিবেচনার কাজটি দিনের আলোর। Photo: Tima Miroshnichenko / Pexels

৭. চারপাশে চিৎকারের মধ্যে ভালো plan কীভাবে pin করবেন?

নিচের পাঁচ ধাপ কনসোলের পাশে রাখার মতো ছোট। ধরে নিচ্ছি কোন statement কষ্ট দিচ্ছে তা আপনি জানেন, সাধারণত AWR ও ASH থেকে।

  1. ধাপ ১: statement ও তার দুটি plan খুঁজুন।
  2. ধাপ ২: ভালো plan এখনও মেমোরিতে আছে কি না নিশ্চিত হোন।
  3. ধাপ ৩: সেটি fixed হিসেবে লোড করুন।
  4. ধাপ ৪: ভালো plan মেমোরি থেকে চলে গিয়ে থাকলে AWR থেকে disabled অবস্থায় লোড করুন, দেখে নিন, তারপর একটিমাত্র plan চালু করুন।
  5. ধাপ ৫: plan সত্যিই ব্যবহৃত হচ্ছে তার প্রমাণ নিন, তারপর তারিখ লিখে রাখুন।
-- ধাপ ১: এক SQL_ID-র নিচে একাধিক plan আছে কি না
SELECT sql_id, plan_hash_value, child_number,
       executions, ROUND(elapsed_time/GREATEST(executions,1)/1000) ms_per_exec
FROM   v$sql
WHERE  sql_id = '&sql_id'
ORDER  BY plan_hash_value;

-- ধাপ ৩: কেবল ভালো plan hash লোড করুন, fixed হিসেবে
VARIABLE n NUMBER
EXEC :n := DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE( -
  sql_id           => '&sql_id', -
  plan_hash_value  => &good_plan_hash, -
  fixed            => 'YES');
PRINT n

plan_hash_value বাদ দিলে Oracle ওই SQL_ID-র cursor cache-এর সব plan লোড করে - খারাপটিসহ, accepted অবস্থায়। তাই hash-টি নাম ধরে দিন।

ধাপ ৪ এখানেই আলাদা, আর এখানেই বিপদ। LOAD_PLANS_FROM_AWR-এ কোনো plan_hash_value parameter নেই, আর basic_filter ফাঁকা রাখলে Oracle-এর ভাষায় "NULL means all plans in AWR are selected"। তার ওপর enabled-এর ডিফল্ট 'YES', অর্থাৎ "the loaded plans will be considered by the optimizer"।

ডিফল্ট মেনে এক কলে লোড করলে ওই snapshot পরিসরে AWR-এ রাখা প্রতিটি plan সঙ্গে সঙ্গে accepted ও enabled হয়ে যায় - যে খারাপ plan থেকে আপনি পালাচ্ছেন সেটিও - আর আপনি তখনও একটিও plan চোখে দেখেননি। হাতে লোড করা plan সবসময় accepted, তাই পেছনে দ্বিতীয় কোনো গেট নেই। তাই: নিষ্ক্রিয় অবস্থায় লোড করুন, পড়ুন, তারপর একটিকে উন্নীত করুন।

-- ৪ক. লোড করার আগে baseline-এ এখন কী আছে তা লিখে রাখুন।
--      এখানে ENABLED যা আছে তা পরেও প্রার্থী থাকবে; FIXED যা আছে তা আরও উপরে।
SELECT plan_name, origin, enabled, accepted, fixed, created
FROM   dba_sql_plan_baselines
WHERE  sql_handle = 'SQL_b6b0d1c71cd1807b'
ORDER  BY created;

-- ৪খ. সমস্যার আগের একটি snapshot পরিসর বাছুন।
SELECT snap_id, begin_interval_time
FROM   dba_hist_snapshot
ORDER  BY snap_id DESC FETCH FIRST 20 ROWS ONLY;

-- ৪গ. প্রার্থীদের নিষ্ক্রিয় অবস্থায় লোড করুন: enabled নয়, fixed-ও বদলাবে না।
VARIABLE cnt NUMBER
EXEC :cnt := DBMS_SPM.LOAD_PLANS_FROM_AWR( -
  begin_snap   => 212, -
  end_snap     => 213, -
  basic_filter => q'[sql_id = '27m0sdw9snw59']', -
  enabled      => 'NO', -
  fixed        => 'NO');
PRINT cnt

-- ৪ঘ. কী এল দেখুন, তারপর প্রতিটি নতুন plan পড়ে নিন।
SELECT plan_name, origin, enabled, accepted, fixed, created
FROM   dba_sql_plan_baselines
WHERE  sql_handle = 'SQL_b6b0d1c71cd1807b'
ORDER  BY created;

SELECT t.*
FROM   TABLE(DBMS_XPLAN.DISPLAY_SQL_PLAN_BASELINE(
               'SQL_b6b0d1c71cd1807b', 'SQL_PLAN_bdc6d3jsd3g3c1234abcd')) t;

-- ৪ঙ. এতক্ষণে, এবং কেবল ৪ঘ-তে চিহ্নিত একটিমাত্র plan_name-এর জন্য।
VARIABLE n NUMBER
EXEC :n := DBMS_SPM.ALTER_SQL_PLAN_BASELINE( -
  sql_handle      => 'SQL_b6b0d1c71cd1807b', -
  plan_name       => 'SQL_PLAN_bdc6d3jsd3g3c1234abcd', -
  attribute_name  => 'enabled', -
  attribute_value => 'YES');
PRINT n

DBA_SQL_PLAN_BASELINES-এ plan hash-এর কলাম নেই, তাই ধাপ ১-এর ভালো PLAN_HASH_VALUE মিলিয়ে plan চেনা যাবে না। চেনার উপায় আকৃতি: প্রতিটি নতুন plan_name-এর জন্য একবার করে DISPLAY_SQL_PLAN_BASELINE চালান আর ধাপ ১-এ রাখা ভালো plan-এর পাশে রাখুন (SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_AWR('27m0sdw9snw59', 1234567890));)।

operation, তাদের ক্রম, access path ও join method মিলছে কি না - এটিই প্রমাণ। দুটি মিললে বাড়তিটিকে disabled রেখে দিন; পরে চালু করা যাবে।

ধাপ ৪ক কাগুজে কাজ নয়। আগে থেকেই enabled ও accepted থাকা plan লোডের পরেও প্রার্থী থাকে, আর আগে থেকে fixed থাকা plan আরও বড় সমস্যা - Oracle: "if new plans are added to a baseline that contains at least one enabled fixed plan, then the optimizer cannot use the new plans until you manually declare them as fixed." তাই ৪ক-তে enabled fixed কোনো plan দেখা গেলে এবং সেটিই সমস্যা হলে, নাম ধরে ALTER_SQL_PLAN_BASELINE-এ attribute_name => 'enabled', attribute_value => 'NO' দিয়ে সেটিকে বন্ধ করুন।

পুরনো fixed plan রেখে নতুনটিকে উন্নীত করলে কিছুই বদলাবে না। নতুন plan-টিকেও fixed করতে চাইলে ৪ঙ-এর মতো আরেকটি কল, attribute_name => 'fixed' দিয়ে।

৪ঙ-তে plan_name কখনও NULL রাখবেন না; তাহলে পরিবর্তনটি ওই handle-এর প্রতিটি plan-এ বসে, অর্থাৎ ৪ঘ-তে ছেঁকে বাদ দেওয়া plan-গুলোকেই চালু করে দেয়। প্রতিটি কল কয়টি plan বদলাল তা ফেরত দেয়: PRINT n-এ ১ আসা উচিত; অন্য কিছু এলে থামুন।

৪গ-তে fixed => 'NO' ডিফল্ট, আর এটি কারও fixed অবস্থা মুছে দেয় না - Oracle-এর সংজ্ঞায় "the loaded plans will not change the current 'fixed' property of the SQL plan baseline into which they are loaded"। তবু কলে লিখে রাখা হয়েছে, কারণ এই পদ্ধতি রাত তিনটায় পড়া হয়।

AWR-এ কেবল ততটুকুই থাকে যতটুকু retention রেখেছে - দরকার পড়ার অনেক আগেই যুক্তিসঙ্গত retention রাখার কারণ এটিই।

ধাপ ৫ ছাড়া কিছুই প্রমাণ নয়। DBA_SQL_PLAN_BASELINES-এর সারি বলে plan আছে ও অনুমোদিত, কিন্তু optimizer সেটিই বেছেছে কি না বলে না।

তাই statement চালিয়ে একই session/instance-এর V$SQL থেকে সদ্য চালানো child-এর CHILD_NUMBER বেছে নিন। উদাহরণের &actual_child_no SQL*Plus/SQLcl substitution-এ সেই সংখ্যাটি দিন; একাধিক child থাকলে প্রতিটির execution ও plan মিলিয়ে নিন। এই argument বাদ দিলে child 0 দেখা যায়; তাই সদ্য execute হওয়া child ধরে নেওয়া ঠিক নয়।

তারপর SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('27m0sdw9snw59', &actual_child_no)); দিয়ে plan-এর নিচের Note লাইনটি দেখুন, আর V$SQL-এর SQL_PLAN_BASELINE কলামে আপনার plan_name এবং ধাপ ১-এর ভালো PLAN_HASH_VALUE আছে কি না মিলিয়ে নিন। একই executed child-এর Note নেই আর V$SQL-এ baseline-এর নাম নেই মানে pin বসেনি, baseline টেবিল যা-ই বলুক - আর বিশ্বাসের বদলে প্রমাণ এই পদ্ধতির মূল কথা।

একটি উদাহরণ (কল্পিত দৃষ্টান্ত, কোনো ঘটনার বিবরণ নয়)

একটি costing স্ক্রিন হঠাৎ টাইম আউট করছে, অথচ ডেটাবেজে কেউ সপ্তাহখানেক হাত দেয়নি। V$SQL-এ এক SQL_ID-র নিচে দুটি সারি, দুটি ভিন্ন PLAN_HASH_VALUE, একটি সদ্য তৈরি। একটি statistics gather এমন টেবিল রিফ্রেশ করেছে যা গতবারের পর বেড়েছে, আর optimizer join-টি নতুন করে হিসাব করেছে।

ধাপ ৩ হলো টুর্নিকেট: পুরনো plan hash-টি fixed => 'YES' দিয়ে লোড করুন, তারপর ধাপ ৫-এ Note লাইন পড়ে প্রমাণ নিন। Pin সময় কিনে দেয়, সমাধান নয়। আসল সমাধান উপরের দিকে - পরিসংখ্যান, index বা ডেটা মডেলে।

কোনো সময়ের হিসাব এখানে দেওয়া হয়নি, কারণ তা বানানো হতো।

৮. Plan কীভাবে evolve হয়, আর কাজটি Oracle-কে দেবেন কি?

Evolution হলো unaccepted plan-এর accepted হয়ে ওঠার পথ। Oracle প্রার্থী plan-টি পরীক্ষামূলকভাবে চালায়, বর্তমান baseline plan-এর সঙ্গে তুলনা করে, আর ভালো করলে তবেই তাকে উন্নীত করে।

কারণটি ডকুমেন্টেশনেই আছে: "if new plans were never added to SQL plan baselines, then the performance of some SQL statements might degrade." ২০২৪ সালের একটিমাত্র plan নিয়ে পড়ে থাকা baseline স্থিতিশীলতা নয়, ধীরগতির ক্ষয়।

স্বয়ংক্রিয় টাস্ক দুটি। SYS_AUTO_SPM_EVOLVE_TASK maintenance window-এ রোজ চলে এবং AutoTask-এর ভেতরে ডিফল্টে চালু। High-frequency টাস্কটির ডিফল্ট OFF; চালু থাকলে এটি ঘণ্টায় একবার চলে, সর্বোচ্চ ৩০ মিনিট, আর এই সময়সূচি পরিবর্তনযোগ্য নয়।

দুটি ব্যাপার আগেভাগে জানা ভালো। Evolve-এর অংশটি আলাদাভাবে বন্ধ করা যায় না, কারণ একটিই scheduler client Automatic SQL Tuning Advisor ও Automatic SPM Evolve Advisor দুটোকেই নিয়ন্ত্রণ করে। আর টাস্কের parameter বদলাতে চাইলে Oracle বলে, task-টির মালিক SYS, তাই কেবল SYS-ই parameter বসাতে পারে।

অডিটের প্রশ্নের আগে জেনে রাখুন: স্বয়ংক্রিয় টাস্কের accept_plans-এর ডিফল্ট true, অর্থাৎ কেউ বোতাম না চেপেও ডেটাবেজ plan উন্নীত করতে পারে। সাধারণত এটিই কাম্য, তবু এটি সচেতন সিদ্ধান্ত হওয়া উচিত।

৯. SPM আসলে কী করছে তা দেখব কীভাবে?

একটি view বেশিরভাগ প্রশ্নের উত্তর দেয়: DBA_SQL_PLAN_BASELINES।

SELECT sql_handle, plan_name, origin,
       enabled, accepted, fixed, reproduced,
       optimizer_cost, created, last_executed
FROM   dba_sql_plan_baselines
ORDER  BY created DESC FETCH FIRST 50 ROWS ONLY;

ORIGIN কলামটি বলে দেয় প্রতিটি plan কীভাবে এসেছে: AUTO-CAPTURE, MANUAL-LOAD-FROM-CURSOR-CACHE, MANUAL-LOAD-FROM-AWR, AUTO-SQLTUNE এবং আরও কয়েকটি। উত্তরাধিকারে পাওয়া ডেটাবেজে ওই এক কলামই সাধারণত পুরো ইতিহাস বুঝিয়ে দেয়।

দুটি কলাম রিপোর্টে ব্যবহারের আগে সতর্কতা চায়। LAST_EXECUTED সরাসরি হালনাগাদ হয় না - Oracle বলে পারফরম্যান্সের কারণে প্রতিটি execution-এর পরপরই এই কলাম আপডেট হয় না। আর auto-capture হওয়া plan-এর workload কলামগুলো ফাঁকা থাকে।

চলমান অবস্থায় দেখতে V$SQL.SQL_PLAN_BASELINE ব্যবহার করুন; এটি বলে দেয় চলমান cursor কোন baseline ব্যবহার করছে। আপনার pin করা statement-এর জন্য এই কলাম খালি থাকলে, pin যা ভাবছেন তা করছে না।

র‍্যাক থেকে অর্ধেক বের করা একটি সার্ভার ব্লেড, তার সারি সারি ছোট সবুজ বাতি জ্বলছে, বাকি র‍্যাক ছায়ায়
Plan-এর ইতিহাস বিনামূল্যে নয়। এটি SYSAUX-এর আসল ব্লক, যার বাজেট ও retention আছে। Photo: panumas nikhomkhai / Pexels

১০. Baseline drop করা ও SYSAUX সামলানো

DROP_SQL_PLAN_BASELINE-এ plan_name বাদ দিলে ওই statement-এর সব plan মুছে যায়; ডিফল্টের অর্থই হলো sql_handle-এ চিহ্নিত statement-এর সঙ্গে যুক্ত সব plan drop করা। ফাংশনটি কতগুলো plan মুছল তা ফেরত দেয়, সেটিই আপনার রসিদ।

শুধু প্রচলন থেকে সরাতে চাইলে drop না করে disable করুন। Drop ফেরানো যায় না; disable যায়।

SMB জায়গার জন্য SYSAUX-এর সঙ্গে ভাগাভাগি করে, তাই Oracle একটি বাজেট দেয়: ডিফল্ট SYSAUX-এর ১০%, যা ১ থেকে ৫০-এর মধ্যে বসানো যায়। এটি ছাদ নয়, বাজেট: সাপ্তাহিক একটি background process SMB-র জায়গা মাপে, সীমা ছাড়ালে alert log-এ সতর্কবার্তা লেখে এবং সীমা বাড়ানো, SYSAUX বড় করা বা পুরনো অবজেক্ট purge করা না হওয়া পর্যন্ত প্রতি সপ্তাহে সতর্ক করতে থাকে। বাড়া কেউ থামায় না।

অব্যবহৃত plan পরিষ্কার হয় টাইমারে: retention-এর ডিফল্ট ৫৩ সপ্তাহ, অর্থাৎ "১ বছর প্লাস ১ সপ্তাহ", যাতে বছরে একবার চলা কোয়েরিও টিকে যায়। এটি ৫ থেকে ৫২৩ সপ্তাহের মধ্যে বসানো যায়।

-- বর্তমান সেটিং
SELECT parameter_name, parameter_value FROM dba_sql_management_config;

-- পরিবর্তন (ADMINISTER SQL MANAGEMENT OBJECT লাগে; দুটিই Oracle-এর নিজের উদাহরণ)
EXECUTE DBMS_SPM.CONFIGURE('space_budget_percent', 30);
EXECUTE DBMS_SPM.CONFIGURE('plan_retention_weeks', 105);

সীমা ছাড়ালে আপনাকে জানানো হবে, তবে জোর গলায় নয়: একটি সাপ্তাহিক background process SMB-র জায়গা মাপে এবং সীমা ছাড়ালে alert log-এ সতর্কবার্তা লেখে। সপ্তাহে একবার, alert log-এ। কেউ alert log না পড়লে কেউ জানবেও না।

SYSAUX maintenance window-এর আগে আরেকটি নির্ভরতা মনে রাখুন: SMB পুরোপুরি SYSAUX-এর ভেতরে থাকায়, ওই tablespace অনুপলব্ধ হলে ডেটাবেজ SQL plan management ও SQL tuning ফিচার ব্যবহার করে না।

১১. কখন SPM ভুল টুল?

SPM plan পুনরুৎপাদন করে। এটি ভালো plan বানায় না, আর বেশিরভাগ খারাপ SPM স্থাপনা এই কথাটি ভুলে যাওয়ার উপরেই দাঁড়ানো। Oracle সীমাটি এক বাক্যে বলে: baseline তখন সাহায্য করতে পারে না যখন কোনো ঘটনা execution plan-এ অপরিবর্তনীয় পরিবর্তন এনেছে, যেমন একটি index drop করা।

উপসর্গSPM কেন ভুল উত্তরবদলে যা করবেন
কোয়েরিটি কখনোই দ্রুত ছিল নাইতিহাসে accept করার মতো ভালো plan-ই নেইStatement টিউন করুন, তারপর ফলটি pin করুন
পুরনো বা অনুপস্থিত পরিসংখ্যানভুল সংখ্যার উপর বানানো plan সর্বত্র জমিয়ে ফেলবেনআগে DBMS_STATS ঠিক করুন
অনুপস্থিত indexFixed plan নতুন index-কে আটকে দিতে পারেঠিকভাবে index করুন, তারপর evolve
যে টেবিল partition হওয়া উচিত তার full scanকয়েকটি খারাপ plan-এর কম খারাপটিকে pin করছেনPartition করুন
"নিরাপদ থাকতে" শত শত statementSYSAUX বাড়ে, আর optimizer আর আপনাকে সাহায্য করতে পারে নাCapture-এ filter দিন; যা সত্যিই গুরুত্বপূর্ণ তাকেই baseline করুন

Literal নিয়ে একটি ফাঁদও আছে। Signature যেহেতু কেবল case ও whitespace-এর জন্য normalize হয়, bind variable-এর বদলে literal দিয়ে SQL বানানো অ্যাপ্লিকেশন প্রতিটি মানের জন্য আলাদা signature তৈরি করবে। Oracle-এর প্রথম শর্ত পুনরাবৃত্ত execution - দুইবারের কম চললে কোনো statement baseline পায় না - তাই একবার আসা মান নিরাপদ।

সমস্যা সেই মানগুলো নিয়ে যেগুলো আবার ফিরে আসে: তার প্রতিটি আলাদা repeatable statement, আর capture প্রতি literal-এর জন্য একটি করে baseline দিয়ে SMB ভরে ফেলবে। সেটি SPM-এর সমস্যা নয়, সেটি টিউনিং ও অ্যাপ্লিকেশন ডিজাইনের সমস্যা।

মনে রাখার নিয়ম: SPM দিয়ে জেতা ফল ধরে রাখুন, জয় বানাতে যাবেন না। আউটেজে pin করুন, দিনের আলোয় কারণ সারান, তারপর plan-টি unfix করে evolution-কে তার কাজ করতে দিন।

প্রশ্নোত্তর

SQL plan management চালু করলে কি ডেটাবেজ ধীর হয়ে যায়?

Baseline ব্যবহার ডিফল্টেই চালু: OPTIMIZER_USE_SQL_PLAN_BASELINES-এর ডিফল্ট true, আর বেশিরভাগ ডেটাবেজ কারও নজরে না পড়েই এভাবে চলে। ভাবার বিষয় automatic capture, OPTIMIZER_CAPTURE_SQL_PLAN_BASELINES, যার ডিফল্ট false। Oracle সতর্ক করে যে এটি চালু করলে অত্যন্ত বেশি সংখ্যক baseline তৈরি হতে পারে, কারণ recursive ও monitoring SQL-ও ধরা পড়ে; তাই চালু করলে DBMS_SPM.CONFIGURE filter ব্যবহার করুন।

SQL plan baseline আর SQL profile-এর পার্থক্য কী?

Baseline হলো accepted plan-এর সেট, আর প্রতিটি plan hint আকারে সংরক্ষিত যা plan-টিকে সম্পূর্ণভাবে নির্দিষ্ট করে। SQL profile হলো ওই statement-এর জন্য বাড়তি পরিসংখ্যান; Oracle বলে profile নিজে কোনো নির্দিষ্ট plan ঠিক করে না, এটি optimizer-এর হিসাব সংশোধন করে। Oracle baseline-কে proactive ও profile-কে reactive বলে, এবং জানায় profile বেশি নমনীয় কারণ এটি optimizer-কে একটিমাত্র plan-এ বেঁধে ফেলে না।

আমার SQL plan baseline উপেক্ষিত হচ্ছে কেন?

DBA_SQL_PLAN_BASELINES-এ তিনটি জিনিস দেখুন: ENABLED, ACCEPTED ও REPRODUCED। Plan প্রার্থী হয় তখনই যখন সেটি enabled এবং accepted দুটোই, আর Oracle-এর নির্বাচন নিয়ম বলে baseline-এ কোনো reproducible plan না থাকলে optimizer নতুন cost-based plan ব্যবহার করে। OPTIMIZER_USE_SQL_PLAN_BASELINES এখনও true কি না দেখুন, আর মনে রাখুন plan বসে থাকে normalized signature-এর বিপরীতে: Oracle signature normalize করে case ও white space-এর জন্য, তাই statement-টি নতুন করে সাজালে সেটি সরে যায় না - কিন্তু literal বদলালে signature বদলায়, অর্থাৎ সেটি আলাদা statement।

একটি নির্দিষ্ট plan কীভাবে pin করব?

DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE দিয়ে cursor cache থেকে ওই plan লোড করুন, sql_id ও কাঙ্ক্ষিত plan_hash_value দুটোই নাম ধরে দিন, আর fixed => 'YES' পাস করুন। Oracle-এর সংজ্ঞায় fixed মানে baseline-টি সময়ের সঙ্গে evolve হবে না এবং fixed plan non-fixed plan-এর চেয়ে অগ্রাধিকার পাবে। এটিকে টুর্নিকেট ভাবুন: পরে যোগ হওয়া নতুন plan আপনি নিজে fixed ঘোষণা না করা পর্যন্ত ব্যবহার হবে না।

Oracle upgrade-এর পর SQL plan baseline কি টিকে থাকে?

Oracle এটিকে baseline-এর অন্যতম ব্যবহার হিসেবেই নথিভুক্ত করেছে। SQL Tuning Guide বলে নতুন optimizer সংস্করণসহ আপগ্রেডে সাধারণত অল্প শতাংশ statement-এর plan বদলায়, আর baseline সম্ভাব্য regression উল্লেখযোগ্যভাবে কমায়। নথিভুক্ত পদ্ধতি হলো পুরনো রিলিজে ভালো plan ধরা, যেমন SQL tuning set-এ, আর নতুন রিলিজে লোড করা; Oracle একটি STS থেকে bulk load-কে আপগ্রেডের পর plan regression ঠেকানোর কার্যকর উপায় বলে।

SQL management base কত SYSAUX জায়গা নেয়?

এর বাজেট ডিফল্টে SYSAUX-এর ১০ শতাংশ, আর DBMS_SPM.CONFIGURE('space_budget_percent', n) দিয়ে তা ১ থেকে ৫০ শতাংশের মধ্যে বসানো যায়। এটি কঠিন সীমা নয়, বাজেট: Oracle বলে একটি সাপ্তাহিক background process SMB-র মোট জায়গা মাপে, নির্ধারিত সীমা ছাড়ালে alert log-এ সতর্কবার্তা লেখে এবং সীমা বাড়ানো, SYSAUX বড় করা বা SQL management object purge করা না হওয়া পর্যন্ত প্রতি সপ্তাহে alert দিতে থাকে। অব্যবহৃত plan retention period-এর পর purge হয়, যার ডিফল্ট ৫৩ সপ্তাহ এবং যা ৫ থেকে ৫২৩ সপ্তাহের মধ্যে বসানো যায়।

যে কোয়েরি কখনোই দ্রুত ছিল না, SPM কি তা ঠিক করতে পারে?

না। SPM আগে থেকে থাকা plan পুনরুৎপাদন করে; ভালো plan উদ্ভাবন করতে পারে না, আর Oracle স্পষ্ট বলে যে কোনো ঘটনা execution plan-এ অপরিবর্তনীয় পরিবর্তন আনলে baseline সাহায্য করতে পারে না, যেমন একটি index drop করা।

ইতিহাসে শুধু খারাপ plan থাকলে সমস্যা উপরের দিকে: পরিসংখ্যান, index বা ডেটা মডেলে। আগে সেটি সারান, তারপর ভালো plan তৈরি হলে SPM দিয়ে ধরে রাখুন।

🗄️ একটিমাত্র কোয়েরি কি আপনার পুরো সকাল আটকে রাখছে?

Plan flip সাধারণত আরও পুরনো কিছুর উপসর্গ: মালিকানাহীন পরিসংখ্যান, কখনও বদলানো হয়নি এমন index, কিংবা নিজের ডিজাইন ছাড়িয়ে যাওয়া একটি টেবিল। আমি ডেটাবেজটি পড়ে সহজ ভাষায় বলে দেব এর কোনটি।

📩 Oracle হেলথ চেক নিন
নাসির উদ্দিন খান — Oracle DBA কনসালট্যান্ট

লেখক পরিচিতি

নাসির উদ্দিন খান একজন Oracle Certified Professional ও CSV-সার্টিফায়েড আইটি কনসালট্যান্ট, ঢাকা, বাংলাদেশ। Oracle ডেটাবেজ প্রশাসন (RAC, Data Guard, RMAN), WebLogic মিডলওয়্যার, ERP সিস্টেম ডিজাইন এবং উৎপাদন, ফার্মাসিউটিক্যাল, ব্যাংকিং ও স্বাস্থ্যসেবা প্রতিষ্ঠানের জন্য AI ইন্টিগ্রেশনে তাঁর ১৮+ বছরের সরাসরি অভিজ্ঞতা।

তথ্যসূত্র ও আরও পড়ুন

উপরের প্রতিটি উদ্ধৃত বাক্য এখানে তালিকাভুক্ত Oracle ডকুমেন্টেশন পৃষ্ঠায় ৪ নভেম্বর ২০২৬-এ পড়া হয়েছে, শেষ তিনটি আবার ৭ নভেম্বর ২০২৬-এ। ডিফল্ট ও সীমা ১৯c পৃষ্ঠা থেকে নেওয়া এবং বর্তমান SQL Tuning Guide-এর সঙ্গে মিলিয়ে দেখা হয়েছে, যেটি Oracle ২৬ai সংস্করণ হিসেবে দেয় - এই দুটি অধ্যায়ের ২৩ai URL সেখানেই পাঠিয়ে দেয়। তাই এখানে দাবি ২৬ai ক্রস-চেকের, ২৩ai-এর নয়; ২৩ai চালালে নিজের রিলিজের পৃষ্ঠা দেখে নিন।

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

সম্পর্কিত লেখা

Plan flip-এ আর সকাল নষ্ট নয়

Oracle পারফরম্যান্স টিউনিং · SQL plan management · AWR ও ASH বিশ্লেষণ · আপগ্রেড পরিকল্পনা। ১৮+ বছরের Oracle অভিজ্ঞতা। বাংলাদেশ ও বিশ্বজুড়ে।

💬