Oracle AWR ও ASH রিপোর্ট পড়া: Wait-Event ট্রাবলশুটিং
"ডেটাবেজ স্লো হয়ে গেছে।" প্রতিটি DBA এই কথা শোনেন, আর আতঙ্কে দেওয়া উত্তর — parameter পরিবর্তন শুরু করা — সাধারণত পরিস্থিতি আরও খারাপ করে। সুশৃঙ্খল উত্তর হলো ডেটাবেজকেই বলতে দেওয়া যে সে কীসের জন্য অপেক্ষা করছে। Oracle-এর AWR ও ASH রিপোর্ট ঠিক এই কাজটিই করে। ব্যাংকিং, ফার্মা ও ERP সিস্টেমে ১৮+ বছরের প্রোডাকশন ফায়ারফাইটিং শেষে আমি শিখেছি যে প্রায় প্রতিটি performance সমস্যা স্পষ্ট হয়ে ওঠে যখন আপনি এই রিপোর্টগুলো সঠিক ক্রমে পড়েন। এই হলো সেই ক্রম।
মূল কথাগুলো
- DB Time-ই হলো tuning-এর মুদ্রা — প্রতিটি AWR বিশ্লেষণ শুরু হয় এই প্রশ্ন দিয়ে: কতটা DB Time ছিল, আর তা কোথায় গেল।
- সংকীর্ণ snapshot window বেছে নিন। ছয় ঘণ্টা জুড়ে থাকা একটি রিপোর্ট ত্রিশ মিনিটের incident-কে গড় করে অদৃশ্য করে দেয় — নতুনদের সবচেয়ে সাধারণ ভুল।
- নির্দিষ্ট ক্রমে পড়ুন: প্রথমে load profile, তারপর top wait events, তারপর SQL ordered by elapsed time। কখনোই wait events দিয়ে শুরু করবেন না।
- AWR, ASH ও ADDM-এর জন্য Diagnostics Pack লাগে (Enterprise Edition-এর অপশন)। Statspack হলো বিনামূল্যের বিকল্প — দুর্বল, কিন্তু সৎ।
- ASH হলো ফায়ার-ড্রিলের হাতিয়ার — সেকেন্ডে-সেকেন্ডে session sample, যা "গত পাঁচ মিনিটে কী হলো" প্রশ্নের উত্তর দেয়, যখন AWR-এর ঘণ্টাভিত্তিক গড় তা লুকিয়ে ফেলে।
- বিখ্যাত সংখ্যাগুলোকে অবিশ্বাস করুন। উচ্চ CPU% প্রায়ই সুস্থতার লক্ষণ, আর buffer cache hit ratio tuning-এর লক্ষ্য হিসেবে প্রায় অকেজো।

