সরাসরি পর্বে যান
পর্ব ০৩ · ৬৭-এর মধ্যে · মডিউল ১

DBMS-এর গঠন — three-schema architecture আর data independence

DBMS Architecture — Schemas, the ANSI/SPARC Three Levels, Data Independence & DBMS Components

Taught in Bangla, this lesson explains schema versus instance, the ANSI/SPARC three-level architecture with a university example, building an external view with CREATE VIEW, logical and physical data independence, DDL, DML, DCL and TCL, the query processor and storage manager, reading the system catalog, and centralized, client-server, three-tier and embedded deployments.

এই পর্বে যা শিখবেন

  • Schema আর instance-এর পার্থক্য, আর কেন এই পার্থক্যই DBMS-এর মূল শক্তি
  • ANSI/SPARC-এর তিন স্তর একটি বিশ্ববিদ্যালয়ের উদাহরণে, আর CREATE VIEW দিয়ে নিজের হাতে external view বানানো
  • Logical ও physical data independence — কোন পরিবর্তন কী ভাঙে, কী ভাঙে না
  • DDL, DML, DCL, TCL; আর একটি কোয়েরি DBMS-এর কোন কোন যন্ত্রাংশ পার হয়
  • System catalog পড়া, আর centralized, client-server, 3-tier ও embedded ডেটাবেসের পার্থক্য

১ · Schema আর instance — নকশা বনাম এই মুহূর্তের অবস্থা

আগের দুই পর্বে দেখেছেন, ডেটাবেস কেন দরকার আর ফাইলে ডেটা রাখার সমস্যাগুলো কী। এবার প্রশ্ন হলো — একটি DBMS ভেতরে কীভাবে সাজানো, যাতে সেই সমস্যাগুলো আর না থাকে? উত্তরের প্রথম ধাপ একটি সরল কিন্তু গভীর ভাগ: নকশা আর ডেটা আলাদা।

পাড়ার মুদি দোকানের বাকির খাতাটি কল্পনা করুন। খাতার প্রথম পাতায় দোকানদার কলাম টেনে লিখে রেখেছেন — “তারিখ, ক্রেতার নাম, জিনিস, দাম, পরিশোধ।” এই কলামের কাঠামো বছরের পর বছর একই থাকে। কিন্তু প্রতিদিন নতুন নতুন লাইন যোগ হয়, পুরোনো বাকি কেটে দেওয়া হয়। কলামের কাঠামো হলো schema; আজকের তারিখে খাতায় যত লাইন আছে, সেটি instance.

মূল ধারণা

Schema = ডেটাবেসের বর্ণনা: কোন টেবিল, কোন column, কোন টাইপ, কোন নিয়ম। এটি ডিজাইনের সময় ঠিক হয়, কদাচিৎ বদলায়। Instance (database state) = কোনো এক মুহূর্তে ডেটাবেসে আসলে থাকা সব সারি। প্রতিটি INSERT, UPDATE বা DELETE instance বদলায়, schema নয়। Schema-কে অনেক সময় intension আর instance-কে extension বলা হয়।

এই কোর্সের বেশিরভাগ উদাহরণ চলবে একটি ছোট অনলাইন দোকানের ডেটাবেসে, যার নাম আমরা দিয়েছি db-shop. এতে পাঁচটি টেবিল: customers (গ্রাহক — customer_id, name, city, phone, joined_on), categories (পণ্যের ধরন), products (পণ্য — product_id, name, category_id, price টাকায়, stock মজুত), orders (অর্ডার — কে, কবে, status) আর order_items (কোন অর্ডারে কোন পণ্য কয়টি, কত দামে)। নিচের editor-এর “নমুনা ডেটা” বাক্স খুলে পুরো ডেটা দেখতে পারেন।

নিচের কোডটি পাতাতেই, আসল SQLite ইঞ্জিনে চলে। প্রতিবার চালালে একদম নতুন একটি ডেটাবেস তৈরি হয়, নমুনা ডেটা ঢোকে, তারপর আপনার লেখা স্টেটমেন্টগুলো একে একে চলে। কোড বদলে দেখতে পারেন; “আগের কোড” বোতাম আসল কোড ফিরিয়ে আনে।

-- instance: কয়টি সারি? schema: কয়টি column?
SELECT COUNT(*) AS row_count FROM products;
SELECT COUNT(*) AS column_count FROM pragma_table_info('products');

INSERT INTO products VALUES (111, 'Tea 400g', 4, 380, 50);

SELECT COUNT(*) AS row_count FROM products;
SELECT COUNT(*) AS column_count FROM pragma_table_info('products');

ফলাফল আসে পাঁচ ভাগে: প্রথমে row_count = 10, column_count = 5; তারপর “✓ INSERT — ১টি সারি বদলেছে”; তারপর row_count = 11, কিন্তু column_count আগের মতোই 5. অর্থাৎ নতুন চা-পাতার সারি ঢুকে instance বদলাল, schema একটুও বদলাল না। pragma_table_info('products') হলো SQLite-এর একটি বিশেষ “টেবিল”, যা products-এর column-এর তালিকা দেয় — অর্থাৎ schema-র তথ্যও DBMS নিজের কাছে টেবিলের মতো রাখে। এই ধারণায় আমরা অংশ ৭-এ ফিরব।

এই ভাগটা কেন এত জরুরি? কারণ অ্যাপ্লিকেশন প্রোগ্রাম লেখা হয় schema দেখে, instance দেখে নয়। “products টেবিলে price নামে একটি column আছে” — এই চুক্তির ওপর ভরসা করে হাজার লাইন কোড লেখা হয়। Instance যতই বদলাক, চুক্তি ঠিক থাকলে কোড চলবে। আর schema বদলানোর দরকার পড়লে কী হবে — এই প্রশ্নের উত্তর দিতেই তৈরি হয়েছে তিন-স্তরের নকশা।

Schema-কে সাধারণত একটি ছোট ছবি দিয়েও দেখানো হয় — বাক্সে টেবিলের নাম আর ভেতরে column-এর তালিকা, একে বলে schema diagram. পর্ব ০৭-এ ER diagram আর পর্ব ১০-এ ER থেকে টেবিল বানানোর নিয়ম দেখবেন; সেগুলো আসলে schema আঁকার আরও সমৃদ্ধ উপায়।

২ · তিন স্তরের নকশা — ANSI/SPARC three-schema architecture

একটি বিশ্ববিদ্যালয়ের কথা ভাবুন। একই শিক্ষার্থীর তথ্য তিন রকম মানুষ তিন রকম চোখে দেখেন। হিসাব শাখা (accounts office) জানতে চায় — কার কত টাকা বকেয়া। রেজিস্ট্রার অফিস জানতে চায় — কে কোন কোর্সে কী গ্রেড পেয়েছে। আর আইটি বিভাগের ডেটাবেস প্রশাসক (DBA) ভাবেন — এই লাখো সারি ডিস্কে কোথায়, কোন index-এ, কোন সার্ভারে।

একটিমাত্র নকশা দিয়ে তিনজনকে খুশি রাখা কঠিন। আবার তিনটি আলাদা ফাইল রাখলে ফিরে আসবে পর্ব ০২-এর সেই পুরোনো সমস্যা — একই তথ্য তিন জায়গায়, তিন রকম। সমাধান হিসেবে ১৯৭০-এর দশকে আমেরিকার মান-নির্ধারণী সংস্থা ANSI-র একটি study group প্রস্তাব করে three-schema architecture: ডেটা থাকবে একবারই, কিন্তু তার বর্ণনা থাকবে তিন স্তরে।

👁️ External level (view level)

প্রতিটি ব্যবহারকারী-দল বা অ্যাপের নিজের external schema — শুধু দরকারি অংশটুকু, দরকারি আকারে। একটি ডেটাবেসে অনেকগুলো external schema থাকতে পারে।

হিসাব শাখার view, রেজিস্ট্রারের view

🧩 Conceptual level (logical level)

পুরো প্রতিষ্ঠানের জন্য একটিমাত্র conceptual schema — সব টেবিল, column, সম্পর্ক আর নিয়ম। ডিস্কের খুঁটিনাটি এখানে নেই।

students, courses, enrollments, fees

💾 Internal level (physical level)

একটিমাত্র internal schema — ডেটা কোন ফাইলে, কোন page-এ, কী ক্রমে, কোন index-সহ রাখা।

ফাইল, page, B+ tree index

নিচের ডায়াগ্রামে একই বিশ্ববিদ্যালয়ের ডেটাবেস ওপর থেকে নিচে দেখুন। “পরে” চাপুন — প্রতিটি স্তরে বর্ণনাটি কেমন, আর কে সেটি দেখেন, খেয়াল করুন।

  1. External: হিসাব শাখাfee_due_viewকার কত বকেয়া
  2. External: রেজিস্ট্রারtranscript_viewকে কী গ্রেড পেল
  3. Conceptual৪টি টেবিলপুরো logical নকশা
  4. Internalফাইল + indexডিস্কে আসল বিন্যাস
fee_due_viewহিসাব শাখা শুধু এটুকু দেখে
student_id │ name           │ term        │ due
2101       │ Arif Hossain   │ 2025-Fall   │ 12000
2203       │ Fahim Karim    │ 2025-Fall   │ 30500
-- নেই: CGPA, গ্রেড, কোর্সের তালিকা
transcript_viewরেজিস্ট্রার শুধু এটুকু দেখে
student_id │ name           │ course_id │ grade
2101       │ Arif Hossain   │ CSE201    │ A
2101       │ Arif Hossain   │ CSE301    │ A-
-- নেই: ফি, বকেয়া, ব্যাংকের তথ্য
Conceptual schemaএকটিমাত্র, পুরো প্রতিষ্ঠানের জন্য
students    (student_id, name, dept_id, batch, cgpa)
courses     (course_id, title, credits)
enrollments (student_id, course_id, term, grade)
fees        (student_id, term, amount, paid)

