← Tutorials 🤖 AI 📓 Architect's Notebook ☁ AWS 📋 Cheatsheet 🗃 Databases 🚀 DevOps 🧩 DSA ❓ FAQ 🖥 Frontend ☕ Java 🍏 MongoDB 🐍 Python 🌿 Spring Boot 🏗 System Design 🏛 TOGAF
TutorialsDatabases › MySQL
★ marked topics are high-priority for senior Java / Solution Architect interviews — explained in depth with diagrams. Other topics give concise, practical summaries.

Introduction to MySQL

🗄️ MySQL · Foundations · 5 min read

MySQL is the world's most popular open-source relational database management system (RDBMS). It stores data in tables (rows & columns), uses SQL to query it, and powers a huge share of web applications.

Why MySQL

  • Open source & free (Community Edition), with commercial support available.
  • ACID-compliant via the InnoDB storage engine — reliable transactions.
  • Mature & fast — battle-tested, great read performance, rich tooling.
  • Cross-platform and integrates with every major language/framework (e.g. Spring Data JPA).
-- connect & basic usage mysql -u root -p CREATE DATABASE shop; USE shop; SHOW TABLES;
💡 MySQL vs PostgreSQL: both are excellent RDBMS. MySQL is famed for read speed and ubiquity; PostgreSQL for advanced features and strict standards compliance. Concepts here (SQL, joins, transactions, indexing) transfer to both.

RDBMS Concepts

🗄️ MySQL · Foundations · 5 min read

A Relational Database Management System organizes data into relations (tables) with rows (records) and columns (attributes), linked by keys, and queried with SQL.

TermMeaning
Table (relation)A collection of rows for one entity
Row (tuple/record)One instance of the entity
Column (attribute)A field with a data type
Primary keyUniquely identifies each row
Foreign keyReferences another table's PK (relationship)
SchemaThe structure/blueprint of the database
💡 The "relational" part = tables related to each other via keys. This enforces integrity (foreign keys) and avoids duplication (normalization) — the core advantages over flat files or simple key-value stores.

Database Design & Best Practices

🗄️ MySQL · Foundations · ★ Interview Topic · 10 min read

Database design is the process of modeling data into well-structured tables and relationships that are correct, efficient, and maintainable. Good design prevents anomalies, redundancy, and performance pain later.

The Design Process — Visual

Requirementsentities & rules ConceptualER diagram Logicaltables + normalize Physicalindexes, types, engine

Best Practices

  • Model entities clearly — one table per entity; meaningful names (snake_case, plural tables).
  • Normalize to 3NF to eliminate redundancy, then denormalize selectively for read performance.
  • Choose right keys — surrogate BIGINT AUTO_INCREMENT PKs; FKs for integrity.
  • Pick correct data types — smallest that fits; DECIMAL for money (never FLOAT); DATETIME/TIMESTAMP for time.
  • Index for your queries — index FK and WHERE/JOIN columns; don't over-index.
  • Add constraints — NOT NULL, UNIQUE, CHECK, FK to enforce integrity at the DB.
  • Plan for growth — consider partitioning/archiving for large tables.
💡 Interview framing: design = ER model → normalize → choose keys/types → index for access patterns → enforce with constraints. The art is balancing normalization (integrity) against denormalization (read speed) for your workload.

Database Schema

🗄️ MySQL · Foundations · 4 min read

A schema is the structure of a database — its tables, columns, data types, keys, indexes, and relationships. In MySQL, "schema" and "database" are effectively synonyms.

CREATE TABLE users ( id BIGINT AUTO_INCREMENT PRIMARY KEY, email VARCHAR(120) NOT NULL UNIQUE, name VARCHAR(80) NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); SHOW CREATE TABLE users; -- inspect the schema DESCRIBE users; -- columns + types
💡 Keep schema changes in version control via migration tools (Flyway/Liquibase) so every environment evolves consistently and changes are auditable.

Data Types

🗄️ MySQL · Foundations · 5 min read

Choosing correct column types affects storage, performance, and correctness.

CategoryTypes
IntegerTINYINT, SMALLINT, INT, BIGINT
DecimalDECIMAL(p,s) (money), FLOAT, DOUBLE
StringCHAR, VARCHAR, TEXT
Date/TimeDATE, DATETIME, TIMESTAMP, TIME
OtherBOOLEAN (TINYINT), ENUM, JSON, BLOB
⚠️ Use DECIMAL for money — FLOAT/DOUBLE are approximate and cause rounding errors. VARCHAR(n) over CHAR(n) for variable text. Pick the smallest integer type that fits to save space and improve index performance.

MySQL Architecture

🗄️ MySQL · Foundations · 6 min read

MySQL has a layered architecture: a connection/SQL layer on top, pluggable storage engines underneath.

Connection Layer (auth, threads, connection pool) SQL Layerparser · optimizer · query cache · executor Storage Engine Layer (pluggable)InnoDB (default) · MyISAM · Memory
  • Connection layer — handles client connections, authentication, threading.
  • SQL layer — parses, optimizes (chooses the execution plan), and executes queries.
  • Storage engine layer — pluggable; InnoDB (transactional, default) vs MyISAM (legacy).
💡 The query optimizer in the SQL layer decides how to run your query (which indexes, join order) — EXPLAIN shows its plan. The pluggable storage engine is MySQL's signature feature; InnoDB is the right default.

DDL Commands

🗄️ MySQL · Command Categories · 4 min read