১. AWR বনাম ASH — সঠিক লেন্স বেছে নিন
- AWR দুটি snapshot-এর মধ্যেকার কার্যকলাপ সমষ্টিভুক্ত করে (সাধারণত প্রতি ঘণ্টায়)। "সিস্টেমটা আজ বিকেলে স্লো ছিল"-এর জন্য এটি ব্যবহার করুন — trend ও সামগ্রিক load।
- ASH প্রতিটি active session প্রতি সেকেন্ডে একবার sample করে। "দুপুর ২:১৪-তে ৯০ সেকেন্ডের জন্য জমে গিয়েছিল"-এর জন্য এটি ব্যবহার করুন — সংক্ষিপ্ত, তীক্ষ্ণ স্পাইক যা এক ঘণ্টার গড় মসৃণ করে মুছে ফেলবে।
মনে রাখার নিয়ম: ঘণ্টার জন্য AWR, মুহূর্তের জন্য ASH। দুটোই একই instrumentation থেকে তথ্য নেয়; পার্থক্য শুধু resolution-এ। AWR হলো সমষ্টিগত খতিয়ান, ASH হলো সিকিউরিটি-ক্যামেরার ফুটেজ। প্রায় প্রতিটি গুরুতর incident-এ আমি দুটোই ব্যবহার করি, আর এই পুরো শৃঙ্খলাটি বসে আছে সেই বৃহত্তর পদ্ধতির ভেতরে, যা আমি আমার Oracle performance tuning গাইডে বর্ণনা করেছি।
২. AWR ও ASH-এর জন্য কি লাইসেন্স লাগে?
হ্যাঁ। AWR, ASH ও ADDM — তিনটির জন্যই Oracle Diagnostics Pack প্রয়োজন, যা আলাদাভাবে লাইসেন্স করা একটি অপশন এবং শুধুমাত্র Enterprise Edition-এ পাওয়া যায়। Pack-এর লাইসেন্স নেই এমন ডেটাবেজে awrrpt.sql চালানো একটি বাস্তব audit finding — রিপোর্টটি টেকনিক্যালি কাজ করে, কিন্তু আপনি তা ব্যবহারের অধিকারী নন।
Instance নিজে কীসের জন্য লাইসেন্সড বলে মনে করে, তা পরীক্ষা করুন:
SHOW PARAMETER control_management_pack_access;
-- DIAGNOSTIC+TUNING : Diagnostics + Tuning packs
-- DIAGNOSTIC : AWR / ASH / ADDM only
-- NONE : neither — do not run AWR reports
আপনি যদি Standard Edition-এ থাকেন, বা Pack ছাড়া Enterprise Edition-এ থাকেন, তবে সৎ বিকল্প হলো Statspack — AWR-এর পুরোনো, বিনামূল্যের পূর্বপুরুষ। এটি একই ধরনের সমষ্টিগত পরিসংখ্যান PERFSTAT schema-তে ধারণ করে, কিন্তু ASH-এর কোনো সমতুল্য নেই, ADDM নেই, আর কোনো historical session sampling-ও নেই। দুর্বল টুলিং, শূন্য লাইসেন্স ঝুঁকি।
-- Statspack: free on every edition
@?/rdbms/admin/spcreate.sql -- one-time install
EXEC statspack.snap; -- take a snapshot (schedule it)
@?/rdbms/admin/spreport.sql -- report between two snapshots
১৮+ বছর পরে আমার অবস্থান: ডেটাবেজটি যদি ব্যবসার জন্য গুরুত্বপূর্ণ হয়, তবে প্রথম গুরুতর incident-এই Diagnostics Pack তার দাম উশুল করে দেয়। কিন্তু ক্লায়েন্টের কাছে Pack না থাকলে কখনো ভান করবেন না যে আছে — Oracle LMS-এর সাথে সেই কথোপকথন আমি দেখেছি, এবং তা মোটেও সুখকর নয়।
৩. রিপোর্টগুলো সঠিকভাবে তৈরি করা
কারিগরি দিকটা হলো দুটি স্ক্রিপ্ট, যা প্রতিটি Oracle home-এর সাথেই আসে:
-- AWR (pick begin/end snapshot IDs when prompted)
@?/rdbms/admin/awrrpt.sql
-- ASH for a specific window
@?/rdbms/admin/ashrpt.sql
-- list recent snapshots so you can pick the right pair
SELECT snap_id, begin_interval_time
FROM dba_hist_snapshot
ORDER BY snap_id DESC FETCH FIRST 12 ROWS ONLY;
RAC-এ মনে রাখবেন, awrrpt.sql শুধু একটি instance-এর রিপোর্ট দেয়। নির্দিষ্ট instance বেছে নিতে awrrpti.sql এবং cluster-wide global রিপোর্টের জন্য awrgrpt.sql ব্যবহার করুন — দুই-নোডের cluster-এ আমি সাধারণত আগে global রিপোর্ট টানি, তারপর যে instance-টি ব্যথা বহন করেছে সেটিতে ঢুকে খুঁটিয়ে দেখি।
নতুনদের এক নম্বর ভুল: ভুল snapshot window
অন্য যেকোনো জায়গার চেয়ে বেশি AWR বিশ্লেষণ এখানেই মারা যায়। ব্যবহারকারীরা যদি ১০:১৫ থেকে ১০:৪৫ পর্যন্ত চিৎকার করে থাকেন আর আপনি ০৮:০০ থেকে ১৪:০০-এর রিপোর্ট তৈরি করেন, তবে incident-টি ছয় ভাগের এক ভাগে লঘু হয়ে যায় এবং প্রতিটি গড় স্বাভাবিক দেখায়। আপনি সিদ্ধান্তে পৌঁছাবেন "ডেটাবেজ ঠিকই ছিল", অথচ ব্যবসা জানে যে ছিল না।
সমস্যাটিকে ঘিরে থাকা সবচেয়ে সংকীর্ণ snapshot জোড়া বেছে নিন — ব্যথাটুকু ঢেকে রাখা এক ঘণ্টার রিপোর্ট প্রতিবারই ছয় ঘণ্টার রিপোর্টকে হারায়। আর কখনোই ডেটাবেজ restart-এর ওপর দিয়ে window টানবেন না: bounce-এর দুই পাশের AWR delta অর্থহীন, এবং রিপোর্ট নিজেই ছোট হরফে সে সতর্কতা দেয়, যা বেশিরভাগ মানুষ এড়িয়ে যায়।
দুটি setting আছে যেগুলোর মালিকানা উত্তরাধিকারসূত্রে না পেয়ে নিজে নেওয়াই ভালো:
-- Snapshot every 30 min, keep 30 days (minutes)
EXEC DBMS_WORKLOAD_REPOSITORY.MODIFY_SNAPSHOT_SETTINGS(
interval => 30, retention => 43200);
-- Take a manual snapshot right now (before/after a load test)
EXEC DBMS_WORKLOAD_REPOSITORY.CREATE_SNAPSHOT;
ডিফল্ট — ঘণ্টাভিত্তিক snapshot, আট দিনের retention — ততক্ষণই ঠিক আছে যতক্ষণ না মাস-শেষের সমস্যাটি ৩১ তারিখে ঘটে আর আগের মাস-শেষের প্রমাণ ৮ তারিখেই মেয়াদোত্তীর্ণ হয়ে গেছে। ত্রিশ দিনের retention আমাকে একাধিকবার বাঁচিয়েছে।