fee_due_view    = students ⋈ fees,  due = amount − paid
transcript_view = students ⋈ enrollments
Internal schemaDBA আর DBMS দেখে
university.db   page size 4096 bytes
students     → B+ tree, key = student_id
enrollments  → B+ tree, key = (student_id, course_id, term)
idx_students_dept  → index on students(dept_id)
fees         → আলাদা disk-এ (SSD), রাতে backup
  1. External view ১ — হিসাব শাখা। হিসাবরক্ষক দেখেন চারটি column: আইডি, নাম, টার্ম আর বকেয়া। “বকেয়া” (due) কোথাও আলাদা করে রাখা নেই — এটি হিসাব করে বের করা মান। CGPA বা গ্রেড এই view-তে নেই, তাই হিসাব শাখার কেউ ভুল করেও সেগুলো দেখতে বা বদলাতে পারবেন না।
  2. External view ২ — রেজিস্ট্রার। একই শিক্ষার্থী Arif Hossain (2101), কিন্তু এখানে দেখা যাচ্ছে কোর্স আর গ্রেড। টাকা-পয়সার কোনো column নেই। দুই অফিস একই ডেটাবেস ব্যবহার করছে, কিন্তু কেউ অন্যের অংশ দেখছে না — এটাই external level-এর কাজ।
  3. Conceptual schema — পুরো নকশা একবারই। চারটি টেবিল, প্রতিটির primary key (সবুজ)। নামটি থাকে শুধু students-এ, একবার। দুটি view-ই এই টেবিলগুলো জোড়া দিয়ে (⋈, join) বানানো — এই নিয়মটিই external–conceptual mapping. শিক্ষার্থীর নাম বদলালে এক জায়গায় বদলালেই দুই view-তে ঠিক নাম দেখাবে।
  4. Internal schema — ডিস্কে আসল বিন্যাস। একই টেবিলগুলো এখানে ফাইল, ৪০৯৬ বাইটের page আর B+ tree-র ভাষায় বর্ণিত; dept_id-এর ওপর একটি index (সবুজ) আছে, ফি-এর টেবিল দ্রুত SSD-তে। এই খুঁটিনাটি হিসাবরক্ষক বা রেজিস্ট্রার কেউ জানেন না, জানার দরকারও নেই। Conceptual টেবিলকে এই ফাইলে রূপ দেওয়ার নিয়ম হলো conceptual–internal mapping.

ট্রেনের টিকিট কাটার কথা ভাবুন। যাত্রী মোবাইল অ্যাপে দেখেন শুধু তাঁর ট্রেন, তারিখ, আসন আর ভাড়া (external). রেলওয়ের পুরো ব্যবস্থায় আছে সব ট্রেন, সব স্টেশন, সব কোচ আর সব বুকিংয়ের সম্পর্ক (conceptual). আর সার্ভার ঘরে সেই তথ্য কোন যন্ত্রে, কোন ফাইলে, কোন সূচিতে রাখা (internal). যাত্রীর অ্যাপ বদলাতে হয় না যখন রেলওয়ে নতুন সার্ভার কেনে — এটাই তিন স্তরের সুবিধা।

Mapping — স্তরগুলোর মাঝের অনুবাদক

তিনটি স্তর থাকলেই হবে না; একটি স্তরের অনুরোধ পাশের স্তরের ভাষায় অনুবাদ করতে হবে। এই অনুবাদের নিয়মকে বলে mapping. দুটি mapping আছে:

  • External–conceptual mapping: “fee_due_view-এর due মানে fees.amount − fees.paid, আর name আসে students থেকে।” SQL-এ এই mapping লেখা হয় view-এর সংজ্ঞায় — পরের অংশে নিজে লিখবেন।
  • Conceptual–internal mapping: “students টেবিলের সারিগুলো university.db-এর এই page-গুলোতে, student_id ক্রমে।” এটি DBMS নিজে রাখে, আপনি সাধারণত শুধু CREATE INDEX বা storage-এর কিছু option দিয়ে প্রভাব ফেলেন।

Mapping-এর একটি দাম আছে: প্রতিটি কোয়েরিকে স্তর পার হয়ে অনুবাদ হতে হয়, তাতে সামান্য সময় যায়। কিন্তু এই দামের বিনিময়ে যা পাওয়া যায় — স্বাধীনভাবে প্রতিটি স্তর বদলানোর ক্ষমতা — সেটি অনেক বেশি মূল্যবান। অংশ ৪-এ সেটাই দেখব।

১৯৭২ সালে ANSI-র কমিটি X3-এর অধীনে SPARC (Standards Planning and Requirements Committee) ডেটাবেস ব্যবস্থাপনা নিয়ে একটি study group গঠন করে। এই ANSI/X3/SPARC Study Group on Data Base Management Systems ১৯৭৫ সালে একটি অন্তর্বর্তী রিপোর্ট (interim report) প্রকাশ করে, আর ১৯৭৭ সালে চূড়ান্ত রিপোর্ট, যা পরে Dionysios Tsichritzis ও Anthony Klug-এর সম্পাদনায় “The ANSI/X3/SPARC DBMS Framework” নামে ছাপা হয়। সেই রিপোর্টেই external, conceptual আর internal — এই তিন স্তরের নাম স্থির হয়। ANSI/SPARC কোনো নির্দিষ্ট DBMS-এর মান হয়নি, কিন্তু এর ভাষা আজও প্রায় সব ডেটাবেস পাঠ্যবইয়ের ভিত্তি।

৩ · External view হাতে-কলমে — CREATE VIEW

SQL-এ external schema বানানোর প্রধান হাতিয়ার হলো CREATE VIEW. View হলো নাম দেওয়া একটি SELECT কোয়েরি, যাকে পরে টেবিলের মতো ব্যবহার করা যায়। সাধারণ view নিজে কোনো ডেটা রাখে না — প্রতিবার ব্যবহারের সময় মূল টেবিল থেকে হিসাব করে। (পর্ব ২৪-এ view আর materialized view বিস্তারিত দেখবেন।)

ধরুন দোকানের গুদাম (warehouse) বিভাগ শুধু জানতে চায় কোন পণ্য কয়টি মজুত আছে। দাম জানার দরকার তাঁদের নেই, আর দাম দেখালে বরং ঝুঁকি। তাঁদের জন্য একটি external view:

-- গুদামের external view: দাম লুকানো
CREATE VIEW stock_view AS
  SELECT product_id, name, stock
  FROM products;

-- গুদামের লোক শুধু view জানেন, মূল টেবিল নয়
SELECT * FROM stock_view WHERE stock < 10;

প্রথমে “✓ CREATE VIEW”, তারপর তিনটি সারি: 103 · Power Bank · 0, 107 · Saree · 8 আর 110 · Smart Watch · 5. ফলে price column একেবারেই নেই। খেয়াল করুন, গুদামের কোয়েরিটি stock_view-কে ঠিক একটি টেবিলের মতো ব্যবহার করেছে — WHERE দিয়ে ফিল্টারও করেছে। DBMS ভেতরে view-এর সংজ্ঞা বসিয়ে কোয়েরিটিকে products-এর ওপর চালিয়েছে; এটাই external–conceptual mapping-এর কাজ।

এবার হিসাব শাখার view. তাঁরা প্রতিটি অর্ডারের মোট টাকা চান, বাতিল অর্ডার বাদে। “মোট টাকা” কোনো টেবিলে রাখা নেই — order_items-এর quantity × unit_price যোগ করে বের করতে হয়। এখানে JOIN আর GROUP BY ব্যবহার হয়েছে, যেগুলো পর্ব ১৮ ও ১৯-এ ধাপে ধাপে শিখবেন; আপাতত শুধু দেখুন view কীভাবে জটিলতা লুকিয়ে রাখে।

-- হিসাব শাখার external view: প্রতি অর্ডারের মোট টাকা
CREATE VIEW order_totals AS
  SELECT o.order_id, o.order_date,
         SUM(i.quantity * i.unit_price) AS total
  FROM orders o
  JOIN order_items i ON i.order_id = o.order_id
  WHERE o.status <> 'cancelled'
  GROUP BY o.order_id;

SELECT * FROM order_totals WHERE total > 5000;

ফল তিনটি সারি: 1001 · 2025-09-01 · 17400, 1005 · 2025-09-15 · 5050 আর 1009 · 2025-10-08 · 18000. হাতে মিলিয়ে দেখুন: অর্ডার 1001-এ একটি ফোন (15000) আর দুটি হেডফোন (2 × 1200 = 2400) — মোট 17400. অর্ডার 1004 (4500 টাকার শাড়ি) বাতিল, তাই বাদ। হিসাবরক্ষকের কাছে order_totals একটি সাধারণ টেবিল; তিনি জানেনও না যে পেছনে দুটি টেবিল জোড়া লাগছে আর যোগফল হচ্ছে।

View লুকায়, কিন্তু নিজে থেকে নিরাপত্তা দেয় না। গুদামের লোক যদি সরাসরি products টেবিল পড়ার অনুমতি পান, তাহলে দামও দেখতে পাবেন। নিরাপত্তার জন্য view-এর সাথে লাগে অনুমতি নিয়ন্ত্রণ — “শুধু stock_view পড়ার অনুমতি, products নয়।” এটি করা হয় DCL দিয়ে (অংশ ৫), আর বিস্তারিত পর্ব ৫১-এ।

৪ · Data independence — নিচের স্তর বদলালে ওপরের স্তর কি ভাঙে?

তিন স্তর আর mapping-এর আসল উদ্দেশ্য একটাই: data independence. এর অর্থ — এক স্তরের schema বদলালেও তার ওপরের স্তর আর অ্যাপ্লিকেশন প্রোগ্রাম যেন বদলাতে না হয়। শুধু মাঝের mapping বদলালেই চলবে। দুই রকম data independence আছে:

💾 Physical data independence

Internal schema বদলালে conceptual schema আর কোয়েরি অপরিবর্তিত। নতুন index, ফাইলের বিন্যাস বদল, HDD থেকে SSD, ডেটা কয়েকটি ডিস্কে ভাগ করা — সব কিছু conceptual–internal mapping-এর ভেতরে লুকিয়ে যায়।

Relational DBMS-এ প্রায় পুরোপুরি পাওয়া যায়

🧩 Logical data independence

Conceptual schema বদলালে external view আর অ্যাপ অপরিবর্তিত। নতুন column যোগ, একটি টেবিল দুই ভাগ করা, দুই টেবিল এক করা — external–conceptual mapping (view-এর সংজ্ঞা) নতুন করে লিখে লুকাতে হয়।

আংশিক পাওয়া যায় — কিছু পরিবর্তন লুকানোই যায় না

কোন পরিবর্তন কী ভাঙে, নিচের ডায়াগ্রামে একটি একটি করে দেখুন। প্রতিটি ধাপে দোকানের ডেটাবেসে একটি পরিবর্তন করা হচ্ছে; গুদামের stock_view আর অ্যাপের কোয়েরির কী হয়, খেয়াল করুন।