Data Definition Language defines and modifies the database structure — tables, schemas, indexes. DDL statements auto-commit (can't be rolled back).

CommandDoes
CREATECreate a table/database/index
ALTERModify an existing structure
DROPDelete a structure entirely
TRUNCATERemove all rows (reset table)
💡 The 4 SQL command families: DDL (structure), DML (data), DCL (permissions), TCL (transactions). DDL is auto-committing — there's no rollback for a dropped table.

DML Commands

🗄️ MySQL · Command Categories · 4 min read

Data Manipulation Language works with the data inside tables. DML runs inside transactions and can be rolled back.

CommandDoes
SELECTRead rows
INSERTAdd rows
UPDATEModify rows
DELETERemove rows
💡 Unlike DDL, DML changes are transactional — wrap them in BEGIN ... COMMIT/ROLLBACK for safety. (Some classifications put SELECT under DQL.)

DCL Commands

🗄️ MySQL · Command Categories · 3 min read

Data Control Language manages access rights and permissions.

GRANT SELECT, INSERT ON shop.* TO 'appuser'@'%'; REVOKE INSERT ON shop.* FROM 'appuser'@'%'; FLUSH PRIVILEGES;
💡 GRANT gives privileges; REVOKE takes them away. Follow least privilege — give an app user only the rights it needs (see Privileges and Roles).

TCL Commands

🗄️ MySQL · Command Categories · 3 min read

Transaction Control Language manages transactions — grouping DML into atomic units.

START TRANSACTION; -- or BEGIN UPDATE accounts SET balance = balance - 100 WHERE id = 1; UPDATE accounts SET balance = balance + 100 WHERE id = 2; COMMIT; -- or ROLLBACK to undo SAVEPOINT sp1; -- partial rollback point
💡 COMMIT makes changes permanent; ROLLBACK undoes them; SAVEPOINT marks a point to partially roll back to. See the Transactions section for depth.

CREATE Statement

🗄️ MySQL · DDL · 4 min read

CREATE defines new databases, tables, indexes, views, etc.

CREATE DATABASE shop; CREATE TABLE products ( id BIGINT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(120) NOT NULL, price DECIMAL(10,2) NOT NULL, category_id BIGINT, FOREIGN KEY (category_id) REFERENCES categories(id) ) ENGINE=InnoDB; CREATE INDEX idx_category ON products(category_id);
💡 Specify ENGINE=InnoDB (the default) for transactions & foreign keys. Define keys/constraints at creation time where possible.

ALTER Statement

🗄️ MySQL · DDL · 4 min read

ALTER modifies an existing table — add/drop/modify columns, keys, and indexes.

ALTER TABLE products ADD COLUMN sku VARCHAR(40); ALTER TABLE products MODIFY COLUMN price DECIMAL(12,2); ALTER TABLE products DROP COLUMN sku; ALTER TABLE products ADD CONSTRAINT uq_name UNIQUE (name); ALTER TABLE products ADD INDEX idx_price (price);
⚠️ On large tables, ALTER can lock the table and run for a long time. Use online DDL (ALGORITHM=INPLACE, LOCK=NONE) or tools like pt-online-schema-change / gh-ost in production.

DROP Statement

🗄️ MySQL · DDL · 3 min read

DROP permanently deletes a database object and all its data/structure.

DROP TABLE products; DROP DATABASE shop; DROP INDEX idx_price ON products; DROP TABLE IF EXISTS temp; -- safe guard
⚠️ DROP is irreversible (DDL auto-commits — no rollback) and removes both data and structure. Always have a backup before dropping in production. Use IF EXISTS to avoid errors in scripts.

TRUNCATE Statement

🗄️ MySQL · DDL · 4 min read

TRUNCATE removes all rows from a table quickly and resets it (incl. AUTO_INCREMENT), keeping the structure.

TRUNCATEDELETE (no WHERE)
TypeDDL (auto-commit)DML (rollback-able)
SpeedVery fast (drops & recreates)Slower (row by row)
AUTO_INCREMENTReset to 1Not reset
WHERE filterNoYes
TriggersNot firedFired
💡 Classic interview question — DELETE vs TRUNCATE vs DROP: DELETE removes selected rows (DML, rollback); TRUNCATE empties the whole table fast (DDL, resets); DROP removes the table entirely.

INSERT Statement

🗄️ MySQL · DML · 3 min read
INSERT INTO users (name, email) VALUES ('Aftab', 'a@x.com'); INSERT INTO users (name, email) VALUES -- bulk insert ('Sam','s@x.com'), ('Ria','r@x.com'); INSERT INTO users SET name='Joe', email='j@x.com'; INSERT INTO archive SELECT * FROM users WHERE created_at < '2020-01-01'; INSERT INTO users (...) VALUES (...) ON DUPLICATE KEY UPDATE name = VALUES(name); -- upsert
💡 Bulk insert (one statement, many rows) is far faster than many single inserts. ON DUPLICATE KEY UPDATE is MySQL's upsert.

UPDATE Statement

🗄️ MySQL · DML · 3 min read
UPDATE users SET status = 'active' WHERE id = 5; UPDATE products SET price = price * 1.1 WHERE category_id = 3; UPDATE orders o JOIN users u ON o.user_id = u.id -- update with join SET o.vip = 1 WHERE u.tier = 'gold';
⚠️ Always include a WHERE clause — UPDATE without one changes every row. Test with a matching SELECT first, and run inside a transaction so you can roll back.

DELETE Statement

🗄️ MySQL · DML · 3 min read
DELETE FROM users WHERE id = 5; DELETE FROM orders WHERE created_at < '2020-01-01' LIMIT 1000; -- batched DELETE o FROM orders o JOIN users u ON o.user_id=u.id WHERE u.banned=1;
⚠️ Like UPDATE, a DELETE without WHERE wipes the whole table. For mass deletes, use LIMIT batches to avoid long locks; to empty a table entirely, TRUNCATE is faster.

SELECT Statement

🗄️ MySQL · DML · 5 min read

SELECT retrieves rows. Knowing the logical execution order of its clauses is key to writing correct queries.

SELECT category_id, COUNT(*) AS cnt FROM products WHERE price > 100 GROUP BY category_id HAVING COUNT(*) > 5 ORDER BY cnt DESC LIMIT 10;

Logical Processing Order

FROMWHEREGROUP BYHAVINGSELECTORDER BYLIMIT

💡 This order explains common gotchas: you can't use a SELECT alias in WHERE (WHERE runs first) but you can in ORDER BY (it runs after SELECT). Avoid SELECT * in production — name the columns you need.

WHERE Clause

🗄️ MySQL · Querying · 4 min read

WHERE filters rows before grouping/aggregation using conditions and operators.

WHERE price BETWEEN 10 AND 100 WHERE status IN ('active','pending') WHERE name LIKE 'A%' -- starts with A WHERE email IS NOT NULL WHERE price > 100 AND category_id = 3 WHERE created_at >= '2024-01-01'
💡 For WHERE to use an index, avoid wrapping the column in a function (WHERE YEAR(created_at)=2024 can't use an index — use a range instead). Leading wildcards (LIKE '%x') also prevent index use.

ORDER BY

🗄️ MySQL · Querying · 3 min read

Sorts the result set by one or more columns, ascending (default) or descending.

SELECT * FROM products ORDER BY price DESC; SELECT * FROM users ORDER BY last_name ASC, first_name ASC; -- multi-column SELECT name, price*0.9 AS sale FROM products ORDER BY sale; -- by alias
💡 An index on the ORDER BY column(s) lets MySQL skip a sort (avoids "Using filesort" in EXPLAIN). Multi-column sorts benefit from a matching composite index in the same order.

GROUP BY

🗄️ MySQL · Querying · 4 min read

Groups rows that share values, so aggregate functions compute per group.

SELECT category_id, COUNT(*) AS cnt, AVG(price) AS avg_price FROM products GROUP BY category_id;
⚠️ Every non-aggregated column in SELECT must appear in GROUP BY (enforced by ONLY_FULL_GROUP_BY, default in modern MySQL). To filter groups, use HAVING (not WHERE).

HAVING Clause

🗄️ MySQL · Querying · 3 min read

HAVING filters groups after aggregation — like WHERE, but it can use aggregate functions.

SELECT category_id, COUNT(*) AS cnt FROM products GROUP BY category_id HAVING COUNT(*) > 5; -- filter groups
WHEREHAVING
FiltersRows (before grouping)Groups (after aggregation)
AggregatesNoYes
💡 Interview classic: WHERE filters rows before GROUP BY; HAVING filters the aggregated groups after. Put row conditions in WHERE (faster, index-able) and only aggregate conditions in HAVING.

DISTINCT

🗄️ MySQL · Querying · 3 min read

Removes duplicate rows from the result set.

SELECT DISTINCT country FROM users; SELECT DISTINCT country, city FROM users; -- distinct combinations SELECT COUNT(DISTINCT country) FROM users;
💡 DISTINCT applies to the whole row (all selected columns), not just the first. It triggers a sort/hash — can be costly on large sets; sometimes a GROUP BY or EXISTS is clearer/faster.

LIMIT

🗄️ MySQL · Querying · 3 min read

Restricts the number of returned rows — for top-N queries and pagination.

SELECT * FROM products ORDER BY price DESC LIMIT 10; -- top 10 SELECT * FROM products ORDER BY id LIMIT 20 OFFSET 40; -- page 3 (size 20)
⚠️ Large OFFSET is slow — MySQL scans & discards all skipped rows. For deep pagination, use keyset (seek) pagination: WHERE id > :lastId ORDER BY id LIMIT 20.

Aggregate Functions

🗄️ MySQL · Functions · 3 min read

Compute a single value over a set of rows — usually with GROUP BY.

FunctionReturns
COUNT(*)Number of rows
SUM(col)Total
AVG(col)Average
MIN/MAX(col)Smallest / largest
GROUP_CONCAT(col)Concatenated values
💡 COUNT(*) counts all rows; COUNT(col) ignores NULLs; COUNT(DISTINCT col) counts unique non-nulls. Aggregates skip NULLs (except COUNT(*)).

String Functions

🗄️ MySQL · Functions · 3 min read
CONCAT(first, ' ', last) -- join UPPER(s), LOWER(s) SUBSTRING(s, 1, 5) -- s[1..5] LENGTH(s), CHAR_LENGTH(s) TRIM(s), LTRIM(s), RTRIM(s) REPLACE(s, 'a', 'b') LOCATE('x', s) -- position LEFT(s, 3), RIGHT(s, 3)
💡 Avoid using string functions on indexed columns in WHERE (WHERE UPPER(email)=...) — it disables the index. Normalize data on write instead, or use a generated/functional index.

Date Functions

🗄️ MySQL · Functions · 3 min read
NOW(), CURDATE(), CURTIME() DATE_ADD(d, INTERVAL 7 DAY) DATEDIFF(d1, d2) -- days between DATE_FORMAT(d, '%Y-%m-%d') YEAR(d), MONTH(d), DAY(d) TIMESTAMPDIFF(HOUR, t1, t2)
⚠️ For range filters use WHERE created_at >= '2024-01-01' AND created_at < '2025-01-01' rather than WHERE YEAR(created_at)=2024 — the former can use an index, the latter can't.

Mathematical Functions

🗄️ MySQL · Functions · 3 min read
ROUND(x, 2), CEIL(x), FLOOR(x) ABS(x), MOD(a, b), POWER(x, 2), SQRT(x) GREATEST(a,b,c), LEAST(a,b,c) RAND() -- random 0..1
💡 Use ROUND(x, n) for display rounding, but store money as DECIMAL and do exact arithmetic — never round-trip through FLOAT.

Joins

🗄️ MySQL · Joins · ★ Interview Topic · 11 min read

A JOIN combines rows from two or more tables based on a related column. Joins are how normalized data is reassembled — and a top interview topic.

The Join Types — Visual

INNER LEFT RIGHT FULL shaded = rows returned · INNER = match only · LEFT/RIGHT = all of one side · FULL = everything

Quick Reference

JoinReturns
INNER JOINOnly matching rows in both tables
LEFT JOINAll left rows + matches (NULLs if none)
RIGHT JOINAll right rows + matches
FULL JOINAll rows from both (emulated in MySQL)
CROSS JOINCartesian product (every combination)
SELF JOINTable joined to itself
SELECT u.name, o.total FROM users u INNER JOIN orders o ON o.user_id = u.id WHERE o.total > 100;
⚠️ Index the join columns (usually the FK) — unindexed joins force full scans and explode in cost. Watch out for accidental Cartesian products from a missing/incorrect ON condition. Use EXPLAIN to confirm the join strategy.
💡 Interview default: INNER for "rows that match in both"; LEFT for "all of A, plus B if present" (great for finding non-matches with WHERE b.id IS NULL).

Inner Join

🗄️ MySQL · Joins · 3 min read

Returns only rows where the join condition matches in both tables. The most common join.

SELECT o.id, u.name FROM orders o INNER JOIN users u ON u.id = o.user_id; -- orders without a matching user are excluded
💡 JOIN with no keyword = INNER JOIN. If a row has no match on the other side, it's dropped from the result.

Left Join

🗄️ MySQL · Joins · 3 min read

Returns all rows from the left table, plus matching right-table rows (NULLs where there's no match).

SELECT u.name, o.id AS order_id FROM users u LEFT JOIN orders o ON o.user_id = u.id; -- users with no orders still appear (order_id = NULL) -- find users who never ordered: SELECT u.* FROM users u LEFT JOIN orders o ON o.user_id = u.id WHERE o.id IS NULL;
💡 The LEFT JOIN ... WHERE right.id IS NULL pattern is the standard way to find "rows in A with no match in B" (anti-join).

Right Join

🗄️ MySQL · Joins · 2 min read

The mirror of LEFT JOIN — returns all rows from the right table plus matches from the left.

SELECT o.id, u.name FROM users u RIGHT JOIN orders o ON o.user_id = u.id; -- all orders, even those with no valid user
💡 RIGHT JOIN is rarely used in practice — most people reorder the tables and use LEFT JOIN for readability. Any RIGHT JOIN can be rewritten as a LEFT JOIN.

Full Join

🗄️ MySQL · Joins · 3 min read

Returns all rows from both tables, matching where possible. MySQL has no native FULL JOIN — emulate it with UNION of LEFT + RIGHT.

SELECT * FROM a LEFT JOIN b ON a.id = b.a_id UNION SELECT * FROM a RIGHT JOIN b ON a.id = b.a_id;
💡 PostgreSQL/SQL Server support FULL OUTER JOIN directly. In MySQL, the LEFT ∪ RIGHT UNION trick gives the same result.

Self Join

🗄️ MySQL · Joins · 3 min read

A table joined to itself (using aliases) — for hierarchical or comparative data within one table.

-- employees and their managers (same table) SELECT e.name AS employee, m.name AS manager FROM employees e LEFT JOIN employees m ON e.manager_id = m.id;
💡 The classic use: org charts (employee → manager), or comparing rows within a table (e.g. find pairs). Always alias the table twice to distinguish the two "copies."

Cross Join

🗄️ MySQL · Joins · 2 min read

Produces the Cartesian product — every row of A combined with every row of B (no ON condition).

SELECT s.size, c.color FROM sizes s CROSS JOIN colors c; -- all size/color combos
⚠️ Result size = rows(A) × rows(B) — explodes fast. An accidental cross join (forgetting the ON clause in a regular join) is a common cause of runaway queries. Use it intentionally for generating combinations.

Subqueries

🗄️ MySQL · Subqueries & Views · 4 min read

A subquery is a query nested inside another — in WHERE, FROM, or SELECT.

-- scalar subquery SELECT name FROM products WHERE price > (SELECT AVG(price) FROM products); -- IN subquery SELECT * FROM users WHERE id IN (SELECT user_id FROM orders); -- derived table (FROM subquery) SELECT c, n FROM (SELECT category_id c, COUNT(*) n FROM products GROUP BY category_id) t WHERE n > 5;
💡 Many subqueries can be rewritten as JOINs, which the optimizer often handles better. Use EXISTS instead of IN for large/correlated checks. CTEs (WITH, MySQL 8+) improve readability over nested subqueries.

Correlated Subqueries

🗄️ MySQL · Subqueries & Views · 4 min read

A correlated subquery references the outer query — so it re-runs for each outer row.

-- each user's orders above their own average SELECT o.* FROM orders o WHERE o.total > (SELECT AVG(o2.total) FROM orders o2 WHERE o2.user_id = o.user_id); -- EXISTS (correlated) — users who have at least one order SELECT * FROM users u WHERE EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id);
⚠️ Correlated subqueries run once per outer row — potentially slow on large datasets. Consider rewriting as a JOIN or using window functions (MySQL 8+). EXISTS short-circuits on the first match, often faster than IN here.

Views

🗄️ MySQL · Subqueries & Views · 8 min read · ★ Interview Topic

A view is a virtual table defined by a stored SELECT query. It doesn't store data — it runs its query each time you read it, presenting a simplified or secured slice of the underlying tables.

How a View Works — Visual

SELECT * FROM v View (virtual)stored SELECT orders users
CREATE VIEW active_customers AS SELECT u.id, u.name, COUNT(o.id) AS orders FROM users u JOIN orders o ON o.user_id = u.id WHERE u.status = 'active' GROUP BY u.id, u.name; SELECT * FROM active_customers WHERE orders > 5; -- query it like a table

Why Use Views

  • Simplify complex queries — hide joins/aggregations behind a name.
  • Security — expose only certain columns/rows; grant access to the view, not the tables.
  • Abstraction — change underlying tables without breaking consumers.
⚠️ A view runs its query every time (no stored data, so no speedup by itself). Some views are updatable (simple, single-table); complex ones (joins, aggregates, DISTINCT) are read-only. For cached results, use a summary table or materialized-view pattern (next).

Materialized Views (Concept)

🗄️ MySQL · Subqueries & Views · 4 min read

A materialized view physically stores the query result and refreshes periodically — trading freshness for read speed. MySQL has no native materialized views; you emulate them.

-- emulation: a summary table refreshed on a schedule/trigger CREATE TABLE mv_sales_daily (day DATE, total DECIMAL(12,2)); -- refresh via event/cron: REPLACE INTO mv_sales_daily SELECT DATE(created_at), SUM(total) FROM orders GROUP BY DATE(created_at);
💡 Regular views = always fresh, computed on read. Materialized = precomputed & stored (fast reads, can be stale). PostgreSQL/Oracle support them natively; in MySQL use a summary table refreshed by a scheduled event or triggers.

Stored Procedures

🗄️ MySQL · Programmable · ★ Interview Topic · 8 min read

A stored procedure is a named set of SQL statements stored in the database and executed with CALL. It encapsulates logic, supports parameters and control flow, and runs close to the data.

DELIMITER // CREATE PROCEDURE transfer(IN from_id BIGINT, IN to_id BIGINT, IN amt DECIMAL(10,2)) BEGIN DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN ROLLBACK; RESIGNAL; END; START TRANSACTION; UPDATE accounts SET balance = balance - amt WHERE id = from_id; UPDATE accounts SET balance = balance + amt WHERE id = to_id; COMMIT; END // DELIMITER ; CALL transfer(1, 2, 100.00);

✓ Pros

  • Logic close to data (less round-trips)
  • Reusable, parameterized
  • Can reduce network traffic
  • Centralized, can be permission-controlled

✗ Cons

  • Business logic in DB (hard to version/test)
  • DB-specific (poor portability)
  • Harder debugging
  • Can overload the DB server
💡 Use procedures for data-intensive batch operations and to enforce critical transactions atomically. Modern apps often keep business logic in the application layer (testable, portable) and reserve procedures for performance-critical DB work.

Functions (Stored)

🗄️ MySQL · Programmable · 3 min read

A stored function returns a single value and can be used inside SQL expressions (unlike a procedure).

DELIMITER // CREATE FUNCTION discounted_price(price DECIMAL(10,2), pct INT) RETURNS DECIMAL(10,2) DETERMINISTIC BEGIN RETURN price - (price * pct / 100); END // DELIMITER ; SELECT name, discounted_price(price, 10) FROM products;
ProcedureFunction
Returns0..N via OUT paramsExactly one value
Called byCALLInside SQL expressions
TransactionsYesNo (typically)
💡 Use a function when you need a reusable computed value inside queries; a procedure for multi-statement operations and transactions.

Triggers

🗄️ MySQL · Programmable · 4 min read

A trigger automatically runs in response to INSERT/UPDATE/DELETE on a table — for auditing, validation, or derived data.

CREATE TRIGGER audit_user_update AFTER UPDATE ON users FOR EACH ROW INSERT INTO user_audit(user_id, old_email, new_email, changed_at) VALUES (OLD.id, OLD.email, NEW.email, NOW());

Timing: BEFORE/AFTER; events: INSERT/UPDATE/DELETE. Use OLD. and NEW. to access row values.

⚠️ Triggers run invisibly — they make behaviour hard to trace and can hurt performance on bulk operations. Use sparingly (audit logs are a good fit); keep complex logic in the application layer.

Cursors

🗄️ MySQL · Programmable · 3 min read

A cursor iterates row-by-row over a result set inside a stored procedure — for procedural processing when set-based SQL won't do.

DECLARE done INT DEFAULT 0; DECLARE cur CURSOR FOR SELECT id FROM users; DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1; OPEN cur; read_loop: LOOP FETCH cur INTO uid; IF done THEN LEAVE read_loop; END IF; -- process each row END LOOP; CLOSE cur;
⚠️ Cursors are slow (row-by-row vs set-based). Almost always a single set-based SQL statement is faster and clearer — reach for cursors only when truly necessary.

Indexes

🗄️ MySQL · Indexes · ★ Interview Topic · 11 min read

An index is a data structure (usually a B+ tree) that speeds up data retrieval — like a book's index, it lets the DB find rows without scanning the whole table. The single biggest lever for query performance.

With vs Without an Index — Visual

No index → full table scan (O(n)): checks every row B+ tree index → log(n) lookup: root leaf pages → row pointers
CREATE INDEX idx_email ON users(email); CREATE UNIQUE INDEX uq_sku ON products(sku); CREATE INDEX idx_cat_price ON products(category_id, price); -- composite SHOW INDEX FROM users;

When to Index

  • Columns in WHERE, JOIN (FKs!), ORDER BY, GROUP BY.
  • High-selectivity columns (many distinct values) benefit most.
⚠️ Indexes speed reads but slow writes (each INSERT/UPDATE maintains them) and use disk. Don't over-index. They're useless if a query wraps the column in a function or uses a leading LIKE '%x'. Verify usage with EXPLAIN.
💡 Interview essentials: B+ tree structure, the read/write trade-off, composite-index left-prefix rule, and "covering index" (query satisfied entirely from the index). Index FK and frequent filter columns first.

Clustered Index

🗄️ MySQL · Indexes · 4 min read

A clustered index determines the physical order of rows on disk — the table is the index. In InnoDB, the primary key is the clustered index; rows are stored in PK order.

💡 Consequences in InnoDB: (1) there's exactly one clustered index (the PK); (2) PK lookups are very fast (data is right there); (3) a random PK (UUID) causes page splits/fragmentation on insert — prefer a monotonic AUTO_INCREMENT PK. Secondary indexes store the PK value to find the row.

Non-Clustered Index

🗄️ MySQL · Indexes · 4 min read

A non-clustered (secondary) index is a separate structure that stores the indexed column(s) + a pointer to the row (the PK in InnoDB). The table data stays in clustered (PK) order.

ClusteredNon-clustered
Per tableOne (the PK)Many
StoresThe actual rowsKey + PK pointer
LookupDirectIndex → PK → row (2 steps)
💡 A secondary-index lookup does a "bookmark lookup" (index → clustered index → row). A covering index (includes all needed columns) skips that second step — much faster.

Composite Index

🗄️ MySQL · Indexes · 4 min read

A composite (multi-column) index covers several columns. Column order matters — the leftmost-prefix rule.

CREATE INDEX idx_cat_price ON products(category_id, price); -- USES the index: WHERE category_id = 3 -- leftmost WHERE category_id = 3 AND price > 100 -- both -- does NOT use it: WHERE price > 100 -- skips the leading column
💡 Left-prefix rule: a composite index on (a, b, c) helps queries filtering on a, a+b, or a+b+c — but not b alone or c alone. Order columns by selectivity and query patterns (equality columns first, then range).

Unique Index

🗄️ MySQL · Indexes · 3 min read

A unique index enforces that all values in the column(s) are distinct — combining a constraint with index performance.

CREATE UNIQUE INDEX uq_email ON users(email); -- duplicate insert now fails with a duplicate-key error ALTER TABLE users ADD CONSTRAINT uq_email UNIQUE (email);
💡 A unique index both speeds lookups and guarantees uniqueness. Unlike a PK, it allows (one) NULL in MySQL and you can have several per table. The PK is a special unique + not-null + clustered index.

Query Optimization

🗄️ MySQL · Optimization · ★ Interview Topic · 9 min read

Query optimization is making queries run faster — primarily by helping the optimizer use indexes and read fewer rows. Always measure with EXPLAIN first.

Optimization Checklist

  • Index the right columns — WHERE, JOIN (FKs), ORDER BY, GROUP BY.
  • Avoid SELECT * — fetch only needed columns (enables covering indexes).
  • Keep WHERE sargable — no functions on indexed columns, no leading LIKE '%'.
  • Prefer JOINs over correlated subqueries; use EXISTS over IN for big sets.
  • Limit result size — paginate; use keyset pagination for deep pages.
  • Batch inserts/updates; avoid N+1 query patterns from the app.
-- Bad: function on column disables index WHERE DATE(created_at) = '2024-01-01' -- Good: sargable range WHERE created_at >= '2024-01-01' AND created_at < '2024-01-02'
💡 The optimizer picks the plan; your job is to give it good indexes and sargable queries. Process: run EXPLAIN → spot full scans/filesorts → add/fix an index → re-measure. 90% of slow queries are a missing index or a non-sargable WHERE.

Explain Plan

🗄️ MySQL · Optimization · ★ Interview Topic · 8 min read

EXPLAIN shows how MySQL will execute a query — which indexes it uses, the join order, and how many rows it estimates scanning. The first tool for diagnosing slow queries.

EXPLAIN SELECT * FROM orders WHERE user_id = 5; EXPLAIN ANALYZE SELECT ...; -- MySQL 8: actual timings

Key Columns

ColumnWhat to look at
typeAccess type: const/eq_ref/ref = good · index/ALL = scan (bad)
keyWhich index is used (NULL = none!)
rowsEstimated rows examined (lower = better)
ExtraUsing index (covering, good) · Using filesort/temporary (costly)
💡 Red flags: type=ALL (full scan), key=NULL (no index), huge rows, or Using filesort/Using temporary in Extra. The fix is usually a better index or a sargable rewrite. Know the type ladder: system > const > eq_ref > ref > range > index > ALL.

Query Execution Plan

🗄️ MySQL · Optimization · 4 min read

The execution plan is the concrete strategy the optimizer chooses to run a query — access methods, join algorithm, and order. EXPLAIN exposes it.

  • Access methods — index lookup, range scan, full scan.
  • Join algorithm — nested-loop (default), hash join (MySQL 8+).
  • Join order — the optimizer reorders tables for efficiency.
EXPLAIN FORMAT=JSON SELECT ...; -- detailed cost breakdown
💡 The optimizer is cost-based — it uses table/index statistics. If plans look wrong, run ANALYZE TABLE to refresh statistics. Use optimizer hints only as a last resort.

SQL Performance Tuning

🗄️ MySQL · Optimization · ★ Interview Topic · 9 min read

Performance tuning spans queries, schema, indexes, server config, and the application. Measure first — find the actual bottleneck before changing anything.

Queries & Indexes

Fix missing indexes, sargable WHERE, avoid SELECT *, covering indexes — the biggest wins.

Schema

Right data types, normalize then selectively denormalize, partition huge tables.

Server config

Tune innodb_buffer_pool_size (~70% RAM), connections, log settings.

App layer

Connection pooling, caching (Redis), eliminate N+1 queries, batch writes.

Read scaling

Read replicas, query cache (external), materialized summary tables.

Diagnose

Slow query log, EXPLAIN ANALYZE, Performance Schema, SHOW PROCESSLIST.

💡 The biggest single lever is almost always indexing + sargable queries. Next: innodb_buffer_pool_size (keep hot data in RAM) and killing N+1 patterns from the app. Enable the slow query log to find the worst offenders.

Normalization (1NF, 2NF, 3NF)

🗄️ MySQL · Normalization · ★ Interview Topic · 11 min read

Normalization organizes columns and tables to minimize redundancy and prevent update/insert/delete anomalies, by progressively applying normal forms.

The Normal Forms — Visual

1NFatomic values 2NFno partial dependency 3NFno transitive dependency BCNFstricter 3NF

Summary

FormRule
1NFAtomic columns, no repeating groups, a primary key
2NF1NF + no partial dependency (non-key cols depend on the whole PK)
3NF2NF + no transitive dependency (non-key cols depend only on the key)
BCNFStricter 3NF — every determinant is a candidate key
💡 Mnemonic: "the key, the whole key, and nothing but the key" (1NF→2NF→3NF). Most schemas aim for 3NF, then denormalize selectively for read performance. Higher normal forms reduce redundancy but add joins.

1NF — First Normal Form

🗄️ MySQL · Normalization · 3 min read

A table is in 1NF when every column holds atomic (indivisible) values — no repeating groups or lists in a cell — and each row is unique (has a PK).

-- NOT 1NF: phones in one column | id | name | phones | | 1 | Sam | "111, 222" | -- 1NF: one value per cell (separate rows or table) | user_id | phone | | 1 | 111 | | 1 | 222 |
💡 The fix for non-atomic data is usually a separate child table (one-to-many). Storing comma-separated lists is the classic 1NF violation.

2NF — Second Normal Form

🗄️ MySQL · Normalization · 3 min read

1NF + no partial dependency: every non-key column depends on the entire primary key (only relevant with composite PKs).

-- PK = (order_id, product_id). product_name depends only on product_id → partial! | order_id | product_id | product_name | qty | -- 2NF: move product_name to a products table order_items(order_id, product_id, qty) products(product_id, product_name)
💡 2NF only matters when the PK is composite. The cure: split out columns that depend on just part of the key into their own table.

3NF — Third Normal Form

🗄️ MySQL · Normalization · 3 min read

2NF + no transitive dependency: non-key columns depend only on the key, not on other non-key columns.

-- dept_name depends on dept_id (a non-key col) → transitive | emp_id | name | dept_id | dept_name | -- 3NF: split department out employees(emp_id, name, dept_id) departments(dept_id, dept_name)
💡 3NF is the practical target for most OLTP schemas — it removes the redundancy that causes update anomalies (changing a dept name in one place, not hundreds of rows).

BCNF — Boyce-Codd Normal Form

🗄️ MySQL · Normalization · 3 min read

A stricter version of 3NF: for every functional dependency X → Y, X must be a candidate key. It handles edge cases 3NF misses (overlapping candidate keys).

💡 Most 3NF tables are already BCNF. BCNF matters in rare cases with multiple overlapping candidate keys. For interviews, knowing it's "3NF where every determinant is a candidate key" is enough.

Denormalization

🗄️ MySQL · Normalization · 4 min read

Denormalization deliberately adds redundancy (duplicated columns, precomputed aggregates) to a normalized schema to speed up reads by avoiding joins.

-- store order_count on users to avoid COUNT(*) joins on every read ALTER TABLE users ADD COLUMN order_count INT DEFAULT 0; -- keep it in sync via triggers / app logic / scheduled refresh
⚠️ Denormalization trades write complexity & consistency risk for read speed — you must keep the duplicated data in sync. Normalize first (3NF), then denormalize only proven read hotspots. Common in reporting/analytics and read-heavy systems.

Primary Key vs Unique Key

🗄️ MySQL · Keys & Constraints · ★ Interview Topic · 7 min read

Both enforce uniqueness, but a primary key is the single, non-null, canonical identifier of a row, while a unique key just guarantees distinct values on other columns.

Primary KeyUnique Key
Per tableExactly oneMany allowed
NULLsNot allowedOne NULL allowed (MySQL)
IndexClustered (InnoDB)Secondary (non-clustered)
PurposeRow identityEnforce distinctness on a column
CREATE TABLE users ( id BIGINT AUTO_INCREMENT PRIMARY KEY, -- the identifier email VARCHAR(120) NOT NULL UNIQUE -- unique but not the PK );
💡 Interview answer: a table has one PK (non-null, the clustered index, the identity); it can have many unique keys (allow a NULL, are secondary indexes). Prefer a surrogate AUTO_INCREMENT PK and put a UNIQUE on natural keys like email.

Foreign Key

🗄️ MySQL · Keys & Constraints · 4 min read

A foreign key links a column to another table's primary key, enforcing referential integrity — you can't reference a row that doesn't exist.

CREATE TABLE orders ( id BIGINT AUTO_INCREMENT PRIMARY KEY, user_id BIGINT NOT NULL, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE -- delete orders when user is deleted ON UPDATE RESTRICT );
ActionOn parent delete/update
CASCADEApply same change to children
RESTRICT / NO ACTIONBlock if children exist
SET NULLNull the child FK
💡 FKs require InnoDB. Always index the FK column (MySQL doesn't auto-index it on the child side) — unindexed FKs make joins and cascade checks slow.

Unique Key

🗄️ MySQL · Keys & Constraints · 3 min read

A unique key constraint ensures all values in a column (or set of columns) are distinct — preventing duplicates like two users with the same email.

ALTER TABLE users ADD CONSTRAINT uq_email UNIQUE (email); ALTER TABLE bookings ADD UNIQUE (room_id, date); -- composite unique
💡 A composite unique key enforces uniqueness on the combination (e.g. one booking per room per date). It also creates an index. Allows a single NULL per column in MySQL.

Composite Key

🗄️ MySQL · Keys & Constraints · ★ Interview Topic · 6 min read

A composite key is a primary (or unique) key made of two or more columns — used when no single column uniquely identifies a row. Common in join/junction tables.

-- junction table for many-to-many: PK = both FKs CREATE TABLE student_course ( student_id BIGINT, course_id BIGINT, enrolled_at DATETIME, PRIMARY KEY (student_id, course_id), -- composite PK FOREIGN KEY (student_id) REFERENCES students(id), FOREIGN KEY (course_id) REFERENCES courses(id) );
💡 The composite PK both identifies the row and prevents duplicate enrollments. Column order matters for the resulting index (left-prefix rule) — put the most-queried column first. Many teams still prefer a surrogate id PK + a composite UNIQUE for simpler FKs.

Constraints

🗄️ MySQL · Keys & Constraints · 4 min read

Constraints enforce data integrity rules at the database level — so invalid data can never be stored, regardless of the application.

ConstraintEnforces
PRIMARY KEYUnique + not null identity
FOREIGN KEYReferential integrity
UNIQUENo duplicate values
NOT NULLValue required
CHECKCustom condition (MySQL 8+)
DEFAULTFallback value
CREATE TABLE products ( price DECIMAL(10,2) NOT NULL CHECK (price >= 0), status VARCHAR(20) DEFAULT 'active' );
💡 Enforce integrity at the DB (constraints) and validate in the app — defense in depth. The DB is the last line of defense and guarantees consistency even if app validation is bypassed.

Relationships

🗄️ MySQL · Relationships · 4 min read

Relationships model how entities relate, implemented with foreign keys. Three cardinalities:

TypeImplementationExample
One-to-OneFK with UNIQUE (or shared PK)User ↔ Profile
One-to-ManyFK on the "many" sideUser → Orders
Many-to-ManyJunction tableStudents ↔ Courses
💡 The FK always lives on the "many" side. Cardinality drives the schema: get this right at design time — it's painful to change later.

One-to-One

🗄️ MySQL · Relationships · 3 min read

Each row in A relates to at most one row in B (and vice versa) — e.g. a user and their profile.

CREATE TABLE profiles ( user_id BIGINT PRIMARY KEY, -- PK = FK enforces 1:1 bio TEXT, FOREIGN KEY (user_id) REFERENCES users(id) );
💡 Implement 1:1 by making the FK also UNIQUE (or the PK). Use it to split rarely-used/large columns (e.g. BLOBs) off the main table for performance.

One-to-Many

🗄️ MySQL · Relationships · ★ Interview Topic · 6 min read

The most common relationship: one parent row relates to many child rows — e.g. one user has many orders. The FK goes on the "many" side.

users (1) order (user_id FK) order (user_id FK) order (user_id FK) 1 → many
CREATE TABLE orders ( id BIGINT AUTO_INCREMENT PRIMARY KEY, user_id BIGINT NOT NULL, -- FK on the "many" side FOREIGN KEY (user_id) REFERENCES users(id) ); CREATE INDEX idx_user ON orders(user_id); -- always index the FK
💡 In JPA this maps to @OneToMany/@ManyToOne. Always index the FK column — it's used in every join and cascade. The "one" side has no FK; the "many" side carries it.

Many-to-Many

🗄️ MySQL · Relationships · ★ Interview Topic · 7 min read

Rows in A relate to many in B and vice versa — e.g. students and courses. SQL can't model this directly; you use a junction (join) table.

students student_course(student_id, course_id) courses junction table = two 1-to-many relationships
CREATE TABLE student_course ( student_id BIGINT, course_id BIGINT, PRIMARY KEY (student_id, course_id), -- composite PK prevents dup enrollment FOREIGN KEY (student_id) REFERENCES students(id), FOREIGN KEY (course_id) REFERENCES courses(id) );
💡 A many-to-many is always resolved into two one-to-many relationships via a junction table. The junction can also hold relationship attributes (e.g. enrolled_at, grade). In JPA: @ManyToMany with @JoinTable.

Transactions

🗄️ MySQL · Transactions · ★ Interview Topic · 9 min read

A transaction is a sequence of operations executed as a single, all-or-nothing unit. Either every statement commits, or none does (rollback) — keeping data consistent.

START TRANSACTION; UPDATE accounts SET balance = balance - 100 WHERE id = 1; UPDATE accounts SET balance = balance + 100 WHERE id = 2; -- if anything fails: ROLLBACK; -- undo everything -- else: COMMIT; -- make permanent

The Money-Transfer Example

Debiting one account and crediting another must happen together. Without a transaction, a crash between the two updates loses money. The transaction guarantees both or neither.

💡 Transactions need a transactional engine — InnoDB (not MyISAM). They're governed by the four ACID properties and tuned by isolation levels (next topics). In Spring, @Transactional manages this for you.

ACID Properties

🗄️ MySQL · Transactions · ★ Interview Topic · 8 min read

ACID is the set of four guarantees that make database transactions reliable.

AAtomicity CConsistency IIsolation DDurability
PropertyGuarantee
AtomicityAll operations succeed or all roll back (no partial)
ConsistencyMoves DB from one valid state to another (constraints hold)
IsolationConcurrent transactions don't interfere (per isolation level)
DurabilityCommitted data survives crashes (written to disk/log)
💡 InnoDB delivers ACID: atomicity/durability via the redo/undo logs (write-ahead logging), isolation via MVCC + locking. This is the key reason to use InnoDB over MyISAM. Contrast with the CAP theorem for distributed systems.

Commit

🗄️ MySQL · Transactions · 2 min read

COMMIT makes all changes in the current transaction permanent and visible to other transactions.

START TRANSACTION; INSERT INTO orders ...; COMMIT; -- durable; can't be undone
💡 By default MySQL runs in autocommit=1 — each statement is its own committed transaction. START TRANSACTION suspends autocommit until you COMMIT/ROLLBACK.

Rollback

🗄️ MySQL · Transactions · 2 min read

ROLLBACK undoes all changes since the transaction began (or since a savepoint) — restoring the prior state.

START TRANSACTION; UPDATE accounts SET balance = balance - 100 WHERE id = 1; -- error detected! ROLLBACK; -- the update is undone
💡 Rollback works via InnoDB's undo log. DDL statements (CREATE/DROP/ALTER) auto-commit and cannot be rolled back — only DML can.

Savepoint

🗄️ MySQL · Transactions · 3 min read

A savepoint marks a point within a transaction you can partially roll back to, without aborting the whole transaction.

START TRANSACTION; INSERT INTO log ...; SAVEPOINT sp1; UPDATE risky ...; ROLLBACK TO sp1; -- undo only the UPDATE, keep the INSERT COMMIT;
💡 Savepoints enable nested/partial rollback — useful in long procedures where you want to retry one step without losing earlier work. Spring's nested propagation uses savepoints.

Isolation Levels

🗄️ MySQL · Transactions · ★ Interview Topic · 9 min read

Isolation levels control how/when one transaction's changes become visible to others — trading consistency against concurrency. Each level prevents specific read anomalies.

Levels vs Anomalies — Visual

LevelDirty readNon-repeatablePhantom
READ UNCOMMITTED❌ possible
READ COMMITTED✅ prevented
REPEATABLE READ (InnoDB default)✅*
SERIALIZABLE
SET TRANSACTION ISOLATION LEVEL READ COMMITTED; SELECT @@transaction_isolation;
💡 Higher isolation = more consistency, less concurrency (more locking). InnoDB's default is REPEATABLE READ, and thanks to MVCC + gap locks it largely prevents phantoms too (*) — stronger than the SQL standard requires. Most apps are fine on the default or READ COMMITTED.

Dirty Reads

🗄️ MySQL · Transactions · 3 min read

A dirty read happens when a transaction reads data that another transaction has modified but not yet committed — and that change may later be rolled back.

T1: UPDATE accounts SET balance = 0 WHERE id=1; -- not committed T2: SELECT balance FROM accounts WHERE id=1; -- reads 0 (dirty!) T1: ROLLBACK; -- the 0 never really existed
💡 Only possible at READ UNCOMMITTED. Every higher level (READ COMMITTED and up) prevents dirty reads — you only see committed data.

Non-Repeatable Reads

🗄️ MySQL · Transactions · 3 min read

A non-repeatable read occurs when a transaction reads the same row twice and gets different values, because another committed transaction changed it in between.

T1: SELECT balance FROM accounts WHERE id=1; -- 100 T2: UPDATE accounts SET balance=50 WHERE id=1; COMMIT; T1: SELECT balance FROM accounts WHERE id=1; -- 50 (changed!)
💡 Prevented at REPEATABLE READ and above (InnoDB's default), where a transaction sees a consistent snapshot for its whole duration via MVCC.

Phantom Reads

🗄️ MySQL · Transactions · 3 min read

A phantom read occurs when a transaction re-runs a range query and finds new rows that another committed transaction inserted matching the condition.

T1: SELECT * FROM orders WHERE total > 100; -- 5 rows T2: INSERT INTO orders (total) VALUES (200); COMMIT; T1: SELECT * FROM orders WHERE total > 100; -- 6 rows (phantom!)
💡 Fully prevented at SERIALIZABLE. InnoDB's REPEATABLE READ also blocks most phantoms using gap/next-key locks — going beyond the SQL standard. The three anomalies (dirty → non-repeatable → phantom) map directly to the isolation ladder.

Locking

🗄️ MySQL · Concurrency · ★ Interview Topic · 9 min read

Locks coordinate concurrent access to data so transactions don't corrupt each other. MySQL/InnoDB uses locks at different granularities and modes.

Lock Modes

ModeMeaning
Shared (S) — read lockMany can read; blocks writers
Exclusive (X) — write lockOne writer; blocks everyone
Row-levelLocks individual rows (InnoDB)
Table-levelLocks the whole table (MyISAM)
SELECT * FROM accounts WHERE id=1 FOR UPDATE; -- exclusive row lock SELECT * FROM accounts WHERE id=1 FOR SHARE; -- shared row lock

Optimistic vs Pessimistic

  • Pessimistic — lock rows up front (FOR UPDATE); safe but reduces concurrency.
  • Optimistic — no locks; detect conflicts at commit via a version column. Better for low-contention.
💡 InnoDB does row-level locking + MVCC, so readers don't block writers and vice versa (huge concurrency win over MyISAM's table locks). Locks held too long cause contention and deadlocks (next).

Row-Level Locking

🗄️ MySQL · Concurrency · 3 min read

InnoDB locks only the specific rows a transaction touches, so other transactions can work on different rows concurrently — maximizing throughput.

💡 Row locks are set on index records. A query without a good index may lock many rows (or escalate effort) — another reason indexing matters. Includes record, gap, and next-key locks (the latter prevent phantoms).

Table-Level Locking

🗄️ MySQL · Concurrency · 3 min read

The entire table is locked for a write — used by MyISAM. Simple and low-overhead, but it serializes writes (only one writer at a time).

Row-level (InnoDB)Table-level (MyISAM)
ConcurrencyHighLow (one writer)
OverheadHigher (track many locks)Very low
Best forOLTP, mixed read/writeRead-heavy, few writes
💡 Table locking kills write concurrency — a key reason InnoDB replaced MyISAM as the default. You can still take explicit table locks (LOCK TABLES) but rarely should.

Deadlocks

🗄️ MySQL · Concurrency · ★ Interview Topic · 8 min read

A deadlock occurs when two transactions each hold a lock the other needs — neither can proceed. InnoDB detects this and rolls back one transaction (the "victim").

Txn 1holds A, wants B Txn 2holds B, wants A Row A Row B

Prevention

  • Consistent lock ordering — always acquire rows/tables in the same order (breaks the cycle).
  • Keep transactions short — hold locks briefly.
  • Right indexes — fewer rows locked.
  • Retry logic — catch the deadlock error (1213) and retry the transaction.
SHOW ENGINE INNODB STATUS; -- "LATEST DETECTED DEADLOCK" section
💡 InnoDB auto-detects deadlocks and rolls back the cheaper transaction — your app must catch error 1213 and retry. The most reliable prevention is consistent lock ordering + short transactions.

Concurrency Control

🗄️ MySQL · Concurrency · 4 min read

Concurrency control lets many transactions run simultaneously while preserving consistency. InnoDB combines MVCC with locking.

  • MVCC (Multi-Version Concurrency Control) — readers see a consistent snapshot without locking; writers create new row versions. So reads don't block writes.
  • Locking — for writes and explicit locks (FOR UPDATE).
  • Isolation levels — tune the consistency/concurrency trade-off.
💡 MVCC is why InnoDB scales: a long-running report (read) doesn't block transactional writes. Each transaction reads the snapshot as of its start (REPEATABLE READ) or statement (READ COMMITTED).

InnoDB Storage Engine

🗄️ MySQL · Storage Engines · ★ Interview Topic · 8 min read

InnoDB is MySQL's default storage engine — transactional, ACID-compliant, row-locking, with foreign keys and crash recovery. The right choice for almost all applications.

Key Features

ACID Transactions

Commit/rollback via redo & undo logs (write-ahead logging) + crash recovery.

Row-Level Locking + MVCC

High concurrency; readers don't block writers.

Foreign Keys

Referential integrity enforced (MyISAM can't).

Clustered Index

Data stored in PK order; buffer pool caches hot pages in RAM.

InnoDBMyISAM
TransactionsYes (ACID)No
LockingRow-levelTable-level
Foreign keysYesNo
Crash recoveryYesWeak
💡 Always use InnoDB unless you have a specific legacy reason. Tune innodb_buffer_pool_size to ~70% of RAM — keeping hot data/indexes in memory is the single biggest InnoDB performance setting.

MyISAM Storage Engine

🗄️ MySQL · Storage Engines · 3 min read

MyISAM is the older, non-transactional engine — fast for read-heavy workloads but lacks transactions, foreign keys, and row-level locking.

⚠️ MyISAM has no transactions, no FKs, table-level locking, and weak crash recovery — avoid it for anything that writes concurrently or needs reliability. It survives mainly in legacy systems and some read-only/full-text use cases (InnoDB now has full-text too).
💡 Interview answer: choose InnoDB for OLTP/transactional integrity (the default); MyISAM only for legacy or pure read-only data where its simplicity is acceptable.

Partitioning

🗄️ MySQL · Scaling & HA · ★ Interview Topic · 8 min read

Partitioning splits one large table into smaller physical pieces (within a single server) based on a key, while it still looks like one table. Improves query performance and maintenance on huge tables.

Partition Types

TypeSplits by
RANGEValue ranges (e.g. by year)
LISTDiscrete value sets (e.g. region)
HASHHash of a column (even distribution)
KEYLike HASH, MySQL-managed
CREATE TABLE orders ( id BIGINT, created_at DATE, ... ) PARTITION BY RANGE (YEAR(created_at)) ( PARTITION p2023 VALUES LESS THAN (2024), PARTITION p2024 VALUES LESS THAN (2025), PARTITION pmax VALUES LESS THAN MAXVALUE );
💡 Partition pruning is the win — a query filtering on the partition key only scans relevant partitions. Also makes archiving easy (DROP PARTITION p2023 is instant). Partitioning ≠ sharding: partitioning is one server, sharding spans many.

Replication

🗄️ MySQL · Scaling & HA · ★ Interview Topic · 9 min read

Replication copies data from a primary (master) to one or more replicas (slaves). It enables read scaling, high availability, and backups without loading the primary.

Primary (writes)binary log Replica (reads) Replica (reads) Replica (reads) async replication of binlog

How It Works

  • Primary records changes in its binary log (binlog).
  • Replicas pull and replay the binlog to stay in sync.
  • Async (default, fast, possible lag) vs semi-sync (waits for one replica) vs group replication (synchronous, HA).
⚠️ Async replication has replication lag — a read replica may be slightly stale. Don't read-your-own-write from a replica right after writing to the primary. App must route writes → primary, reads → replicas.

Master-Slave Replication

🗄️ MySQL · Scaling & HA · 4 min read

The classic topology: one primary handles all writes, multiple replicas serve reads. Scales read-heavy workloads and provides a failover target.

TopologyNotes
Master-Slave1 writer, N read replicas (read scaling)
Master-Master2 writers (conflict risk; needs care)
Group ReplicationSynchronous multi-node, auto-failover (HA)
💡 Read scaling is easy (add replicas); write scaling is hard (one primary) — that's where sharding comes in. On primary failure, promote a replica (manual, or automated with tools like Orchestrator / MySQL InnoDB Cluster).

Sharding

🗄️ MySQL · Scaling & HA · ★ Interview Topic · 9 min read

Sharding splits data horizontally across multiple servers (shards), each holding a subset of rows. It's how you scale writes and storage beyond one machine.

Shard router (by key) Shard 1 (users 0-1M) Shard 2 (1M-2M) Shard 3 (2M-3M)

Strategies & Trade-offs

  • Range / Hash / Directory sharding by a shard key (e.g. user_id).
  • Choosing a good shard key is critical — avoid hotspots and cross-shard queries.
⚠️ Sharding adds major complexity: cross-shard joins/transactions are hard, rebalancing is painful, and JOINs across shards may need app-side merging. Exhaust vertical scaling, read replicas, and caching first. Partitioning (one server) ≠ sharding (many servers).

High Availability (HA) Databases

🗄️ MySQL · Scaling & HA · ★ Interview Topic · 8 min read

High availability means the database keeps serving through failures — minimizing downtime via redundancy and automatic failover.

HA Building Blocks

Replication

Replicas as standby; promote one if the primary fails.

Automatic Failover

InnoDB Cluster (Group Replication) + MySQL Router, or Orchestrator.

Multi-AZ

Spread nodes across availability zones (cloud RDS/Aurora).

Backups + PITR

Point-in-time recovery from backups + binlog.

💡 Measured by RTO (recovery time) & RPO (data loss window). Managed options (AWS RDS Multi-AZ, Aurora) provide HA with automatic failover out of the box. MySQL InnoDB Cluster = Group Replication + MySQL Router + Shell for self-hosted HA.

Database Scalability

🗄️ MySQL · Scaling & HA · ★ Interview Topic · 9 min read

Scalability is handling growing load. Databases scale vertically (bigger server) or horizontally (more servers), with reads and writes scaling very differently.

The Scaling Ladder — Visual

1. Verticalbigger box 2. Indexing/Cache+ Redis 3. Read Replicasscale reads 4. Partitionbig tables 5. Shardscale writes
💡 Practical order: tune queries/indexes → add caching (Redis) → vertical scale → read replicas (read scaling) → partition large tables → shard (write scaling, last resort). Reads scale easily; writes are the hard limit — that's the core scalability insight.

Backup and Restore

🗄️ MySQL · Operations · 4 min read

Backups protect against data loss. MySQL offers logical (SQL dump) and physical (file copy) backups.

# logical backup (portable, slower) mysqldump -u root -p --single-transaction shop > shop.sql mysql -u root -p shop < shop.sql # restore # physical/hot backup (fast, large DBs) xtrabackup --backup --target-dir=/backups
TypeToolBest for
Logicalmysqldump / mysqlpumpSmall/medium, portable
PhysicalPercona XtraBackupLarge DBs, fast restore
Point-in-timeBackup + binlogRestore to an exact moment
⚠️ Use --single-transaction for consistent InnoDB dumps without locking. An untested backup is not a backup — regularly practice restores. Combine full backups + binlog for point-in-time recovery.

Database Security

🗄️ MySQL · Operations · 4 min read
  • Least privilege — app users get only needed grants; never use root from apps.
  • Encrypt — TLS in transit; encryption at rest (TDE) for sensitive data.
  • No SQL injection — always use parameterized queries / prepared statements.
  • Network — bind to private interfaces, firewall the port (3306), no public exposure.
  • Audit — enable the audit log; rotate credentials; remove default/anonymous accounts.
💡 The biggest real-world risks: SQL injection (fix with prepared statements), over-privileged accounts, and databases exposed to the internet. Run mysql_secure_installation on new installs.

User Management

🗄️ MySQL · Operations · 3 min read
CREATE USER 'app'@'%' IDENTIFIED BY 'strong_password'; GRANT SELECT, INSERT, UPDATE, DELETE ON shop.* TO 'app'@'%'; ALTER USER 'app'@'%' IDENTIFIED BY 'new_password'; DROP USER 'app'@'%'; SHOW GRANTS FOR 'app'@'%';
💡 The 'user'@'host' form scopes where a user can connect from — restrict the host (e.g. app subnet) rather than % (anywhere) in production.

Privileges and Roles

🗄️ MySQL · Operations · 3 min read

Privileges grant specific actions; roles (MySQL 8+) bundle privileges for easy assignment.

CREATE ROLE 'readwrite'; GRANT SELECT, INSERT, UPDATE, DELETE ON shop.* TO 'readwrite'; GRANT 'readwrite' TO 'app'@'%'; SET DEFAULT ROLE ALL TO 'app'@'%';
💡 Roles (MySQL 8+) make privilege management scalable — define a role once, assign to many users. Always follow least privilege; grant at the narrowest scope (db.table) needed.

Connection Pooling

🗄️ MySQL · Operations · 4 min read

Opening a DB connection is expensive. A connection pool keeps a set of reusable connections open, handing them to the app on demand — drastically cutting latency and load.

# Spring Boot uses HikariCP by default spring.datasource.hikari.maximum-pool-size=20 spring.datasource.hikari.minimum-idle=5 spring.datasource.hikari.connection-timeout=3000
⚠️ Size the pool carefully — too small starves the app; too large overwhelms MySQL (each connection uses memory + a thread). A common formula: pool ≈ (core_count × 2) + effective_spindles; measure under load. HikariCP is the fast default in Spring Boot.

Database Monitoring

🗄️ MySQL · Operations · 4 min read

Monitor health and performance to catch problems early.

WhatHow
Slow queriesSlow query log + pt-query-digest
Live queriesSHOW PROCESSLIST
InternalsPerformance Schema, SHOW ENGINE INNODB STATUS
Metrics/alertsPrometheus mysqld_exporter + Grafana, PMM
💡 Key metrics: query latency, slow-query count, connections, buffer pool hit ratio, replication lag, locks/deadlocks. Enable the slow query log first — it surfaces the worst offenders immediately.

Database Migration

🗄️ MySQL · Operations · 4 min read

Schema migrations evolve the database structure over time in a controlled, versioned, repeatable way.

# Flyway versioned migration files V1__create_users.sql V2__add_email_index.sql V3__add_orders_table.sql # Flyway/Liquibase apply pending migrations in order, tracked in a metadata table
💡 Keep migrations in version control alongside code (Flyway/Liquibase). For large tables, use online schema change tools (pt-online-schema-change, gh-ost) to avoid long locks. Always make migrations backward-compatible for zero-downtime deploys.

Data Archiving

🗄️ MySQL · Operations · 3 min read

Archiving moves old, rarely-accessed data out of hot tables into archive storage — keeping the active dataset small and fast.

# move old rows to an archive table, then delete in batches INSERT INTO orders_archive SELECT * FROM orders WHERE created_at < '2022-01-01'; DELETE FROM orders WHERE created_at < '2022-01-01' LIMIT 1000; # batched
💡 Smaller hot tables = faster queries, indexes, and backups. Partitioning makes archiving trivial — just DROP PARTITION for old date ranges. Define a retention policy aligned with compliance needs.

SQL Best Practices

🗄️ MySQL · Best Practices · 4 min read

✓ Do

  • Name explicit columns (no SELECT *)
  • Parameterize queries (prevent injection)
  • Index WHERE/JOIN/ORDER BY columns
  • Keep WHERE sargable (no fn on columns)
  • Paginate large result sets
  • Use transactions for multi-step writes
  • EXPLAIN slow queries

✗ Avoid

  • SELECT * in app code
  • String-concatenated SQL
  • Functions on indexed columns in WHERE
  • N+1 query patterns
  • Huge OFFSET pagination
  • Unbounded queries (no LIMIT)
  • Implicit cross joins
💡 The recurring theme: be explicit, be sargable, be indexed, be safe (parameterized + transactional). These habits prevent most performance and security problems.

Schema Design Best Practices

🗄️ MySQL · Best Practices · 4 min read
  • Normalize to 3NF, then denormalize selectively for read hotspots.
  • Surrogate PKs (BIGINT AUTO_INCREMENT); UNIQUE on natural keys.
  • Right data types — smallest that fits; DECIMAL for money; UTC DATETIME/TIMESTAMP.
  • Constraints — NOT NULL, FK, UNIQUE, CHECK to enforce integrity in the DB.
  • Index for access patterns — FKs and frequent filters; avoid over-indexing.
  • Consistent naming — snake_case, plural tables, id/*_id conventions.
  • Plan for scale — partition/archive large tables; avoid unbounded columns.
💡 Good schema design is the foundation everything else rests on — it's far cheaper to get right up front than to refactor a live production database later.

CAP Theorem & Consistency vs Availability

🗄️ MySQL · Best Practices & Theory · ★ Interview Topic · 8 min read

The CAP theorem states a distributed data store can guarantee at most two of: Consistency, Availability, Partition tolerance. Since network partitions are unavoidable, the real choice under a partition is C vs A.

Consistency Availability Partition tol. CP AP CA (single node) pick 2
ChoiceMeaningExamples
CPConsistent + partition-tolerant (may reject requests)MySQL Cluster, HBase, MongoDB
APAvailable + partition-tolerant (may be stale)Cassandra, DynamoDB
CAOnly without partitions (single node)Single MySQL instance

Consistency vs Availability Trade-off

During a network partition you must choose: refuse to answer to stay consistent (CP), or answer with possibly-stale data to stay available (AP). Many systems offer eventual consistency (AP) or tunable consistency.

💡 A single MySQL server is effectively CA (no partitions within one node). Once you add replication/clustering across the network, you face CAP: async replicas favour availability (stale reads possible = AP-ish); synchronous group replication favours consistency (CP). Relational DBs lean CP; many NoSQL stores lean AP. Modern nuance: the PACELC theorem adds the latency-vs-consistency trade-off even when there's no partition.