৪. আমি বাস্তবে যে ক্রমে পড়ি
একটি AWR রিপোর্ট কয়েক ডজন পৃষ্ঠার, আর প্রলোভন হলো সোজা wait events-এ লাফ দেওয়া। আমি প্রতিবার একটি নির্দিষ্ট ক্রমে পড়ি: প্রথমে load profile, তারপর top foreground events, তারপর SQL ordered by elapsed time। Load profile আমাকে বলে আমি কী ধরনের workload দেখছি, events বলে সেটি কীসের জন্য অপেক্ষা করেছে, আর SQL সেকশন বলে কে তা ঘটিয়েছে।
এই ক্রমটি গুরুত্বপূর্ণ, কারণ প্রেক্ষাপটহীন একটি wait event বিভ্রান্ত করে। "DB Time-এর ৪০% জুড়ে db file scattered read" — ইচ্ছাকৃত full scan চালানো একটি data warehouse-এ এর এক অর্থ, আর সকাল ১০টার একটি OLTP order-entry সিস্টেমে সম্পূর্ণ ভিন্ন অর্থ। Load profile-ই সেই প্রেক্ষাপট; এটি এড়িয়ে যাওয়াতেই DBA-রা ভুল জিনিস টিউন করে বসেন।
৫. শুরু করুন শীর্ষ থেকে: DB Time ও Load Profile
প্রথমেই wait events-এ স্ক্রল করবেন না। header পড়ুন। DB Time হলো session-গুলো কাজ করা ও অপেক্ষা করায় ব্যয় করা মোট সময়। একে elapsed time-এর সাথে তুলনা করুন: ৬০ মিনিট elapsed অথচ ৬০০ মিনিট DB Time মানে প্রতি সেকেন্ডে গড়ে দশটি session active ছিল — ভারী concurrency।
DB Time আপনার আগে-পরের মাপকাঠিও বটে। একই business workload-এর জন্য আপনার fix-এর পরে DB Time যদি ৬০০ মিনিট থেকে ২০০-তে নামে, আপনি উন্নতি করেছেন; না নামলে করেননি — parameter যা-ই বলুক।
Load Profile আপনাকে workload-এর আকৃতি জানায় — per second ও per transaction হিসেবে:
- DB Time / sec — গড় active session; "কতটা ব্যস্ত" জানার একক সেরা সংখ্যা।
- Logical ও physical reads — কাজটা memory-তে হচ্ছে নাকি disk-এ?
- Hard parses / sec — উচ্চ মান চিৎকার করে বলে "কোনো bind variable নেই"।
- Redo size, commits, rollbacks — write-এর তীব্রতা ও transaction-এর ধরন।
আমি load profile-কে একই সপ্তাহের দিনের একটি পরিচিত-ভালো baseline-এর সাথেও তুলনা করি। "গত সোমবারের তুলনায় physical reads তিনগুণ" — এটি একটি finding; শুধু "physical reads ৪,২০০/sec" নিজে নিজে কেবল একটি সংখ্যা।
৬. মূল কেন্দ্র: Top Foreground Wait Events
এই সেকশন র্যাঙ্ক করে দেখায় DB Time আসলে কোথায় গেল। শীর্ষের দুই-তিনটি event-ই আপনার সমস্যা — তার নিচের সব কিছুই noise। এগুলো একটি বাক্যের মতো পড়ুন: "ডেটাবেজ তার বেশিরভাগ সময় ____-এর জন্য অপেক্ষায় কাটিয়েছে।" তারপর সেটি ঠিক করুন।
মোট সময়ের মতোই average wait time কলামটিতেও মনোযোগ দিন। ভালো flash storage-এ ০.৪ ms করে দশ লক্ষ single-block read একটি সুস্থ সিস্টেম; ১৫ ms করে এক লক্ষ read একটি storage সমস্যা। একই event, বিপরীত রোগনির্ণয়।

