«সাইট ধীর» প্রায় সবসময় ডেটাবেস পর্যন্ত পৌঁছায়। আর যাচাই সঠিক ক্রমে করলে কয়েক মিনিট নেয়: আগে যা খোদ ধীরগতির সময়েই দেখা যায়, তারপর যা সপ্তাহ ধরে জমছিল।

সমস্যা চলার সময়েই দেখুন

মূল নিয়ম: সবচেয়ে মূল্যবান তথ্য ঠিক তখনই পাওয়া যায় যখন সব ধীর। MySQL-এর জন্য প্রথম কমান্ড:

sudo mysqladmin processlist
mysql -e 'SHOW FULL PROCESSLIST'

PostgreSQL-এর জন্য:

sudo -u postgres psql -c "SELECT pid, state, wait_event, now()-query_start AS dur, query
FROM pg_stat_activity WHERE state != 'idle' ORDER BY dur DESC;"

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

সংযোগ

mysql -e "SHOW GLOBAL STATUS LIKE 'Threads_connected'"
mysql -e "SHOW GLOBAL STATUS LIKE 'Max_used_connections'"
mysql -e "SHOW VARIABLES LIKE 'max_connections'"

Max_used_connections max_connections-এর কাছাকাছি পৌঁছাচ্ছে হলে অ্যাপ্লিকেশন মাঝেমধ্যে «too many connections» প্রত্যাখ্যান পাচ্ছে। আর সীমা বাড়ানো প্রথম কাজ হওয়া উচিত নয়: প্রতিটি সংযোগ মেমরি নেয়, আর যে সার্ভারে মেমরি এমনিতেই কম সেখানে সেটি বাড়ানো কেবল swap পর্যন্ত যাত্রা দ্রুত করে। আগে জানুন সংযোগ মুক্ত হচ্ছে না কেন: সাধারণত কোনো লম্বা কোয়েরি বা অ্যাপ্লিকেশনের দিকে সংযোগ পুলের অনুপস্থিতি।

আর এটি অন্য দিক থেকেও দেখা উচিত: সংযোগে দ্রুত বৃদ্ধি বাড়তে থাকা জনপ্রিয়তা থেকে নয়, ভারী কোনো পাতা পেটানো বট থেকেও আসতে পারে — কোনো অনুসন্ধান, তালিকার ফিল্টার, রিপোর্ট বানানো।

ধীর কোয়েরি

ধীর কোয়েরির লগ ছাড়া এগোনোর জায়গা নেই। MySQL:

SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;

একদিন পরে:

sudo mysqldumpslow -s t -t 10 /var/log/mysql/mysql-slow.log

PostgreSQL, postgresql.conf-এ:

log_min_duration_statement = 1000

তারপর pg_stat_statements সম্প্রসারণ, যা প্রতি কোয়েরির মোট সময় সরাসরি দেখায়:

SELECT calls, round(total_exec_time) AS total_ms, query
FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 10;

খেয়াল রাখুন: গুরুত্বপূর্ণ সবচেয়ে লম্বা কোয়েরি নয়, বরং যার মোট সময় সবচেয়ে বেশি। মিনিটে হাজারবার চলা 50 মিলিসেকেন্ডের কোয়েরি দিনে একবার চলা দশ সেকেন্ডের রিপোর্টের চেয়ে বেশি ক্ষতি করে।

যে সেটিংস সবচেয়ে বেশি ভুলে যাওয়া হয়

MySQL ও MariaDB-র জন্য এটি InnoDB-র বাফার পুলের আকার। ডিফল্ট মান 128 মেগাবাইট আর দশক ধরে বদলায়নি। আর যে সার্ভারে ডেটাবেস কয়েক গিগাবাইট দখল করে, সেখানে এর মানে মেমরির বদলে ডিস্ক থেকে অবিরত পড়া।

mysql -e "SHOW VARIABLES LIKE 'innodb_buffer_pool_size'"