পরিবর্তন আর তার প্রভাব
পরিবর্তন
কোন স্তর বদলাল
কোন mapping বদলাতে হলো
stock_view
অ্যাপের কোয়েরি
  1. নতুন index — physical independence-এর সহজতম উদাহরণ। Index শুধু খোঁজার একটি বাড়তি কাঠামো। কোয়েরির ফল একই থাকে, শুধু দ্রুত আসে। কোয়েরি লেখককে কিছুই জানাতে হয় না; DBMS-এর optimizer নিজেই ঠিক করে index ব্যবহার করবে কি না। একটু পরেই এটি নিজে চালিয়ে দেখবেন।
  2. ডিস্ক আর page size বদল — এখনো physical. ডেটা নতুন ডিস্কে, নতুন আকারের page-এ সরে গেল। কোন সারি কোন বাইটে আছে সব বদলেছে, কিন্তু conceptual schema-য় “products টেবিলে পাঁচটি column” — একই। অ্যাপ কিছু টের পায় না, শুধু হয়তো দ্রুত চলে।
  3. নতুন column — logical, কিন্তু নিরীহ। Conceptual schema বদলাল, তবু stock_view নিজের তিনটি column বেছে নেয়, তাই তার কিছু হয় না। যেসব কোয়েরি column-এর নাম লিখে চায়, সেগুলোও ঠিক থাকে। ব্যতিক্রম শুধু SELECT * — সে হঠাৎ একটি বাড়তি column পাবে, যা অ্যাপের কোড আশা করেনি। পেশাদার কোডে SELECT * এড়ানোর এটি একটি বড় কারণ।
  4. টেবিল ভাঙা — logical independence-এর আসল পরীক্ষা। মজুতের তথ্য আলাদা product_stock টেবিলে সরল। এখন view-এর সংজ্ঞা (mapping) নতুন করে লিখতে হবে: দুই টেবিল জোড়া দিয়ে একই নাম আর একই তিনটি column ফেরত দিতে হবে। তাহলে view ব্যবহারকারী অ্যাপ কিছুই টের পাবে না। কিন্তু যে অ্যাপ সরাসরি products.stock পড়ত, সেটি ভাঙবে। আর view-এর মাধ্যমে লেখা (UPDATE) সবসময় সম্ভব নয় — এখানেই logical independence কঠিন।
  5. Column মুছে ফেলা — কোনো mapping বাঁচাতে পারে না। যে তথ্য আর নেই, তাকে কোনো view-ও তৈরি করতে পারে না। এই পরিবর্তন অ্যাপ বদলাতে বাধ্য করে। শিক্ষা: physical পরিবর্তন প্রায় সবই লুকানো যায়; logical পরিবর্তনের মধ্যে শুধু যেগুলো তথ্য যোগ করে বা সাজানো বদলায়, সেগুলো লুকানো যায়, তথ্য বাদ দেয় এমনগুলো নয়।

Physical independence নিজে চালিয়ে দেখুন — index যোগ, কোয়েরি একই

SQLite-এ EXPLAIN QUERY PLAN লিখে যেকোনো কোয়েরির আগে বসালে DBMS কোয়েরিটি চালায় না, বরং বলে দেয় কীভাবে চালাত। নিচে একই কোয়েরি দুবার পরীক্ষা করা হচ্ছে — index বানানোর আগে আর পরে। কোয়েরির লেখা এক অক্ষরও বদলানো হয়নি।

EXPLAIN QUERY PLAN
  SELECT name, price FROM products WHERE price = 550;

-- internal schema বদলালাম: নতুন index
CREATE INDEX idx_products_price ON products(price);

EXPLAIN QUERY PLAN
  SELECT name, price FROM products WHERE price = 550;

SELECT name, price FROM products WHERE price = 550;

প্রথম plan-এর detail column-এ লেখা SCAN products — অর্থাৎ পুরো টেবিলের প্রতিটি সারি পড়ে দেখা। Index বানানোর পর একই কোয়েরির plan: SEARCH products USING INDEX idx_products_price (price=?) — এখন index-এর সাজানো কাঠামোয় সরাসরি 550 খোঁজা হচ্ছে। শেষ কোয়েরির ফল একটি সারি: SQL Book · 550, index থাকুক বা না থাকুক একই। (id, parent, notused column-এর সংখ্যাগুলো SQLite-এর ভেতরের হিসাব, version ভেদে আলাদা হতে পারে — ওগুলো উপেক্ষা করুন।) ১০টি সারিতে পার্থক্য চোখে পড়বে না; কিন্তু ১ কোটি সারিতে scan মানে কোটি সারি পড়া, আর B+ tree index-এ খোঁজা মানে মাত্র কয়েকটি page — পর্ব ৪০-এ এর হিসাব দেখবেন।

Logical independence — যোগ করা নিরাপদ, মুছে ফেলা নয়

এবার conceptual schema বদলাই। প্রথমে products-এ নতুন একটি column যোগ করি, তারপর গুদামের পুরোনো view আগের মতো চলে কি না দেখি।

CREATE VIEW stock_view AS
  SELECT product_id, name, stock FROM products;

-- conceptual schema বদলাল: নতুন column
ALTER TABLE products ADD COLUMN weight_kg REAL;

-- গুদামের পুরোনো কোয়েরি, এক অক্ষরও না বদলে
SELECT * FROM stock_view WHERE stock < 10;

“✓ CREATE VIEW”, “✓ ALTER TABLE”, তারপর আগের সেই তিনটি সারি — Power Bank · 0, Saree · 8, Smart Watch · 5. নতুন weight_kg column view-তে আসেনি, কারণ view নিজের column-গুলো নাম ধরে বেছে নেয়। গুদামের অ্যাপ এই পরিবর্তনের কথা জানতেও পারল না — logical data independence কাজ করছে।

এবার উল্টোটা: যে column-এর ওপর view নির্ভর করে, সেটিই মুছে ফেলার চেষ্টা। এই কোডে ইচ্ছা করেই একটি error আছে।

CREATE VIEW stock_view AS
  SELECT product_id, name, stock FROM products;

-- view যে column ব্যবহার করে, সেটিই মুছছি
ALTER TABLE products DROP COLUMN stock;

প্রথম স্টেটমেন্ট সফল, দ্বিতীয়টিতে error: error in view stock_view after drop column: no such column: stock. SQLite column মুছে ফেলার পর পুরো schema আবার যাচাই করে, দেখে যে stock_view আর অর্থবহ নেই, তাই পুরো পরিবর্তনটিই বাতিল করে দেয় — টেবিল আগের মতো থাকে। অন্য DBMS-এর আচরণ আলাদা: PostgreSQL-ও view-নির্ভর column মুছতে দেয় না (বলে view-টি নির্ভর করছে), যদি না CASCADE দিয়ে view-সহ মুছতে বলা হয়; আবার কিছু DBMS view-টিকে “অকার্যকর” চিহ্ন দিয়ে রেখে দেয়, যা পরে ব্যবহারের সময় error দেয়। যেভাবেই হোক, তথ্য বাদ দেওয়া পরিবর্তন কোনো mapping লুকাতে পারে না।

কেন physical সহজ, logical কঠিন

Physical পরিবর্তন ডেটার অর্থ বদলায় না — একই সারি, একই মান, শুধু অন্য জায়গায় বা অন্য সূচিতে। তাই DBMS একাই পুরো অনুবাদ করতে পারে। Logical পরিবর্তন ডেটার গঠন ও অর্থ বদলায়, আর অ্যাপ্লিকেশন সেই অর্থের ওপর নির্ভর করে লেখা। View পড়ার দিক অনেকটা বাঁচাতে পারে, কিন্তু view-এর মাধ্যমে লেখা (insert, update) সবসময় নির্দিষ্ট অর্থে অনুবাদ করা যায় না — একে বলে view update problem. এজন্য বাস্তবে logical independence আংশিক।

৫ · DBMS-এর ভাষা — DDL, DML, DCL আর TCL

তিন স্তরের সাথে মিল রেখে DBMS-এর সাথে কথা বলার ভাষাকেও কাজ অনুযায়ী ভাগ করা হয়। SQL একটিমাত্র ভাষা, কিন্তু তার স্টেটমেন্টগুলো চারটি দলে পড়ে:

দলকাজপ্রধান স্টেটমেন্টকী বদলায়
DDL — Data Definition LanguageSchema তৈরি ও বদলCREATE, ALTER, DROP, TRUNCATESchema (আর catalog)
DML — Data Manipulation Languageডেটা পড়া ও বদলSELECT, INSERT, UPDATE, DELETEInstance
DCL — Data Control Languageকার কী অনুমতিGRANT, REVOKEঅনুমতির তালিকা (catalog-এ)
TCL — Transaction Control Languageকাজের একক নিয়ন্ত্রণBEGIN, COMMIT, ROLLBACK, SAVEPOINTকোন পরিবর্তন স্থায়ী হবে

কিছু বইয়ে SELECT-কে আলাদা করে DQL (Data Query Language) বলা হয়, আবার ANSI/SPARC-এর মূল ভাষায় internal স্তরের জন্য আলাদা SDL (storage definition language)-এর কথাও আছে। আধুনিক DBMS-এ SDL আলাদা ভাষা নয় — CREATE INDEX, tablespace বা storage option-এর মতো কিছু DDL স্টেটমেন্টই সেই কাজ করে। পরীক্ষায় এই চার দলের নাম আর উদাহরণ প্রায়ই চাওয়া হয়।

Procedural বনাম declarative DML

DML দুই রকম হতে পারে। Procedural (navigational) DML-এ প্রোগ্রামারকে বলতে হয় কীভাবে ডেটা খুঁজতে হবে — “প্রথম record-এ যাও, পরেরটায় যাও, এই link ধরে অন্য record-এ যাও।” ১৯৬০–৭০-এর দশকের CODASYL network ডেটাবেসে এভাবেই লেখা হতো। Declarative DML-এ বলতে হয় শুধু কী চাই — “৫৫০ টাকার পণ্যগুলোর নাম চাই।” কীভাবে খুঁজবে, সেটি DBMS-এর optimizer ঠিক করে।

-- procedural (navigational) ধারা: কীভাবে খুঁজব, ধাপে ধাপে
open products file
record = FIND FIRST product
while record exists:
    if record.price = 550: print record.name
    record = FIND NEXT product

-- declarative ধারা (SQL): শুধু কী চাই
SELECT name FROM products WHERE price = 550;

দেখলেন তো, অংশ ৪-এ index যোগ করার পর SQL কোয়েরি না বদলেই দ্রুত হয়ে গেল? Procedural কোড হলে সেটা হতো না — প্রোগ্রামার নিজে “ফাইলের শুরু থেকে শেষ পর্যন্ত পড়ো” লিখে রেখেছেন, index এলেও কোড সেটা ব্যবহার করবে না। Declarative ভাষাই physical data independence-কে বাস্তবে সম্ভব করে। এটি পর্ব ০৪-এ data model-এর ইতিহাসে আবার আসবে।

TCL হাতে-কলমে — ভুল করে সব দাম দ্বিগুণ!

একটি transaction হলো কয়েকটি কাজকে একসাথে বাঁধা একক — হয় সবগুলো স্থায়ী হবে, নয়তো কোনোটিই না। নিচে ভুল করে সব পণ্যের দাম দ্বিগুণ করা হলো, তারপর ROLLBACK দিয়ে ফিরিয়ে আনা হলো।

BEGIN;                                  -- TCL
UPDATE products SET price = price * 2;  -- DML (ভুল!)
SELECT name, price FROM products WHERE product_id = 101;
ROLLBACK;                               -- TCL: সব বাতিল
SELECT name, price FROM products WHERE product_id = 101;

ফলাফল: “✓ BEGIN”, “✓ UPDATE — ১০টি সারি বদলেছে”, তারপর Mobile Phone · 30000 — transaction-এর ভেতরে দাম সত্যিই দ্বিগুণ। তারপর “✓ ROLLBACK”, আর শেষ কোয়েরিতে আবার Mobile Phone · 15000. দশটি সারির পরিবর্তন একসাথে মুছে গেল, যেন কিছুই হয়নি। ROLLBACK-এর বদলে COMMIT লিখলে দাম স্থায়ীভাবে দ্বিগুণ থাকত। Transaction-এর পূর্ণ তত্ত্ব — ACID — পর্ব ৪৪-এ।