৭. বড় Wait Event-গুলোর মর্মোদ্ধার, একটি একটি করে
বাস্তব প্রোডাকশন সিস্টেমে তালিকার শীর্ষে এই event-গুলোই ওঠে। প্রতিটির জন্য: এর সাধারণ অর্থ কী, এবং এরপর আমি কী পরীক্ষা করি।
db file sequential read
এর অর্থ: ডিস্ক থেকে single-block read — প্রায় সবসময়ই index access (index root/branch/leaf block, তারপর ROWID ধরে table block)। যেকোনো OLTP সিস্টেমে কিছু পরিমাণ সম্পূর্ণ স্বাভাবিক।
এরপর আমি যা পরীক্ষা করি: কোন statement এটি চালাচ্ছে তা জানতে SQL ordered by Reads, তারপর তার plan — চিরাচরিত অপরাধী হলো লক্ষ লক্ষ row জুড়ে TABLE ACCESS BY INDEX ROWID-কে খাওয়ানো একটি index range scan, যেখানে একটি ভালো index বা নতুন করে লেখা predicate block-এর ভগ্নাংশ স্পর্শ করত। SQL নির্দোষ হলে আমি average wait time দেখি: ধারাবাহিকভাবে ~১০ ms-এর ওপরে থাকলে আলোচনাটা storage টিমের দিকে যায়। কেবল তার পরেই আমি buffer cache-এর আকার নিয়ে ভাবি।
db file scattered read
এর অর্থ: buffer cache-এ multi-block read — full table scan ও index fast full scan। reporting workload-এ বৈধ; OLTP window-তে প্রাধান্য বিস্তার করলে সন্দেহজনক।
এরপর আমি যা পরীক্ষা করি: কোন SQL scan করছে (SQL ordered by Gets ও by Reads) এবং scan-টি ইচ্ছাকৃত কি না। রাতারাতি index access থেকে full scan-এ উল্টে যাওয়া plan-এর অর্থ সাধারণত optimizer statistics বদলেছে — বাসি stats, বাঁকা sample-এ নেওয়া নতুন gather, বা যেখানে থাকার কথা নয় সেখানে একটি histogram-এর আবির্ভাব। উপসর্গ নয়, statistics ঠিক করুন।
direct path read
এর অর্থ: buffer cache-কে পাশ কাটিয়ে সরাসরি session-এর PGA-তে multi-block read। parallel query এটি design-গতভাবেই করে; 11g থেকে serial session-ও করে, যখন Oracle সিদ্ধান্ত নেয় যে cache-এর তুলনায় segment-টি "বড়"।
এরপর আমি যা পরীক্ষা করি: কোন table তা জানতে Segments by Direct Physical Reads, তারপর সেই table-টি চুপিসারে adaptive threshold ছাড়িয়ে বেড়ে গেছে কি না — বছরের পর বছর ভালোভাবে চলা একটি query প্রতিবার execution-এ ডিস্ক পেটাতে শুরু করে, কারণ তার block-গুলো আর cache-এ থাকে না। আসল সমাধান হলো SQL, partition pruning, অথবা scan-টিকে মেনে নিয়ে তাকে bandwidth দেওয়া; ভুল সমাধান হলো অন্ধভাবে একটি ২০০ GB table cache করা।
log file sync
এর অর্থ: LGWR-এর redo ডিস্কে flush করার জন্য COMMIT-এ অপেক্ষারত session। এই event প্রতিটি commit-রত session-কে আটকে দেয়, তাই ব্যবহারকারীরা তা সঙ্গে সঙ্গে টের পান।
এরপর আমি যা পরীক্ষা করি: দুটি সংখ্যা। load profile-এ commits per second — প্রতি সেকেন্ডে হাজার হাজার মানে application একটি loop-এর ভেতরে row ধরে ধরে commit করছে, আর পৃথিবীর কোনো storage তাকে বাঁচাতে পারবে না। এবং background event log file parallel write — LGWR নিজেই ধীর হলে (গড় ২–৩ ms-এর ওপরে) redo log-গুলো ধীর বা contended storage-এ বসে আছে। প্রথমটি সারায় application-এ commit batching; দ্বিতীয়টি সারায় redo-কে আপনার হাতে থাকা দ্রুততম, শান্ততম storage-এ সরানো।
enq: TX — row lock contention
এর অর্থ: একটি session একটি row lock ধরে আছে আর অন্যরা তার পেছনে লাইন দিচ্ছে। এটি আসলে ডেটাবেজের পোশাক পরা একটি application-design সমস্যা — online ব্যবহারকারীদের সাথে batch job-এর সংঘর্ষ, unindexed foreign key, অথবা এমন একজন ব্যবহারকারী যিনি একটি form খুলে, একটি row lock করে দুপুরের খাবারে চলে গেছেন।
এরপর আমি যা পরীক্ষা করি: ASH, তৎক্ষণাৎ — blocking_session আমাকে ঠিক বলে দেয় lock-টি কে ধরে রেখেছিল আর ভুক্তভোগীরা কোন SQL চালাচ্ছিল। unindexed foreign-key ফাঁদ ও deadlock নির্ণয়সহ এই জট পাকানোর পূর্ণাঙ্গ শারীরস্থান আছে আমার Oracle locking, blocking ও deadlocks গাইডে।
gc buffer busy / gc cr block busy (RAC)
এর অর্থ: interconnect-এর ওপারে একই block নিয়ে instance-দের লড়াই। RAC-এ একটি hot block শুধু latency সমস্যা নয় — এটি নোডগুলোর মধ্যে পিং-পং খেলে আর বহুগুণ হয়।
এরপর আমি যা পরীক্ষা করি: প্রথমে interconnect-এর স্বাস্থ্য (private network latency এবং RAC statistics সেকশনে lost blocks), তারপর object-টি — চিরাচরিত অপরাধী হলো sequence-চালিত primary key-র index-এর ডান প্রান্ত, যেখানে প্রতিটি instance একই leaf block-এ insert করছে। সমাধানের পরিসর index-টিকে reverse বা hash-partition করা থেকে শুরু করে service-এর মাধ্যমে workload-কে এক নোডে route করা পর্যন্ত। একটি live 19c RAC cluster-এ এটি নির্ণয় করা আমার করা সবচেয়ে তৃপ্তিদায়ক কাজগুলোর একটি।
পার্শ্বচরিত্রেরা
buffer busy waits — memory-তে একই block-এর জন্য session-দের প্রতিযোগিতা; সাধারণত concurrent DML-এর নিচে hot block। cursor: pin S wait on X / library cache lock — parsing contention, আর সমাধান প্রায় সবসময়ই literal SQL-এর বদলে bind variable। শীর্ষে DB CPU — এটি আদৌ কোনো wait নয়, আর স্বয়ংক্রিয়ভাবে কোনো সমস্যাও নয়; নিচের false-leads সেকশনে এ নিয়ে আরও আছে।
৮. Wait থেকে SQL-এর দিকে অনুসরণ করুন
একটি wait event আপনাকে উপসর্গ বলে; SQL সেকশন অপরাধীর নাম বলে। SQL ordered by Elapsed Time এবং by Gets পড়ুন, সবচেয়ে খারাপ SQL_ID সংগ্রহ করুন, এবং এর plan বের করুন:
SELECT * FROM TABLE(
DBMS_XPLAN.DISPLAY_AWR('&sql_id'));
-- or the live plan from cursor cache
SELECT * FROM TABLE(
DBMS_XPLAN.DISPLAY_CURSOR('&sql_id', NULL, 'ALLSTATS LAST'));
এবার গল্পটা সম্পূর্ণ হয়: উচ্চ db file sequential read + একটি top SQL যা লক্ষ লক্ষ row-এর ওপর বিশাল INDEX RANGE SCAN করে TABLE ACCESS BY ROWID-এ যাচ্ছে = index বা query-টির কাজ প্রয়োজন।
৯. ASH: শেষ-পাঁচ-মিনিটের ফায়ার ড্রিল
সমস্যা চলাকালীন যখন ফোন বাজে, আমি কোনো রিপোর্টই তৈরি করি না। আমি সরাসরি v$active_session_history query করি — এটি memory-তে মোটামুটি শেষ এক ঘণ্টার সেকেন্ডে-সেকেন্ডে নেওয়া sample ধরে রাখে। এই হলো আমার আসল ফায়ার-ড্রিল ক্রম, পরপর।
প্রথমে: এই মুহূর্তে সবাই কীসের জন্য অপেক্ষা করছে?
SELECT NVL(event, 'ON CPU') AS activity, COUNT(*) AS samples
FROM v$active_session_history
WHERE sample_time > SYSDATE - 5/1440 -- last 5 minutes
GROUP BY NVL(event, 'ON CPU')
ORDER BY samples DESC FETCH FIRST 10 ROWS ONLY;
দ্বিতীয়ত: এর পেছনে কোন SQL?
SELECT sql_id, NVL(event, 'ON CPU') AS activity, COUNT(*) AS samples
FROM v$active_session_history
WHERE sample_time > SYSDATE - 5/1440
GROUP BY sql_id, NVL(event, 'ON CPU')
ORDER BY samples DESC FETCH FIRST 10 ROWS ONLY;
তৃতীয়ত: কেউ কি সবাইকে block করছে?
SELECT blocking_session, event, COUNT(*) AS samples,
COUNT(DISTINCT session_id) AS victims
FROM v$active_session_history
WHERE sample_time > SYSDATE - 5/1440
AND blocking_session IS NOT NULL
GROUP BY blocking_session, event
ORDER BY samples DESC;
প্রতিটি sample আনুমানিক একটি active session-এর এক সেকেন্ড, তাই count-গুলো সরাসরি ব্যথার সেকেন্ড হিসেবে পড়া যায়। তিনটি query, দুই মিনিটের কম সময় — আর আমি সাধারণত জেনে যাই আমি একটি lock-জট, একটি লাগামছাড়া SQL, নাকি সিস্টেমজোড়া I/O স্থবিরতা দেখছি — যেখানে AWR পুরো নাটকটাই গড় করে মুছে ফেলত।
গতকাল ঘটে যাওয়া কোনো স্পাইকের জন্য একই query-গুলো চলে dba_hist_active_sess_history-এর ওপর — persisted কপি, যা প্রতি সেকেন্ডের বদলে প্রতি দশ সেকেন্ডে sample নেয়। আর একটি historical window-এর জন্য ashrpt.sql এই সবকিছুকে একটি সাজানো রিপোর্টে মুড়ে দেয়।
১০. ADDM-কে দ্বিতীয় মতামত দিতে দিন
Oracle-এর Automatic Database Diagnostic Monitor AWR ডেটা পড়ে এবং সহজ ভাষায় findings লেখে — একটি কাজের sanity check, বিচারবুদ্ধির বিকল্প নয়:
@?/rdbms/admin/addmrpt.sql
ADDM findings-কে যাচাই করার সূত্র হিসেবে দেখুন, অন্ধভাবে মানার আদেশ হিসেবে নয়।