নিবেদিত ডেটাবেস সার্ভারের জন্য যুক্তিসঙ্গত মাপ RAM-এর অর্ধেক; আর যে সার্ভারে ওয়েব সার্ভার ও PHP-ও চলছে তার জন্য এক-চতুর্থাংশ, swap-এ যাতে না যায় সেই হিসেবে। আর এই একটি সেটিংস সাধারণত এক সপ্তাহের কোয়েরি উন্নত করার চেয়ে বেশি দেয়।

দুটি জিনিস যা নীরবে ডিস্ক খেয়ে ফেলে

MySQL-এর বাইনারি লগ। এগুলো প্রতিলিপি আর সময়ের কোনো বিন্দু পর্যন্ত পুনরুদ্ধারের জন্য দরকার, অবিরত বাড়ে, আর ডিফল্টে কখনও কখনও কখনওই মোছে না। যাচাই করতে:

sudo du -sh /var/lib/mysql/*bin.*
mysql -e "SHOW VARIABLES LIKE 'binlog_expire_logs_seconds'"

আপনার এখানে প্রতিলিপি না থাকলে আর সময়ের বিন্দু পর্যন্ত পুনরুদ্ধার ব্যবহার না করলে রাখার মেয়াদ কয়েকটা যুক্তিসঙ্গত দিন করে দিন।

ভুলে যাওয়া প্রতিলিপি স্লটসহ PostgreSQL-এর WAL ফাইল। কম পরিচিত আর বেশি বিপজ্জনক পরিস্থিতি: প্রতিলিপির স্লট থাকলে কিন্তু তার গ্রাহক আর যুক্ত না হলে PostgreSQL বিচ্ছিন্ন হওয়ার মুহূর্ত থেকে প্রতিটি লগ রাখতে বাধ্য — আর ডিস্ক ফুরোনো পর্যন্ত রাখবে।

sudo -u postgres psql -c "SELECT slot_name, active, restart_lsn FROM pg_replication_slots;"

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

আকার ও বৃদ্ধি

SELECT table_schema, round(sum(data_length+index_length)/1024/1024) AS mb
FROM information_schema.tables GROUP BY table_schema ORDER BY mb DESC;

আর কাজের জিনিস বর্তমান সংখ্যা নয়, বরং তার বদলের গতি। যে ডেটাবেস ট্রাফিকে সমানুপাতিক বৃদ্ধি ছাড়াই এক মাসে দ্বিগুণ হয়েছে, সাধারণত তার মানে কোনো এন্ট্রির সারণি লেখা হচ্ছে আর কখনও পরিষ্কার হয় না — সেশন, কাজের ইতিহাস, কোনো প্লাগইনের ঘটনার লগ।

দুই লাইনে নিরাপত্তা

আর ডেটাবেসের কনসোল যখন খোলাই আছে তখন দুটি জিনিস যাচাই করে নেওয়ার মতো। প্রথমত, ডেটাবেস বাইরে থেকে নাগালে কি না (ss -tulpn | grep 3306): তার প্রায় কখনও বাইরের দিকে থাকা উচিত নয়। আর দ্বিতীয়ত, যেসব অ্যাকাউন্ট যেকোনো ঠিকানা থেকে যুক্ত হতে পারে:

mysql -e "SELECT user, host FROM mysql.user"

host = '%'-সহ এন্ট্রির মানে «সব জায়গা থেকে সংযোগ»। আর পোর্টও খোলা থাকলে ডেটাবেস আর ইন্টারনেটের মাঝে কেবল একটা পাসওয়ার্ড দাঁড়িয়ে।

ব্যাকআপ

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

রোজকার নজরদারির জন্য তিনটি সংখ্যাই যথেষ্ট: সংযোগের সংখ্যা, দৈনিক ধীর কোয়েরির সংখ্যা, আর ডেটাবেসের আকার। তিনটিই ধীরে আর পূর্বানুমেয়ভাবে বদলায়, আর এদের যেকোনোটির দ্রুত নড়াচড়া চোখ বোলানোর মতো। এক পাতায় এরা কেমন দেখায় তা নিচের ডেমো দেখায়।