DCL — কার কী অনুমতি

SQLite একটি ফাইলের ভেতরে চলে, তাই এতে user বা GRANT নেই — ফাইল খুলতে পারলেই সব পারা যায় (অনুমতি নিয়ন্ত্রণ করে অপারেটিং সিস্টেম)। Client-server DBMS-এ DCL খুব গুরুত্বপূর্ণ। নিচের কোডটি PostgreSQL-এর, এই পাতায় চালানো যাবে না:

-- গুদামের জন্য একটি role, শুধু stock_view পড়ার অনুমতি
CREATE ROLE warehouse LOGIN PASSWORD 'change-me';
GRANT SELECT ON stock_view TO warehouse;
REVOKE ALL ON products FROM warehouse;   -- মূল টেবিলে হাত নেই

এভাবেই view (external schema) আর DCL মিলে আসল নিরাপত্তা দেয়: গুদামের লোক দাম দেখতে পান না, কারণ মূল টেবিলে তাঁর অনুমতিই নেই। MySQL-এ একই কাজের লেখা একটু আলাদা (CREATE USER, তারপর GRANT SELECT ON shop.stock_view TO …)।

৬ · DBMS-এর ভেতরের যন্ত্রপাতি — query processor আর storage manager

এবার DBMS প্রোগ্রামটির ভেতরে ঢুকি। বড় যেকোনো DBMS-কে মোটামুটি দুটি বড় যন্ত্রে ভাগ করা যায়, আর দুজনের মাঝে একটি “খাতা”:

🧠 Query processor

Query processor SQL লেখা গ্রহণ করে, বোঝে, পরিকল্পনা করে আর চালায়। তিন অংশ: parser, optimizer, executor.

“কী চাই” থেকে “কীভাবে করব”

🗄️ Storage manager

Storage manager ডিস্ক আর মেমরির মধ্যে ডেটা আনা-নেওয়া, একসাথে অনেকের কাজ আর crash থেকে রক্ষা সামলায়। অংশ: buffer, file, transaction, lock আর recovery manager.

ডেটা নিরাপদে, দ্রুত, ঠিকঠাক

📒 System catalog

Catalog (data dictionary) — সব schema আর mapping-এর metadata: কোন টেবিল, কোন column, কোন index, কার কী অনুমতি। দুই যন্ত্রই প্রতি মুহূর্তে এটি পড়ে।

ডেটাবেসের নিজের খাতা

Storage manager-এর অংশগুলো এক নজরে:

  • Buffer manager — ডিস্কের page-এর কপি মেমরিতে (buffer pool) রাখে। মেমরি ডিস্কের চেয়ে বহু গুণ দ্রুত, তাই একই page বারবার ডিস্ক থেকে পড়া এড়ানোই এর কাজ। জায়গা ফুরোলে কোন page সরাবে তাও ঠিক করে (পর্ব ৩৯)।
  • File manager — ডিস্কে ডেটাবেস ফাইলের জায়গা বরাদ্দ করে আর নির্দিষ্ট page পড়ে-লেখে।
  • Transaction manager — প্রতিটি transaction-এর শুরু, COMMIT আর ROLLBACK-এর হিসাব রাখে।
  • Lock manager — একসাথে অনেকে একই ডেটায় কাজ করলে কে কখন হাত দিতে পারবে, lock দিয়ে ঠিক করে (পর্ব ৪৬)।
  • Recovery manager — log রাখে, যাতে বিদ্যুৎ চলে গেলেও COMMIT হওয়া কাজ না হারায় আর অর্ধেক কাজ মুছে যায় (পর্ব ৪৮)।

এবার একটি কোয়েরিকে এই যন্ত্রপাতির ভেতর দিয়ে ধাপে ধাপে পাঠাই। ধরুন idx_products_price index আগেই বানানো আছে, আর অ্যাপ জানতে চাইছে ৫৫০ টাকার পণ্য কোনগুলো।

  1. অ্যাপSQL লেখাপ্রশ্ন পাঠায়
  2. Parserparse treeব্যাকরণ + catalog
  3. Optimizerquery planসস্তা পথ বাছাই
  4. Executor + Txn/Lockplan চালানোread lock নিয়ে
  5. Buffer managerbuffer poolমেমরিতে আছে?
  6. File managerdisk pageডিস্ক থেকে পড়া
  7. ফলSQL Bookঅ্যাপে ফেরত
প্রতিটি ধাপে
এখন কার হাতে
হাতে কী আছে
Catalog থেকে যা লাগল
Buffer pool
  1. অ্যাপ প্রশ্ন পাঠাল। DBMS-এর কাছে এটি এখনো শুধু একটি লেখা (string) — মানুষের কাছে অর্থবহ, যন্ত্রের কাছে এখনো অক্ষরের সারি। অ্যাপ বলেছে কী চাই, কীভাবে নয়।
  2. Parser — ব্যাকরণ আর catalog যাচাই। Parser লেখাটিকে শব্দে ভাঙে, ব্যাকরণ মেলায় (FROM-এর আগে SELECT ঠিক আছে কি না), তারপর একটি গাছের মতো কাঠামো (parse tree) বানায়। এরপর catalog খুলে দেখে products টেবিল আর name, price column সত্যিই আছে কি না, আর এই ব্যবহারকারীর পড়ার অনুমতি আছে কি না। টেবিলের নাম ভুল হলে এখানেই no such table error আসে। View থাকলে এই পর্যায়েই view-এর সংজ্ঞা বসিয়ে দেওয়া হয় — external–conceptual mapping.
  3. Optimizer — সবচেয়ে সস্তা পথ। একই প্রশ্নের উত্তর দুইভাবে বের করা যায়: পুরো টেবিল scan, অথবা price-এর index দিয়ে সরাসরি খোঁজা। Optimizer catalog থেকে জানে কোন index আছে আর টেবিলে আনুমানিক কত সারি, তারপর প্রতিটি পথের খরচ অনুমান করে index-এর পথ বেছে নেয়। এখানেই conceptual–internal mapping ব্যবহার হয়। (পর্ব ৪৩-এ cost-এর হিসাব।)
  4. Executor চালাতে শুরু করল — transaction আর lock. পড়া শুরুর আগে transaction manager একটি read transaction খোলে, আর lock manager পড়ার অনুমতি (shared lock) দেয়, যাতে পড়ার মাঝখানে অন্য কেউ ডেটা বদলে অর্ধেক-লেখা অবস্থা দেখিয়ে না ফেলে। SQLite (rollback journal mode-এ) পুরো ডেটাবেস ফাইলে SHARED lock নেয়; PostgreSQL বা InnoDB সাধারণত পড়ার জন্য lock-ই নেয় না, বরং MVCC দিয়ে একটি স্থির “ছবি” দেখায় (পর্ব ৪৭)। এরপর executor index-এর প্রথম page চায়।
  5. Buffer manager — আগে মেমরিতে খোঁজা। Index-এর root page সম্প্রতি অন্য কোয়েরি ব্যবহার করেছে, তাই buffer pool-এ আছে — একে বলে hit, ডিস্কে যেতে হলো না। কিন্তু যে leaf page-এ 550 আছে, সেটি নেই — miss. তাই buffer manager file manager-কে ডাকে।
  6. File manager — ডিস্ক থেকে page আনা। Catalog থেকে জানা ফাইল আর page size দিয়ে file manager হিসাব করে page-টি ফাইলের কোন জায়গায় (SQLite-এ page নম্বর n থাকে (n − 1) × 4096 বাইট দূরে), তারপর পুরো page একবারে পড়ে buffer pool-এ রাখে। ডিস্ক থেকে পড়া পুরো যাত্রার সবচেয়ে ধীর অংশ, তাই DBMS-এর খরচ সাধারণত গোনা হয় “কয়টি page পড়তে হলো” দিয়ে।
  7. ফল ফেরত। Leaf page থেকে মূল সারির ঠিকানা পেয়ে executor সারিটি আনে (সেটির page-ও একইভাবে buffer হয়ে আসে), plan অনুযায়ী name বেছে নিয়ে অ্যাপে পাঠায়: SQL Book. Transaction শেষ, lock ছেড়ে দেওয়া হলো। এটি শুধু পড়া ছিল, তাই recovery manager-এর কিছু লিখতে হয়নি — UPDATE হলে পরিবর্তনের আগে log-এ লেখা হতো।

এই যাত্রার প্রতিটি ধাপ পরের মডিউলগুলোর এক-একটি বিষয়: parser আর optimizer — পর্ব ৪২–৪৩; buffer আর page — পর্ব ৩৯; index — পর্ব ৪০–৪১; transaction, lock আর recovery — মডিউল ৭. এখন শুধু মানচিত্রটি মাথায় রাখুন।

আরও গভীরে: SQLite-এর ভেতরের নকশা পাঠ্যবইয়ের ছবির খুব কাছাকাছি, কিন্তু একটি মজার মোড় আছে — এর “code generator” প্রতিটি SQL কোয়েরিকে একটি ছোট bytecode প্রোগ্রামে বদলায়, আর একটি virtual machine (VDBE) সেটি চালায়। SQLite-এ শুধু EXPLAIN (QUERY PLAN ছাড়া) লিখলে সেই bytecode-এর নির্দেশগুলোই দেখা যায়। PostgreSQL-এ parser-এর পরে একটি “rewriter” ধাপ আছে, যেখানে view-এর সংজ্ঞা বসানো হয় — অর্থাৎ external–conceptual mapping-এর কাজের নিজস্ব একটি যন্ত্রাংশ। আর PostgreSQL-এ DDL-ও transaction-এর অংশ (BEGIN; CREATE TABLE …; ROLLBACK; করলে টেবিল তৈরিই হয় না), যেখানে MySQL-এ DDL চালালে আগের খোলা transaction নিজে থেকে COMMIT হয়ে যায়।

৭ · System catalog — ডেটাবেস নিজের কথা নিজেই জানে

একটি লাইব্রেরির কথা ভাবুন। বইগুলো তাকে থাকে, কিন্তু কোন বই কোন তাকে, কোন লেখকের, কয় কপি — এই তথ্য থাকে আলাদা ক্যাটালগে। মজার ব্যাপার, ক্যাটালগটিও নিজে একটি বই বা কার্ডের বাক্স, লাইব্রেরিরই অংশ। Relational DBMS-ও ঠিক এভাবে চলে: schema-র সব তথ্য রাখা হয় টেবিলের আকারে, যাকে সাধারণ SQL দিয়েই পড়া যায়।

SQLite-এ এই catalog-এর নাম sqlite_master (নতুন version-এ sqlite_schema নামেও ডাকা যায়)। এর প্রতিটি সারি একটি টেবিল, index, view বা trigger.

-- ডেটাবেসে কী কী আছে? catalog-কে জিজ্ঞেস করি
SELECT name, type FROM sqlite_master;