১১. একটি যুদ্ধকাহিনি: সোমবার সকালের স্লোডাউন
এক ফার্মাসিউটিক্যাল ক্লায়েন্টের ERP — যেটি আমি বছরের পর বছর support করেছি — এক সোমবার সকাল ৯টায় গুড়ের মতো জমে গেল। যে order entry সাধারণত দুই সেকেন্ড নিত, তা নিচ্ছিল চল্লিশ। application টিম দোষ দিল ডেটাবেজকে; infrastructure টিম দোষ দিল application-কে; কারও কাছে প্রমাণ ছিল না।
আমি দুটি AWR রিপোর্ট টানলাম: সোমবার ০৯:০০–১০:০০, এবং baseline হিসেবে আগের সোমবারের একই ঘণ্টা। তুলনাটি তিন লাইনে গল্পটা বলে দিল। DB Time চারগুণ হয়েছে। Physical reads আট গুণ বেড়েছে। আর db file scattered read, যা আগের সপ্তাহে প্রায় অদৃশ্য ছিল, এখন DB Time-এর ৬২%।
SQL ordered by Reads একটি statement-কে শীর্ষে বসাল — order-entry স্ক্রিনের ভেতরের একটি query, যা বছরের পর বছর ধরে চলছিল। এর SQL_ID-র জন্য DBMS_XPLAN.DISPLAY_AWR ইতিহাসে দুটি plan দেখাল: শনিবার পর্যন্ত একটি index range scan, রবিবার রাত থেকে একটি full table scan।
রবিবার রাতেই ডিফল্ট statistics-gathering window চলে। একটি skewed কলাম একটি নতুন histogram পেয়ে বসেছিল, optimizer query-টিকে নতুন করে cost করল, আর plan উল্টে গেল — ঠিক সেই failure mode যা আমি DBMS_STATS আর্টিকেলে ব্যবচ্ছেদ করেছি। আমরা DBMS_STATS.RESTORE_TABLE_STATS দিয়ে সেই table-এর আগের statistics ফিরিয়ে আনলাম, plan উল্টে আগের জায়গায় ফিরল, আর কয়েক মিনিটের মধ্যে DB Time baseline-এ ফিরে এল।
মোট নির্ণয়ের সময়: প্রায় পঁচিশ মিনিট, যার বেশিরভাগটাই রিপোর্ট তৈরি হওয়ার অপেক্ষা। কোনো parameter বদলানো হয়নি। AWR আপনাকে এটাই কিনে দেয় — টিমগুলোর মধ্যে তর্ক শেষ হয়ে যায়, কারণ ডেটাবেজ নিজেই সাক্ষ্য দেয়।
১২. আমার ১০-মিনিটের AWR Triage রুটিন
কোনো মতামত গঠনের আগে একটি তাজা AWR রিপোর্টে আমি ঠিক এই ক্রমটি চালাই। দশ মিনিট, এই ক্রমে:
- মিনিট ১ — window-টি sanity-check করুন। snapshot জোড়াটি কি সত্যিই অভিযোগটিকে ঘিরে আছে? মাঝে কোনো restart নেই তো? না মিললে থামুন এবং নতুন করে তৈরি করুন।
- মিনিট ২ — DB Time বনাম elapsed। average active sessions পেতে DB Time-কে elapsed time দিয়ে ভাগ করুন। CPU count-এর সাথে তুলনা করুন: তার অনেক ওপরে মানে সত্যিকারের queuing।
- মিনিট ৩–৪ — load profile, একটি baseline-এর বিপরীতে। প্রতি সেকেন্ডে reads, redo, commits, hard parses। একটি ভালো দিনের তুলনায় কী বদলেছে?
- মিনিট ৫ — শীর্ষ ৫টি foreground event। বাক্যটি লিখুন: "ডেটাবেজ বেশিরভাগ সময় ____-এর জন্য অপেক্ষা করেছে।" শুধু মোট নয়, average wait time-ও টুকে নিন।
- মিনিট ৬–৭ — SQL ordered by Elapsed Time, তারপর by Gets ও by Reads। শীর্ষ দুই-তিনটি SQL_ID সংগ্রহ করুন। DB Time-এর ৪০%+ জুড়ে থাকা একটি statement-ই আপনার উত্তর।
- মিনিট ৮ — plan বের করুন সবচেয়ে খারাপ SQL_ID-র জন্য DBMS_XPLAN.DISPLAY_AWR দিয়ে, আর snapshot জুড়ে plan-এর পরিবর্তন খুঁজুন।
- মিনিট ৯ — Segments statistics ক্রস-চেক করুন (by physical reads, by row lock waits) — object-এর দৃশ্য প্রায়ই SQL-এর দৃশ্যকে নিশ্চিত করে।
- মিনিট ১০ — এক লাইনের hypothesis লিখুন এবং সেটি পরীক্ষা করার একটিমাত্র পরিবর্তন। সেই লাইনটি লিখতে না পারলে, কিছু ছোঁয়ার আগে আমি ASH পড়ি।
১৩. যে মিথ্যা সূত্রগুলো ঘণ্টার পর ঘণ্টা নষ্ট করে
AWR দক্ষতার অর্ধেকটাই হলো জানা — কোন বিখ্যাত সংখ্যাগুলো উপেক্ষা করতে হবে।
- উচ্চ CPU% স্বয়ংক্রিয়ভাবে খারাপ নয়। CPU-তে কার্যকর কাজ করা একটি ডেটাবেজ — এর জন্যই তো আপনি টাকা দিয়েছেন; events-এর শীর্ষে DB CPU আর সন্তুষ্ট ব্যবহারকারী মানে একটি সুস্থ সিস্টেম। সমস্যা তখনই, যখন CPU-র চাহিদা ক্ষমতা ছাড়িয়ে যায় (run queue জমছে, ASH-এ ON CPU প্রাধান্য বিস্তার করছে আর response time বাড়ছে) — তখন যে SQL তা পোড়াচ্ছে সেটি খুঁজুন, আগে core কিনবেন না।
- buffer cache hit ratio প্রায় অকেজো। ৯৯.৯% ratio এমন একটি query আড়াল করতে পারে যা মিনিটে পাঁচ কোটি logical read করছে — logical I/O-ও CPU পোড়ায়। আমি এমন সিস্টেম ভয়ানক থেকে চমৎকারে টিউন করেছি, যেখানে ওই ratio নড়েইনি। ratio নয়, SQL টিউন করুন।
- অতিরিক্ত প্রশস্ত রিপোর্ট — একটি ৪-ঘণ্টার AWR ঘটনাটিকে গড় করে অদৃশ্য করে দেয়। কোনো সিদ্ধান্তে পৌঁছানোর আগে সংকীর্ণ window-তে নতুন করে তৈরি করুন।
- একসাথে অনেক কিছু পরিবর্তন করা — আপনি কখনো জানবেন না কোনটি সাহায্য করল (বা ক্ষতি করল)।
- application উপেক্ষা করা — row-lock contention ও literal SQL হলো code-এর সমস্যা, parameter-এর নয়। loop-এর ভেতরের commit কোনো init.ora setting সারায় না।
- baseline ছাড়া tuning — তুলনার জন্য একটি "ভালো দিনের" AWR (বা DBMS_WORKLOAD_REPOSITORY দিয়ে একটি আনুষ্ঠানিক baseline) রাখুন।
শেষ কথা
যে মুহূর্তে আপনি instrumentation-কে বিশ্বাস করেন, performance tuning অনুমানের খেলা হওয়া বন্ধ করে দেয়। AWR ও ASH উত্তর লুকায় না — উপর থেকে নিচে পড়লে তারা উত্তরটি আপনার হাতে তুলে দেয়: কতটা ব্যস্ত, কীসের জন্য অপেক্ষা, কোন SQL-এর কারণে। স্মৃতি থেকে parameter পরিবর্তনের তাড়না দমন করুন। পরিমাপ করুন, একটি hypothesis গঠন করুন, একটি জিনিস পরিবর্তন করুন, আবার পরিমাপ করুন। এই শৃঙ্খলাই একজন DBA যিনি একটি incident শান্ত করেন আর যিনি তা দীর্ঘায়িত করেন — এই দুইয়ের মধ্যে পুরো পার্থক্য।
যদি আপনার একটি বারবার হওয়া slowdown, মাস-শেষের স্পাইক, বা একটি AWR রিপোর্ট থাকে যেটিতে আরেক জোড়া চোখ চান, চলুন কথা বলি। আমি ব্যাংকিং, ফার্মা ও ERP সিস্টেমের জন্য production Oracle performance নির্ণয় ও টিউন করি এবং দ্রুত আসল bottleneck খুঁজে পেতে আপনাকে সাহায্য করতে পারি।
সাধারণ জিজ্ঞাসা (FAQ)
Oracle-এ AWR এবং ASH-এর মধ্যে পার্থক্য কী?
AWR (Automatic Workload Repository) দুটি snapshot-এর মধ্যেকার সময়ে (সাধারণত এক ঘণ্টার ব্যবধানে) ডেটাবেজ কার্যকলাপের একটি সমষ্টিগত চিত্র দেয় — trend ও সামগ্রিক বিশ্লেষণের জন্য আদর্শ। ASH (Active Session History) প্রতি সেকেন্ডে প্রতিটি active session sample করে এবং সংক্ষিপ্ত, ক্ষণস্থায়ী স্পাইক নির্ণয়ের জন্য আদর্শ, যা এক ঘণ্টার AWR গড়ের আড়ালে ঢাকা পড়ে যায়।
AWR ও ASH ব্যবহারের জন্য কি লাইসেন্স লাগে?
হ্যাঁ — AWR, ASH ও ADDM-এর জন্য Oracle Diagnostics Pack প্রয়োজন, যা শুধুমাত্র Enterprise Edition-এ পাওয়া একটি পেইড অপশন। আপনার instance কী ব্যবহারের জন্য সেট করা আছে তা দেখতে control_management_pack_access প্যারামিটারটি পরীক্ষা করুন। Pack ছাড়া Statspack হলো বিনামূল্যের বিকল্প: একই ধরনের সমষ্টিগত পরিসংখ্যান, কিন্তু কোনো session sampling নেই, ADDM-ও নেই।
AWR-এর জন্য কোন snapshot interval ও retention ব্যবহার করা উচিত?
ডিফল্ট হলো প্রতি ঘণ্টায় snapshot, যা আট দিন সংরক্ষিত থাকে। আমি সাধারণত ঘণ্টাভিত্তিক snapshot রাখি (অস্থির সিস্টেমে ৩০ মিনিট), কিন্তু DBMS_WORKLOAD_REPOSITORY.MODIFY_SNAPSHOT_SETTINGS দিয়ে retention ৩০ দিনে বাড়িয়ে দিই, যাতে মাস-শেষ ও মাস-শুরুর incident-গুলো আগেরবারের ঘটনার সাথে তুলনা করা যায়।
AWR রিপোর্টে DB Time কী?
DB Time হলো সব foreground session মিলিয়ে CPU-তে কাজ করা বা অপেক্ষায় ব্যয় করা মোট সময়। একে elapsed time দিয়ে ভাগ করলে পাওয়া যায় average active sessions — "ডেটাবেজ কতটা ব্যস্ত ছিল" জানার একক সেরা সংখ্যা। এটি যেকোনো fix-এর মাপকাঠিও: একই workload-এর জন্য DB Time না কমলে fix কাজ করেনি।
'db file sequential read' wait event-এর অর্থ কী?
এটি হলো ডিস্ক থেকে single-block read-এর জন্য অপেক্ষায় ব্যয়িত সময়, প্রায় সবসময়ই index access। উচ্চ মান সাধারণত অনুপস্থিত বা অকার্যকর index, ছোট buffer cache, বা প্রয়োজনের চেয়ে অনেক বেশি block পড়া query-র কারণে সৃষ্ট physical I/O নির্দেশ করে। অল্প পরিমাণে এটি স্বাভাবিক; সমস্যা তখনই হয় যখন এটি DB time-এ প্রাধান্য বিস্তার করে।
AWR রিপোর্টে দেখার মতো একক সবচেয়ে খারাপ wait event কোনটি?
সর্বজনীনভাবে সবচেয়ে খারাপ কোনো event নেই — আপনার সিস্টেমে যে event DB Time-এ প্রাধান্য বিস্তার করে, সেটিই সবচেয়ে খারাপ। তবে যেগুলো দেখলে আমি সবচেয়ে দ্রুত সোজা হয়ে বসি সেগুলো হলো enq: TX row lock contention (ব্যবহারকারীরা একে অপরের পেছনে লাইনে দাঁড়িয়ে আছে, আর তা তুষারগোলকের মতো বাড়ে) এবং log file sync (সিস্টেমের প্রতিটি commit আটকে যাচ্ছে)। দুটিই একসাথে সবাইকে আঘাত করে, শুধু একটি রিপোর্টকে নয়।
AWR রিপোর্টে সবচেয়ে খারাপ SQL কীভাবে খুঁজে বের করবেন?
SQL Statistics সেকশনে যান এবং 'SQL ordered by Elapsed Time' ও 'SQL ordered by Gets' পড়ুন। শীর্ষে থাকা যে statement-গুলো DB time-এর সবচেয়ে বড় অংশ গ্রাস করছে, সেগুলোই আপনার tuning লক্ষ্য। SQL_ID সংগ্রহ করুন এবং DBMS_XPLAN দিয়ে এর execution plan বের করে দেখুন সময় কোথায় যাচ্ছে।
🔗 ডেটাবেজ স্লো চলছে?
AWR/ASH বিশ্লেষণ, SQL tuning, এবং root-cause performance নির্ণয়। বিনামূল্যে ৩০ মিনিটের পরামর্শ।
তথ্যসূত্র ও আরও পড়ুন
- 📄 Oracle Database Performance Tuning Guide (19c)
- 📄 Automatic Performance Diagnostics — AWR & ADDM
- 📄 V$ACTIVE_SESSION_HISTORY Reference
এই গাইডটি ব্যাংকিং, ফার্মা ও ERP সিস্টেমে হাতে-কলমে Oracle performance troubleshooting এবং Oracle-এর অফিসিয়াল ডকুমেন্টেশনের ভিত্তিতে তৈরি।