আটটি সারি আসে, এই ক্রমে: customers · table, sqlite_autoindex_customers_1 · index, categories · table, sqlite_autoindex_categories_1 · index, products · table, orders · table, order_items · table, sqlite_autoindex_order_items_1 · index. পাঁচটি টেবিল তো চেনা, কিন্তু তিনটি index কে বানাল? কেউ CREATE INDEX লেখেনি! customers.phone-এ UNIQUE, categories.name-এ UNIQUE আর order_items-এর দুই-column primary key — এই নিয়মগুলো দ্রুত যাচাই করতে SQLite নিজে থেকে index বানিয়েছে। products-এর INTEGER PRIMARY KEY-এর জন্য আলাদা index লাগেনি, কারণ SQLite-এ সেটিই সারির নিজস্ব key (rowid). Internal schema-র এই সিদ্ধান্তগুলো DBMS নিজেই নেয় — physical independence-এর আরেকটি রূপ।

একটি নির্দিষ্ট টেবিলের column-গুলোর বিস্তারিত দেখতে SQLite-এ আছে PRAGMA table_info:

PRAGMA table_info(products);

ছয়টি column-এর একটি টেবিল: cid (column-এর ক্রমিক নম্বর, 0 থেকে), name, type, notnull (1 মানে NOT NULL), dflt_value (default মান) আর pk (primary key-র অংশ কি না)। পাঁচটি সারি: product_id · INTEGER · pk 1; name · TEXT · notnull 1; category_id · INTEGER; price · REAL · notnull 1; আর stock · INTEGER · notnull 1 · dflt_value 0. প্রতিটি মান হুবহু CREATE TABLE products-এর সংজ্ঞা থেকে এসেছে। Parser এই তথ্য দেখেই বোঝে price নামে column আছে কি না।

Catalog-এর নাম আর গঠন প্রতিটি DBMS-এ আলাদা, কিন্তু SQL মান (SQL-92 থেকে) একটি সাধারণ রূপ দিয়েছে: INFORMATION_SCHEMA. PostgreSQL, MySQL আর SQL Server-এ এটি আছে; SQLite আর Oracle-এ নেই (Oracle-এর নিজস্ব data dictionary view আছে, যেমন USER_TABLES, ALL_TAB_COLUMNS). নিচের কোড PostgreSQL-এর, এই পাতায় চলবে না:

-- মান অনুযায়ী: information_schema (MySQL, SQL Server-এও আছে)
SELECT column_name, data_type, is_nullable
FROM information_schema.columns
WHERE table_name = 'products'
ORDER BY ordinal_position;

-- PostgreSQL-এর নিজস্ব catalog: pg_catalog
SELECT relname, relkind, reltuples
FROM pg_catalog.pg_class
WHERE relname LIKE 'products%';

প্রথম কোয়েরি PRAGMA table_info-এর মতোই column-এর তালিকা দেয়, কিন্তু যেকোনো মান-মেনে চলা DBMS-এ চলে। দ্বিতীয়টি PostgreSQL-এর ভেতরের pg_class টেবিল পড়ে — relkind-এ r মানে সাধারণ টেবিল, i মানে index, v মানে view; আর reltuples হলো optimizer-এর জন্য রাখা আনুমানিক সারির সংখ্যা (ANALYZE বা autovacuum চালালে হালনাগাদ হয়)। অর্থাৎ optimizer যে statistics দেখে সিদ্ধান্ত নেয় (অংশ ৬-এর ধাপ ৩), সেটিও catalog-এই থাকে।

NID ডেটাবেসের কথা ভাবুন। কোটি মানুষের তথ্য আছে, কিন্তু তার পাশাপাশি থাকতে হয় একটি “নিয়মের খাতা” — প্রতিটি রেকর্ডে কোন কোন ঘর, জন্মতারিখ কোন ফরম্যাটে, কোন অফিস কোন অংশ দেখতে পারবে। রেকর্ডগুলো হলো instance, আর নিয়মের খাতা হলো catalog. খাতা হারিয়ে গেলে রেকর্ডগুলো থাকলেও কেউ জানবে না কোন সংখ্যার কী মানে।

Catalog কেন টেবিলে রাখা হয়? কারণ তাহলে আলাদা কোনো ভাষা বা টুল লাগে না — একই SQL, একই query processor, একই নিরাপত্তা ব্যবস্থা। ডেটাবেস ব্যবস্থাপনার টুল (DBeaver, pgAdmin), ORM আর migration টুল (পর্ব ৫০) সবাই এভাবেই catalog পড়ে জানে ডেটাবেসে কী আছে। Codd ১৯৮৫ সালে relational DBMS-এর যে ১২টি নিয়ম প্রকাশ করেন, তার চতুর্থটিই হলো — catalog-ও relational হতে হবে, সাধারণ ব্যবহারকারী একই ভাষায় সেটি পড়তে পারবেন।

৮ · DBMS কোথায় চলে — centralized, client-server, embedded আর distributed

এতক্ষণ দেখলাম DBMS-এর ভেতরের গঠন। এবার প্রশ্ন — DBMS, অ্যাপ আর ব্যবহারকারী কোন কম্পিউটারে বসে, আর কীভাবে একে অপরের সাথে কথা বলে? এই বিন্যাসকেও architecture বলা হয়।

DBMS, অ্যাপ আর ব্যবহারকারীর টার্মিনাল — সব একটি বড় কম্পিউটারে (mainframe). ১৯৬০–৮০-এর দশকে ব্যাংক, বিমা আর সরকারি দপ্তরের ব্যবস্থা এমনই ছিল: সাধারণ “টার্মিনাল” শুধু পর্দা আর কিবোর্ড, সব কাজ হতো কেন্দ্রীয় যন্ত্রে। সুবিধা: একটাই যন্ত্র, নিয়ন্ত্রণ সহজ। অসুবিধা: যন্ত্র বন্ধ মানে সব বন্ধ, আর বাড়াতে হলে আরও বড় (ও দামি) যন্ত্র কিনতে হয়।

Client-server ব্যবস্থায় DBMS চলে একটি আলাদা সার্ভার প্রোগ্রাম হিসেবে (MySQL, PostgreSQL, Oracle, SQL Server), আর অ্যাপ (client) নেটওয়ার্কে সংযোগ করে SQL পাঠায়। 2-tier-এ অফিসের প্রতিটি কম্পিউটারে একটি desktop সফটওয়্যার বসানো থাকে, যা সরাসরি ডেটাবেস সার্ভারে যুক্ত হয়। অনেক অফিসের পুরোনো হিসাব বা ইনভেন্টরি সফটওয়্যার এভাবে চলে। সমস্যা: প্রতিটি কম্পিউটারে ডেটাবেসের পাসওয়ার্ড থাকে, আর নিয়ম বদলালে সব কম্পিউটারে সফটওয়্যার আবার বসাতে হয়।

3-tier নকশায় মাঝখানে একটি application server বসে: ব্রাউজার বা মোবাইল অ্যাপ → application server (PHP, Node.js, Java, Python) → database server. ব্যবহারকারী কখনো সরাসরি ডেটাবেসে পৌঁছান না; ডেটাবেসের পাসওয়ার্ড থাকে শুধু সার্ভারে। আজকের প্রায় সব ওয়েবসাইট, ই-কমার্স আর মোবাইল ব্যাংকিং ব্যবস্থা এই নকশায় চলে। নিচের ডায়াগ্রামে একটি অনুরোধের যাত্রা দেখুন।

Embedded database আলাদা সার্ভার ছাড়াই অ্যাপের ভেতরে একটি library হিসেবে চলে, আর ডেটা রাখে একটি সাধারণ ফাইলে। সবচেয়ে বিখ্যাত উদাহরণ SQLite — ২০০০ সালে D. Richard Hipp এটি তৈরি করেন। প্রতিটি Android আর iPhone-এ SQLite আছে; WhatsApp-এর মতো অ্যাপ ফোনের ভেতরের ডেটা রাখতে, আর Chrome, Firefox, Safari ব্রাউজার ইতিহাস ও অন্য ডেটা রাখতে SQLite ব্যবহার করে। এই পাতার live editor-ও আসলে আপনার ব্রাউজারের ভেতরে চলা একটি embedded SQLite!

Distributed database-এ ডেটা নেটওয়ার্কে যুক্ত অনেকগুলো কম্পিউটারে ভাগ করে (partitioning/sharding) বা কপি করে (replication) রাখা হয়, কিন্তু ব্যবহারকারীর কাছে মনে হয় একটিই ডেটাবেস — এটিও একধরনের independence (“distribution transparency”). পর্ব ৫৪-এ partitioning, sharding আর CAP, আর পর্ব ৬১-এ cloud database ও DBaaS — যেখানে সার্ভার চালানোর দায়িত্বও সেবাদাতার।

  1. Tier 1: ব্রাউজারGET /ordersব্যবহারকারী
  2. Tier 2: App serverNode / PHP / Javaনিয়ম আর যাচাই
  3. Tier 3: DatabasePostgreSQL / MySQLডেটা
অনুরোধটি কোথায়, কে কী জানে
অনুরোধ এখন
যা পাঠানো হচ্ছে
ডেটাবেসের পাসওয়ার্ড কোথায়
  1. ব্রাউজার অনুরোধ পাঠাল। Rahim Uddin (customer_id 1) দোকানের ওয়েবসাইটে “আমার অর্ডার” চাপলেন। ব্রাউজার SQL পাঠায় না — পাঠায় একটি HTTP অনুরোধ আর লগইনের প্রমাণ (cookie). ব্রাউজার জানেও না ডেটাবেস কোথায়।
  2. App server যাচাই করল। Application server লগইন যাচাই করে বুঝল ইনি customer 1, আর ঠিক করল তিনি শুধু নিজের অর্ডার দেখতে পারবেন। ব্যবসার নিয়ম (business logic) থাকে এই মাঝের স্তরে — নিয়ম বদলালে শুধু সার্ভারের কোড বদলাতে হয়, কারও কম্পিউটারে কিছু বসাতে হয় না।
  3. App server ডেটাবেসে SQL পাঠাল। সার্ভার নিজের কাছে রাখা ডেটাবেস পাসওয়ার্ড দিয়ে সংযোগ করে, আর customer_id-কে আলাদা parameter হিসেবে পাঠায় (prepared statement) — ব্যবহারকারীর লেখা সরাসরি SQL-এ জোড়া হয় না, এটাই SQL injection ঠেকানোর মূল উপায় (পর্ব ৪৯ ও ৫১)।
  4. ডেটাবেস ফল দিল। DBMS অংশ ৬-এর সেই যাত্রা শেষ করে customer 1-এর তিনটি অর্ডার ফেরত দিল — db-shop-এ 1001 (delivered), 1003 (shipped), 1009 (pending).
  5. ব্রাউজারে উত্তর। App server সারিগুলোকে HTML বা JSON-এ সাজিয়ে ব্রাউজারে পাঠাল। পুরো যাত্রায় ব্রাউজার একবারও ডেটাবেস দেখেনি, পাসওয়ার্ডও পায়নি। 2-tier-এ এই নিরাপত্তা-দেয়ালটি থাকে না।
ArchitectureDBMS কোথায়ভালো যেখানেউদাহরণ
Centralizedএকটি বড় কেন্দ্রীয় যন্ত্রেপুরোনো ব্যাংকিং, ব্যাচ প্রসেসিংIBM mainframe-এ IMS, Db2
2-tier client-serverআলাদা সার্ভারে; client সরাসরি যুক্তছোট অফিসের LAN, অল্প ব্যবহারকারীdesktop অ্যাপ + SQL Server/Oracle
3-tierআলাদা সার্ভারে; শুধু app server যুক্তওয়েব ও মোবাইল অ্যাপ, বহু ব্যবহারকারীব্রাউজার → Node/PHP → PostgreSQL/MySQL
Embeddedঅ্যাপের ভেতরে, একটি ফাইলেফোন, ব্রাউজার, desktop অ্যাপ, ছোট যন্ত্রSQLite
Distributed / cloudঅনেক যন্ত্রে ভাগ করা বা কপি করাবিশাল ডেটা, সারা দুনিয়ার ব্যবহারকারীপর্ব ৫৪ ও ৬১

৯ · AI-এর যুগে — schema আর architecture বুঝে AI ব্যবহার

AI এখন মুহূর্তেই SQL কোয়েরি, view আর এমনকি পুরো ডেটাবেস ডিজাইন লিখে দেয়। কিন্তু AI আপনার ডেটাবেসের catalog দেখতে পায় না — সে অনুমান করে। তাই AI-এর লেখা কোয়েরি প্রায়ই চলে, কিন্তু ভুল উত্তর দেয়: NULL ভুলভাবে সামলায়, JOIN-এ সারি দ্বিগুণ করে ফেলে, এমন column-এর নাম বানায় যা নেই, index বা transaction-এর কথা ভাবে না। এই পর্বের ধারণাগুলোই সেই ফাঁক ধরার হাতিয়ার।

AI অনুমান করে schema; catalog জানে আসল schema.
আগে catalog থেকে schema দিন, তারপর প্রশ্ন করুন।

AI সাধারণত যেসব ভুল করে

AI-এর ভুলকীভাবে ধরবেন
Column বা টেবিলের নাম বানিয়ে লেখে (customer_name, অথচ আছে name)PRAGMA table_info বা information_schema.columns-এর সাথে মেলান; চালালেই no such column
JOIN-এর পরে SUM — সারি দ্বিগুণ হয়ে যোগফল ফুলে যায়JOIN-এর আগে ও পরে COUNT(*) তুলনা করুন; একটি ফল হাতে হিসাব করুন
NULL ভুলে যায় (city = NULL, বা NULL-ওয়ালা সারি নিঃশব্দে বাদ)NULL আছে এমন সারি (যেমন customer 5-এর phone, customer 7-এর city) দিয়ে পরীক্ষা
অ্যাপের কোডে SELECT * — schema বদলালে ভাঙেদরকারি column নাম ধরে লিখতে বলুন, বা একটি view দিন
অন্য DBMS-এর syntax মিশিয়ে ফেলে (TOP, AUTO_INCREMENT, information_schema SQLite-এ)প্রম্পটে DBMS আর version লিখুন
“দ্রুত করো” বললে কোয়েরি ঘুরিয়ে লেখে, index-এর কথা বলে নাEXPLAIN QUERY PLAN / EXPLAIN দিয়ে আগে-পরে plan দেখুন

AI-কে প্রশ্ন করার আগে catalog থেকে আসল schema বের করে দিন (SQLite-এ SELECT sql FROM sqlite_master; সব CREATE স্টেটমেন্ট দেয়)। তারপর নিচের কাঠামোয় প্রশ্ন করুন:

আমি SQLite 3.4x ব্যবহার করছি। আমার ডেটাবেসের আসল schema নিচে দিলাম (sqlite_master থেকে নেওয়া): [এখানে CREATE TABLE স্টেটমেন্টগুলো পেস্ট করো] আমার দোকানের হিসাব শাখার জন্য একটি VIEW দরকার: প্রতিটি অর্ডারের মোট টাকা, বাতিল অর্ডার বাদে। ১. শুধু উপরের schema-র column ব্যবহার করো; কোনো column নিজে বানিয়ো না। ২. JOIN-এর কারণে সারি দ্বিগুণ হতে পারে কি না, ব্যাখ্যা করো। ৩. কোনো column-এ NULL থাকলে ফল কী হবে, বলো। ৪. ফল যাচাই করার জন্য একটি ছোট পরীক্ষা-কোয়েরিও দাও (যেমন একটি অর্ডারের মোট হাতে মেলানো)।

AI-এর লেখা SQL বা ডিজাইন হাতে পেলে — যাচাইয়ের চেকলিস্ট

পরীক্ষা ও ইন্টারভিউতে এই পর্ব থেকে যে ধরনের প্রশ্ন সাধারণত আসে:

  • Three-schema architecture-এর তিনটি স্তর কী কী? — External (view), conceptual (logical) আর internal (physical); মাঝে দুটি mapping.
  • Logical আর physical data independence-এর পার্থক্য কী, কোনটি কঠিন? — Physical: internal বদলালে conceptual অপরিবর্তিত; logical: conceptual বদলালে external অপরিবর্তিত। Logical কঠিন, কারণ অ্যাপ ডেটার গঠন ও অর্থের ওপর নির্ভর করে।
  • Schema আর instance কী? — Schema ডেটাবেসের নকশা (কম বদলায়), instance কোনো মুহূর্তের আসল ডেটা (প্রতিটি DML-এ বদলায়)।
  • DDL, DML, DCL, TCL-এর একটি করে উদাহরণ দিন। — CREATE TABLE, UPDATE, GRANT, ROLLBACK.
  • Data dictionary বা system catalog কী? — DBMS-এর নিজের রাখা metadata-র টেবিল: টেবিল, column, index, view, user, অনুমতি; parser আর optimizer এটি ব্যবহার করে।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন। এগুলোই আপনাকে কোয়েরি-লেখক থেকে সিস্টেম-চিন্তক বানাবে।

প্র ০১১৯৭০-এর দশকে ঠিক কোন সমস্যার কারণে তিন স্তরের নকশার প্রস্তাব এলো? তখনকার ডেটাবেসে কী অসুবিধা ছিল?

১৯৬০-এর দশকের শেষে আর ৭০-এর দশকের শুরুতে বড় প্রতিষ্ঠানগুলো দুই ধরনের ডেটাবেস ব্যবহার করত: IBM-এর IMS-এর মতো hierarchical আর CODASYL Data Base Task Group-এর সুপারিশ মেনে তৈরি network ডেটাবেস। দুটিতেই প্রোগ্রাম লেখা হতো ডেটার ভৌত পথ ধরে — কোন record-এর পরে কোন record, কোন pointer ধরে কোন record-এ যেতে হবে। প্রোগ্রামারকে অনেকটাই জানতে হতো ডেটা ডিস্কে কীভাবে সাজানো।

ফলাফল ছিল ভয়ংকর রকমের ভঙ্গুরতা। ডেটা দ্রুত খুঁজতে কেউ record-এর ক্রম বদলালে বা নতুন একটি access path যোগ করলে, পুরোনো প্রোগ্রামগুলো ভুল চলত বা থেমে যেত। বড় প্রতিষ্ঠানে প্রোগ্রামারদের সময়ের বড় অংশ চলে যেত নতুন কিছু বানাতে নয়, বরং পুরোনো প্রোগ্রামকে ডেটার নতুন বিন্যাসের সাথে মানিয়ে নিতে। ১৯৭০ সালে Edgar F. Codd তাঁর বিখ্যাত প্রবন্ধ “A Relational Model of Data for Large Shared Data Banks”-এর শুরুতেই এই সমস্যার কথা বলেন — ব্যবহারকারীকে ডেটার ভেতরের বিন্যাস থেকে রক্ষা করা, অর্থাৎ data independence-ই ছিল তাঁর প্রধান লক্ষ্য।

প্রায় একই সময়ে ANSI/X3/SPARC study group প্রশ্নটি অন্যভাবে দেখল: কোনো নির্দিষ্ট data model নয়, বরং যেকোনো DBMS-এর জন্য একটি সাধারণ কাঠামো। তাদের উত্তর ছিল — বর্ণনাকে তিন স্তরে ভাগ করা, আর স্তরগুলোর মাঝে mapping রাখা। তাহলে ভৌত বিন্যাস বদলালে শুধু নিচের mapping বদলাবে, প্রতিষ্ঠানের logical নকশা বদলালে শুধু ওপরের mapping, আর প্রোগ্রামগুলো নিরাপদ থাকবে। ১৯৭৫ সালের অন্তর্বর্তী আর ১৯৭৭ সালের চূড়ান্ত রিপোর্ট এই কাঠামো স্থির করে।

মজার বিষয় হলো, ANSI/SPARC কোনো বাস্তব পণ্যের মান হয়ে ওঠেনি; ১৯৮০-এর দশকে বাজার জয় করে relational DBMS আর SQL, যা নিজের ভেতরেই view (external), টেবিল (conceptual) আর storage (internal) — এই তিনটি ধারণা ধারণ করে। ANSI/SPARC-এর ভাষা টিকে গেছে চিন্তার হাতিয়ার হিসেবে।

মূল উপলব্ধি: তিন স্তরের নকশা কোনো তাত্ত্বিক বিলাসিতা নয়; এটি এসেছিল একটি বাস্তব, খরুচে সমস্যা থেকে — ডেটার বিন্যাস বদলালেই প্রোগ্রাম ভেঙে যাওয়া। আজকের যেকোনো ভালো সফটওয়্যার নকশায় “interface আর implementation আলাদা রাখা” নীতিটি একই চিন্তার উত্তরসূরি।

প্র ০২বাস্তবে logical data independence কেন এত কঠিন? চালু একটি অ্যাপ বন্ধ না করে কীভাবে schema বদলানো হয়?

তত্ত্বে view দিয়ে যেকোনো conceptual পরিবর্তন লুকানো যাওয়ার কথা। বাস্তবে তিনটি বাধা আসে।

  • View-এর মাধ্যমে লেখা: পড়ার জন্য view চমৎকার, কিন্তু view-এর মাধ্যমে INSERT বা UPDATE করলে DBMS-কে বুঝতে হয় মূল কোন টেবিলের কোন সারি বদলাতে হবে। দুই টেবিলের join, GROUP BY বা হিসাব করা column থাকলে প্রায়ই এর একটি নির্দিষ্ট উত্তর থাকে না — order_totals-এর total 17400 থেকে 20000 করলে কোন জিনিসের দাম বা সংখ্যা বাড়বে? একে বলে view update problem; ১৯৭০–৮০-এর দশক থেকে এ নিয়ে গবেষণা চলছে, আর সাধারণভাবে এর পূর্ণ সমাধান নেই। এজন্য অনেক DBMS শুধু “সরল” view-তে লেখা মেনে নেয়, বাকিগুলোর জন্য trigger লিখতে হয় (পর্ব ২৯)।
  • অ্যাপ সরাসরি টেবিল পড়ে: বাস্তবের বেশিরভাগ অ্যাপ view নয়, মূল টেবিলেই কোয়েরি করে, অনেক সময় SELECT * দিয়ে। ORM আবার টেবিলের গঠনকে সরাসরি class-এ বেঁধে ফেলে। ফলে mapping-এর স্তরটিই থাকে না।
  • অর্থের পরিবর্তন: একটি column-এর নাম না বদলে অর্থ বদলালে (যেমন price আগে VAT ছাড়া ছিল, এখন VAT সহ) কোনো DBMS সেটি ধরতে পারে না, কিন্তু প্রতিটি হিসাব ভুল হয়ে যায়।

তাহলে চালু সিস্টেমে schema বদলানো হয় কীভাবে? পেশাদার দলগুলো সাধারণত expand–contract (বা “parallel change”) পদ্ধতি নেয়: প্রথমে নতুন column বা টেবিল যোগ করা হয়, পুরোনোটাও থাকে (expand); অ্যাপ দুই জায়গাতেই লেখে; পুরোনো ডেটা নতুন জায়গায় কপি করা হয়; ধীরে ধীরে সব কোড নতুন গঠনে সরানো হয়; সবশেষে, নিশ্চিত হয়ে, পুরোনোটা মোছা হয় (contract). লক্ষ করুন — অংশ ৪-এর ডায়াগ্রামের শিক্ষার সাথে এটি মেলে: যোগ করা নিরাপদ, মোছা ঝুঁকিপূর্ণ, তাই মোছাকে সবার শেষে আলাদা ধাপে রাখা হয়। এই ধাপগুলো migration টুল দিয়ে লেখা হয় (পর্ব ৫০)।

মূল উপলব্ধি: Logical data independence DBMS একা দিতে পারে না; এটি আংশিক প্রযুক্তি (view, trigger) আর আংশিক শৃঙ্খলা (নাম ধরে column চাওয়া, ধাপে ধাপে migration). ভালো দল দুটোই মেনে চলে।

প্র ০৩SQLite-এ সার্ভার নেই, user নেই, GRANT নেই — তবু এটি পৃথিবীর সবচেয়ে বেশি ব্যবহৃত ডেটাবেসগুলোর একটি। এটি কি “আসল” DBMS?

প্রশ্নটির উত্তর নির্ভর করে “DBMS” বলতে কী বোঝেন তার ওপর। এই পর্বের মাপকাঠিতে দেখলে SQLite-এ প্রায় সবই আছে: schema আর catalog (sqlite_master), view আর index, declarative SQL, parser–optimizer–executor (bytecode virtual machine), buffer (page cache), transaction, lock আর recovery (rollback journal বা WAL). SQLite-এর transaction সত্যিকারের ACID — বিদ্যুৎ চলে গেলেও COMMIT হওয়া ডেটা হারায় না, আর এর পরীক্ষা-ব্যবস্থা বিখ্যাত রকমের কঠোর।

যা নেই, তা হলো client-server অংশ: আলাদা সার্ভার প্রোগ্রাম, নেটওয়ার্ক সংযোগ, অনেক user আর তাদের অনুমতি। এটা দুর্বলতা নয়, সচেতন নকশা। SQLite-এর নিজের ওয়েবসাইটে এর অবস্থান মোটামুটি এভাবে বলা আছে: SQLite client-server ডেটাবেসের প্রতিদ্বন্দ্বী নয়, এর প্রতিদ্বন্দ্বী হলো অ্যাপের নিজের বানানো ফাইল-ফরম্যাট (fopen())। অর্থাৎ অ্যাপ যখন নিজের ডেটা নিজের কাছে রাখতে চায় — ফোনের contact, ব্রাউজারের history, desktop অ্যাপের সেটিং — তখন হাতে-বানানো ফাইলের বদলে SQLite নিলে পর্ব ০২-এর সব সমস্যা (অর্ধেক লেখা ফাইল, একসাথে দুই প্রক্রিয়ার লেখা, খোঁজার কষ্ট) একসাথে মিটে যায়।

তবে সীমাও আছে। SQLite-এ একসময়ে মাত্র একজন লিখতে পারে (WAL mode-এ পড়া আর লেখা একসাথে চলে, কিন্তু লেখক একজনই)। অনেক কম্পিউটার থেকে নেটওয়ার্কে একই ফাইলে লেখা (যেমন শেয়ার করা ফোল্ডারে) ঝুঁকিপূর্ণ। হাজার হাজার ব্যবহারকারী একসাথে অর্ডার দেবেন এমন ওয়েবসাইটের কেন্দ্রীয় ডেটাবেস হিসেবে তাই PostgreSQL বা MySQL বেশি মানানসই। আবার কম লেখা, বেশি পড়ার ছোট-মাঝারি ওয়েবসাইট SQLite দিয়েই দিব্যি চলে।

এই কোর্সে SQLite বেছে নেওয়ার কারণও এখানে: এটি পুরোপুরি ব্রাউজারের ভেতরে চলতে পারে (WebAssembly), তাই কিছু ইনস্টল না করেই আপনি আসল SQL চালাতে পারছেন। আর যেখানে MySQL বা PostgreSQL-এর আচরণ আলাদা, সেখানে আলাদা করে দেখানো হবে।

মূল উপলব্ধি: “আসল DBMS” একটি মাত্রা নয়, অনেকগুলো বৈশিষ্ট্যের সমষ্টি। Architecture (embedded নাকি client-server) একটি নকশার সিদ্ধান্ত — কাজ অনুযায়ী বেছে নিতে হয়, “বড় মানেই ভালো” নয়।

প্র ০৪Catalog-কেও সাধারণ টেবিল হিসেবে রাখার সিদ্ধান্ত কেন এত গুরুত্বপূর্ণ? এর কোনো ঝুঁকি আছে কি?

১৯৭০-এর দশকের মাঝামাঝি IBM-এর গবেষণাগারে তৈরি System R — প্রথম দিকের relational DBMS-গুলোর একটি, যেখানে SQL-এর পূর্বসূরি SEQUEL জন্মায় — catalog-কে সাধারণ relation হিসেবেই রেখেছিল। পরে Codd ১৯৮৫ সালে relational DBMS যাচাইয়ের যে ১২টি নিয়ম প্রকাশ করেন, তার চতুর্থ নিয়ম এটিকেই বাধ্যতামূলক করে: ডেটাবেসের বর্ণনা relational আকারে থাকবে, আর অনুমতিপ্রাপ্ত ব্যবহারকারী সাধারণ ডেটার মতো একই query language দিয়ে তা পড়তে পারবেন।

এর সুবিধা অনেক:

  • একটিই হাতিয়ার: schema জানতে আলাদা টুল বা ফাইল-ফরম্যাট শিখতে হয় না — SELECT … FROM information_schema.columns.
  • Self-describing ডেটাবেস: ডেটাবেস ফাইল বা সার্ভার নিজেই নিজের নকশা বহন করে। কোনো প্রোগ্রাম আগে থেকে কিছু না জেনেও catalog পড়ে সব টেবিল দেখাতে পারে — DBeaver, pgAdmin, phpMyAdmin এভাবেই কাজ করে।
  • স্বয়ংক্রিয় টুল: ORM catalog পড়ে class বানায়, migration টুল বর্তমান schema আর কাঙ্ক্ষিত schema তুলনা করে পরিবর্তনের স্ক্রিপ্ট বানায়, ডকুমেন্টেশন টুল diagram আঁকে।
  • DDL-ও নিরাপদ: catalog টেবিল হলে DDL আসলে catalog-এ লেখা — আর সেটি transaction, lock আর recovery-র সুরক্ষা পায়। PostgreSQL-এ CREATE TABLE-কেও ROLLBACK করা যায় ঠিক এই কারণে।

ঝুঁকিও আছে। Catalog পড়তে পারা মানে আক্রমণকারী একবার কোনো দুর্বলতা পেলে প্রথমেই জেনে নিতে পারে কোন টেবিলে কী আছে; তাই নিরাপত্তায় “least privilege” নীতি — অ্যাপের user শুধু যা দরকার তাই দেখবে (পর্ব ৫১)। আবার catalog-এর টেবিলে সরাসরি UPDATE করলে (কিছু DBMS বিশেষ অনুমতিতে সেটা দেয়) পুরো ডেটাবেস নষ্ট হতে পারে; এজন্য catalog বদলানোর একমাত্র নিরাপদ পথ DDL. আর বড় সিস্টেমে catalog নিজেই ব্যস্ততম টেবিল — প্রতিটি কোয়েরির parser ও optimizer এটি পড়ে, তাই DBMS এর জন্য আলাদা cache রাখে।

মূল উপলব্ধি: “নিজের বর্ণনাও নিজের ডেটার মতো রাখা” — এই এক সিদ্ধান্ত থেকে এসেছে টুলের পুরো জগৎ, transactional DDL আর self-describing ডেটাবেস। একই চিন্তা দেখবেন HTML-এর DOM-এ, Java-র reflection-এ বা REST API-এর OpenAPI বর্ণনায়।

প্র ০৫ব্রাউজার সরাসরি ডেটাবেসে কোয়েরি পাঠালেই তো দ্রুত হতো — 3-tier-এ মাঝখানে app server রাখা হয় কেন? Firebase বা Supabase কি এই নিয়ম ভাঙে?

সরাসরি সংযোগ কাগজে সহজ, কিন্তু বাস্তবে চারটি বড় সমস্যা তৈরি করে।

  • পাসওয়ার্ড আর নিরাপত্তা: ব্রাউজার থেকে ডেটাবেসে যুক্ত হতে হলে ডেটাবেসের পাসওয়ার্ড বা চাবি ব্রাউজারের কোডে রাখতে হবে — আর ব্রাউজারের কোড যে কেউ খুলে পড়তে পারে। পাসওয়ার্ড পেলে তিনি আপনার অ্যাপের বোতাম ছাড়াই নিজের ইচ্ছামতো SQL চালাবেন।
  • ব্যবসার নিয়ম: “গ্রাহক শুধু নিজের অর্ডার দেখবেন”, “স্টক না থাকলে অর্ডার নয়”, “বাতিলের পরে টাকা ফেরত” — এই নিয়মগুলো ব্রাউজারে রাখলে যে কেউ বদলে ফেলতে পারে। App server হলো এমন জায়গা যা ব্যবহারকারীর নিয়ন্ত্রণের বাইরে।
  • সংযোগের খরচ: ডেটাবেস সার্ভার একসাথে সীমিত সংখ্যক সংযোগ ভালোভাবে সামলাতে পারে; PostgreSQL প্রতিটি সংযোগের জন্য আলাদা process চালায়। লাখ ব্যবহারকারী সরাসরি যুক্ত হলে সার্ভার বসে যাবে। App server অল্প কয়েকটি সংযোগ ভাগ করে ব্যবহার করে (connection pool, পর্ব ৪৯)।
  • স্বাধীনভাবে বদল: মাঝের স্তর থাকলে ডেটাবেসের schema বদলালে শুধু সার্ভারের কোড বদলাতে হয়; লাখো ফোনে অ্যাপ আপডেটের অপেক্ষা করতে হয় না। এটি আসলে data independence-এরই আরেক রূপ — app server এখানে একধরনের external level.

তাহলে Firebase বা Supabase-এ ব্রাউজার “সরাসরি” ডেটাবেসে লেখে কীভাবে? আসলে মাঝের স্তরটি বাদ যায়নি, শুধু সেবাদাতা সেটি বানিয়ে দিয়েছেন। Supabase-এ ব্রাউজারের অনুরোধ যায় একটি API স্তরে, আর সুরক্ষা আসে PostgreSQL-এর row-level security নিয়ম থেকে — “প্রতিটি সারি শুধু তার মালিক পড়তে পারবেন।” Firebase-এ একই কাজ করে তার security rules. ব্রাউজারে যে চাবি থাকে, তা দিয়ে কেবল সেই নিয়মের ভেতরে যা অনুমতি আছে তাই করা যায়। নিয়ম ভুল লিখলে, বা নিয়ম ছাড়াই চালু করলে, পুরো ডেটাবেস উন্মুক্ত হয়ে যেতে পারে — এমন ভুল-কনফিগারেশনের ঘটনা নিয়ে নিরাপত্তা গবেষকরা বহুবার সতর্ক করেছেন।

মূল উপলব্ধি: 3-tier-এর মাঝের স্তর গতির জন্য নয়, বিশ্বাসের সীমারেখা টানার জন্য। ব্যবহারকারীর যন্ত্রে যা আছে তা বিশ্বাসযোগ্য নয়; নিয়ম আর চাবি থাকতে হবে এমন জায়গায়, যা আপনার নিয়ন্ত্রণে।

অনুশীলন

প্রথমে নিজে উত্তর লিখুন বা ভাবুন, তারপর মিলিয়ে দেখুন। কোয়েরিগুলো উপরের যেকোনো live editor-এ পেস্ট করে চালাতে পারেন।

  1. দলে ভাগ করুন: নিচের প্রতিটি স্টেটমেন্ট DDL, DML, DCL না TCL? (ক) ALTER TABLE products ADD COLUMN weight_kg REAL (খ) DELETE FROM orders WHERE status = 'cancelled' (গ) REVOKE SELECT ON products FROM warehouse (ঘ) COMMIT (ঙ) CREATE INDEX idx_city ON customers(city) (চ) SELECT * FROM stock_view

    (ক) DDL — schema বদলায়। (খ) DML — instance বদলায়। (গ) DCL — অনুমতি। (ঘ) TCL — transaction স্থায়ী করে। (ঙ) DDL — internal schema বদলায়; ANSI/SPARC-এর ভাষায় এটি storage definition-এর কাজ, কিন্তু SQL-এ DDL-এরই অংশ। (চ) DML (কিছু বইয়ে DQL) — view-এর ওপর কোয়েরিও সাধারণ SELECT.

  2. কোয়েরি লিখুন — ডেলিভারি দলের view: db-shop-এ একটি view বানান delivery_view, যা শুধু shipped অবস্থার অর্ডারের order_id, গ্রাহকের name, city আর phone দেখাবে (দাম বা পণ্যের তালিকা নয়)। তারপর view থেকে সব সারি বের করুন। কয়টি সারি আসবে?
    CREATE VIEW delivery_view AS
      SELECT o.order_id, c.name, c.city, c.phone
      FROM orders o
      JOIN customers c ON c.customer_id = o.customer_id
      WHERE o.status = 'shipped';
    
    SELECT * FROM delivery_view;

    দুটি সারি আসে: 1003 · Rahim Uddin · Dhaka · 01711000001 আর 1008 · Mitu Rani · Dhaka · 01411000008. ডেলিভারি দলের জন্য এটি একটি external schema — তাঁরা ঠিকানা আর ফোন পান, কিন্তু কত টাকার অর্ডার বা গ্রাহকের অন্য অর্ডার দেখেন না। নিরাপত্তা পুরো করতে হলে client-server DBMS-এ এই দলকে শুধু delivery_view-এ GRANT SELECT দিতে হবে।

  3. হাতে trace করুন — কী ভাঙবে? একটি অ্যাপ শুধু দুটি কোয়েরি চালায়: (১) SELECT * FROM stock_view (view = product_id, name, stock) আর (২) SELECT name, price FROM products WHERE category_id = 2. নিচের প্রতিটি পরিবর্তনের পর কোনটি ঠিক চলবে, আর এটি কোন ধরনের independence-এর উদাহরণ? (ক) category_id-এ index যোগ (খ) products-এ discount column যোগ (গ) price column-এর নাম বদলে unit_price

    (ক) দুটিই একই ফল দেয়; কোয়েরি (২) হয়তো দ্রুত হয়। এটি physical data independence.

    (খ) দুটিই ঠিক চলে। View নিজের column বেছে নেয়, আর (২) column-এর নাম ধরে চায়। এটি logical data independence-এর সফল উদাহরণ। (মূল টেবিলে SELECT * থাকলে সেটি নতুন column পেত।)

    (গ) কোয়েরি (১) চলবে — view price ব্যবহার করে না। কোয়েরি (২) no such column: price error দেবে, কারণ সে সরাসরি মূল টেবিল পড়ছে। যদি অ্যাপ price-ও একটি view থেকে পড়ত, তাহলে view-এর সংজ্ঞায় unit_price AS price লিখে mapping বদলালেই অ্যাপ বাঁচত। শিক্ষা: logical independence পেতে হলে অ্যাপকে view-এর মাধ্যমে কথা বলতে হয়।

  4. ফল আগে থেকে বলুন: db-shop-এ নিচের তিনটি স্টেটমেন্ট পরপর চালালে শেষ কোয়েরি কয়টি সারি দেবে, আর শেষ দুটি সারি কী হবে? CREATE VIEW stock_view AS SELECT product_id, name, stock FROM products; → CREATE INDEX idx_products_price ON products(price); → SELECT name, type FROM sqlite_master;

    আগে catalog-এ ছিল ৮টি সারি (৫টি টেবিল আর ৩টি স্বয়ংক্রিয় index). একটি view আর একটি index যোগ হওয়ায় এখন ১০টি সারি. SQLite নতুন জিনিস তৈরির ক্রমে catalog-এ সারি যোগ করে, তাই শেষ দুটি সারি: stock_view · view আর idx_products_price · index. লক্ষ করুন, CREATE VIEW আর CREATE INDEX — দুটিই DDL, আর দুটিই আসলে catalog টেবিলে একটি করে সারি লেখে। (SQL-এ ORDER BY ছাড়া সারির ক্রমের নিশ্চয়তা নেই; এখানে ক্রমটি SQLite-এর বর্তমান আচরণ।)

নিজেকে যাচাই করুন

ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।

ক্রমএকটি SELECT কোয়েরি DBMS-এর ভেতরে যে ক্রমে যন্ত্রাংশগুলো পার হয়, সাজান (▲▼ চেপে সরান)

  1. 🔍 Parser — ব্যাকরণ যাচাই, catalog-এ টেবিল ও column খোঁজা
  2. 🧮 Optimizer — সম্ভাব্য plan-গুলোর খরচ তুলনা করে একটি বাছাই
  3. ⚙️ Executor — plan অনুযায়ী page চাওয়া শুরু
  4. 🧠 Buffer manager — page মেমরিতে আছে কি না দেখা
  5. 💽 File manager — না থাকলে ডিস্ক থেকে page পড়া

ANSI/SPARC three-schema architecture-এ কোন স্তরে পুরো ডেটাবেসের একটিমাত্র logical বর্ণনা থাকে — সব টেবিল, column, সম্পর্ক আর constraint, কিন্তু ডিস্কের বিন্যাস নয়?

  • External level
  • Conceptual level
  • Internal level
  • Mapping level

একটি টেবিলে নতুন index যোগ করার পর কোনো অ্যাপের কোয়েরি বদলাতে হলো না, শুধু দ্রুত চলল। এটি কীসের উদাহরণ?

  • Logical data independence
  • DML-এর ব্যবহার
  • Physical data independence
  • Distribution transparency

নিচের কোন জোড়াটি সঠিক?

  • GRANT — DDL
  • ROLLBACK — DML
  • ALTER TABLE — DCL
  • UPDATE — DML

একটি SQL কোয়েরিতে টেবিলের নামের বানান ভুল (prodcts). DBMS-এর কোন অংশ সবার আগে এই ভুল ধরবে?

  • Parser (catalog দেখে)
  • Optimizer
  • Buffer manager
  • Recovery manager

একটি ই-কমার্স ওয়েবসাইটে ব্রাউজার → application server → database server — এই নকশার প্রধান নিরাপত্তা-সুবিধা কোনটি?

  • প্রতিটি কোয়েরি সবসময় দ্রুততর হয়
  • ডেটাবেসের পাসওয়ার্ড ও নিয়ম ব্যবহারকারীর যন্ত্রে থাকে না
  • Index আর লাগে না
  • Transaction-এর দরকার থাকে না

SQLite-এ SELECT name, type FROM sqlite_master; চালালে কী পাওয়া যায়?

  • সব টেবিলের সব সারি
  • ডেটাবেসের user আর তাদের অনুমতি
  • ডেটাবেসের টেবিল, index, view আর trigger-এর নাম ও ধরন
  • সম্প্রতি চালানো কোয়েরির তালিকা

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:

মনে রাখুন

  1. Schema = নকশা (কম বদলায়), instance = এই মুহূর্তের ডেটা (প্রতিটি DML-এ বদলায়)।
  2. ANSI/SPARC-এর তিন স্তর: external (অনেকগুলো view) → conceptual (একটি logical নকশা) → internal (ফাইল, page, index); মাঝে দুটি mapping.
  3. Physical independence সহজ ও প্রায় সম্পূর্ণ (index, ডিস্ক বদল লুকানো যায়); logical independence আংশিক — যোগ করা লুকানো যায়, মোছা নয়।
  4. SQL-এর চার দল: DDL (CREATE/ALTER/DROP), DML (SELECT/INSERT/UPDATE/DELETE), DCL (GRANT/REVOKE), TCL (BEGIN/COMMIT/ROLLBACK); declarative DML-ই physical independence সম্ভব করে।
  5. DBMS = query processor (parser → optimizer → executor) + storage manager (buffer, file, transaction, lock, recovery) + catalog, যা নিজেই টেবিল; চলে centralized, client-server (2/3-tier), embedded বা distributed রূপে।

পরের পর্বে: তিন স্তরের conceptual স্তরটি কোন “ভাষায়” লেখা হবে — গাছের মতো hierarchical, জালের মতো network, টেবিলের relational, নাকি NoSQL-এর document আর key-value? ডেটা মডেলের ইতিহাস আর কেন relational model জিতল।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান