Skip to content

DB Principles (Index / Transaction / Query Optimization)

Core

Sao Excel query 10s, Shopee search 0.01s? Khi data từ "vài nghìn" → "vài tỷ", "1 người" → "triệu người concurrent", Excel chết. DB = "super Excel" chuyên cho big data + high concurrency.


1. Sao cần "DB"?

1.1 Từ quán sách nhỏ → Shopee

Quán sách nhỏ, vài ghi sổ:

2024-01-15: An mua "Đắc Nhân Tâm" 80k
2024-01-16: Linh mua "Sapiens" 200k

Đủ. Nhưng "Shopee" với triệu order/ngày:

  • Volume: vài tỷ row
  • Concurrent: vài chục triệu access cùng lúc
  • Relation: order ↔ user ↔ product ↔ stock ↔ logistics
  • Safety: mất điện không mất order
Excel DB
Cá nhân / team nhỏEnterprise
Vài nghìn rowTỷ+
1 người sequentialTriệu concurrent
Manual, chậmms query

DB giải: store hiệu quả, query nhanh, manage safely big data.

1.2 Pit: sao không Excel cho user data

Story 1 startup

Linh làm social app, đầu dùng Excel store user. 100k user → vấn đề:

  • Excel mở 5 phút
  • Filter "Hà Nội user" lag
  • File corrupt, vài nghìn user data mất vĩnh viễn

Chí mạng: muốn "xem mọi order của 1 user" — user info và order ở Excel khác nhau, phải copy-paste tay, 30 phút mỗi lần.

Senior xem: "Bạn không cần Excel, mà DB."

Đổi DB:

  • "Hà Nội user" 0.01s
  • JOIN auto user + order
  • Auto backup

Bài học: data nhỏ Excel OK, data lớn = thảm hoạ.


2. Core: Table, Row, Column, Primary Key

Ẩn dụ thư viện:

Concept Thư việnUseVd
DatabaseCả thư việnContainer mọi dataDB của e-commerce
TableKệ sáchLoại data cùng kiểuusers, products, orders
ColumnLabel spine sáchAttributename, age, phone
RowMỗi cuốn sách1 record"Hoàng, 25, HCM"
Primary KeyISBN mỗi cuốnID unique mỗi rowuser_id = 1001

Vd users table:

user_id (PK)nameagecityemail
1001Hoàng25HCMh@example.com
1002Linh30HNl@example.com
1003An28HCMa@example.com

2.1 Primary Key: "CCCD" của data

Đặc điểm:

  • Unique: không lặp
  • Not null: phải có value
  • Immutable: set xong không sửa
sql
-- Không PK: sẽ sửa mọi "Hoàng" trong table!
UPDATE users SET age = 26 WHERE name = 'Hoàng';

-- Có PK: precise
UPDATE users SET age = 26 WHERE user_id = 1001;

Golden rule: mỗi table có PK, không sửa.

2.2 Foreign Key: cầu nối table

DB mạnh hơn Excel — table có relation.

users:

user_id (PK)namephone
1001Hoàng0901...
1002Linh0902...

orders:

order_id (PK)productpriceuser_id (FK)
5001iPhone 1525M1001
5002MacBook50M1001
5003AirPods5M1002

user_id = 1001 trong orders → trỏ user_id = 1001 trong users (Hoàng).

Lợi:

  • Data không duplicate: Hoàng mua 100 order, info chỉ store 1 lần
  • Maintain dễ: đổi phone Hoàng, sửa users, mọi order auto link
  • Flexible query
🔗外键关系演示理解表与表之间如何关联
想象你在管理一个家族谱系:有"家谱表"记录每个人,有"婚姻表"记录谁和谁结婚了。两张表通过"人名"关联起来,这就是外键的作用。
👥用户表 (users)主表
🔑 user_id
name
phone
address
101
张三
138xxxx
北京
102
李四
139xxxx
上海
103
王五
137xxxx
广州
user_id (外键) → user_id (主键)
📦订单表 (orders)从表
🔑 order_id
book_name
🔗 user_id
price
001
百年孤独
101
59
002
活着
101
39
003
三体
101
99
004
百年孤独
102
59
005
红楼梦
102
79
006
西游记
103
69
💡 核心概念

主键(Primary Key):用户表的 user_id 是主键,唯一标识每个用户。

外键(Foreign Key):订单表的 user_id 是外键,指向用户表的主键。

关联查询:通过外键,数据库可以快速找到"订单 001 是用户 101 买的",然后去用户表查到"用户 101 是张三"。

🎯核心优势:外键消除了数据冗余。张三的地址只存一次,无论他买多少本书。如果要修改地址,只需改用户表的一行,所有订单自动关联到新地址。

3. Đối thoại với DB: SQL

SQL (Structured Query Language) = ngôn ngữ DB. Gần natural English.

3.1 CRUD

OpENSQLNote
CreateCreateINSERTThêm record
ReadReadSELECTQuery
UpdateUpdateUPDATESửa
DeleteDeleteDELETEXoá

3.2 SELECT

Vd 1: tìm user HCM

sql
SELECT name, age FROM users WHERE city = 'HCM';

Vd 2: product 5000-15000

sql
SELECT name, price FROM products WHERE price BETWEEN 5000 AND 15000;

Vd 3: fuzzy search

sql
SELECT name FROM users WHERE name LIKE '%Hoàng%';

Performance trap LIKE

LIKE '%text%'full table scan, lớn = cực chậm.

Optimize:

  • LIKE '%text%' (cả 2 phía %)
  • LIKE 'text%' (chỉ sau)

LIKE 'text%' dùng được index, %text% không.

3.3 INSERT

sql
INSERT INTO users (user_id, name, age, city, email)
VALUES (1004, 'An', 35, 'Đà Nẵng', 'a@example.com');

Batch (faster):

sql
INSERT INTO users (name, age, city) VALUES
('Minh', 25, 'HN'),
('Mai', 28, 'HCM'),
('Khang', 30, 'Đà Nẵng');

3.4 UPDATE

sql
UPDATE users SET age = age + 1 WHERE city = 'HCM';

Đừng quên WHERE!

sql
-- DANGEROUS: sửa MỌI user age = 26
UPDATE users SET age = 26;

-- Đúng:
UPDATE users SET age = 26 WHERE user_id = 1001;

Lesson real: 2012, 1 cty nổi tiếng dev quên WHERE → vài triệu user data prod bị sửa nhầm, system down 4h.

3.5 DELETE

sql
DELETE FROM users WHERE user_id = 1004;

DELETE càng nguy hiểm!

sql
-- DANGEROUS: xoá CẢ TABLE!
DELETE FROM users;

-- Đúng:
DELETE FROM users WHERE user_id = 1004;

Best practice:

  1. Xoá trước SELECT confirm
  2. System quan trọng dùng soft delete (is_deleted field)
  3. Prod op trước backup

3.6 JOIN: thời khắc magic

Scenario: query "product Hoàng đã mua"

3 table: users, products, orders.

sql
SELECT u.name, p.name AS product_name, p.price, o.quantity
FROM orders o
JOIN users u ON o.user_id = u.user_id
JOIN products p ON o.product_id = p.product_id
WHERE u.name = 'Hoàng';

Hiểu JOIN:

  1. FROM orders o: từ orders
  2. JOIN users u ON o.user_id = u.user_id: link user
  3. JOIN products p ON o.product_id = p.product_id: link product
  4. WHERE u.name = 'Hoàng': filter
💻SQL 练习场体验 SQL 的 CRUD 操作
SQL 就像和数据库对话:你说"给我找所有年龄大于 25 的用户",数据库就会执行查询并返回结果。即使不会编程,也能很快上手。
📝 示例 SQL
SELECT name, age FROM users WHERE age > 25;
💡 逐词翻译
SELECT name, age选择 name 和 age 这两列
FROM users从 users 这张表
WHERE age > 25在 age 大于 25 的条件下
📊 返回结果
name
age
李四
30
王五
28
🎯核心概念:CRUD 涵盖了所有数据管理的基本需求。无论是淘宝、微信、抖音,它们的数据库操作本质上就是这四种:增、删、改、查。

4. Sao DB nhanh? Index principle

Excel tìm "tên Hoàng" = full scan. Data nhiều = chậm.

DB có 1 tỷ row, tìm vẫn vài ms. Bí mật: Index.

4.1 Ẩn dụ từ điển

Tìm chữ trong sách 1000 trang không có mục lục → lật từng trang (full scan, avg 500 lần).

pinyin/alphabet index:

  1. Tìm trang index, đến phần "D"
  2. Trong "D" tìm "Database"
  3. Index báo: trang 256

Chỉ 3 lần! Đây là index lookup.

Index DB như mục lục sách:

  • Không index: scan từng row (1 tỷ row = vài phút)
  • Có index: jump thẳng (3 disk I/O = vài ms)

4.2 Full scan vs Index

Table 10 triệu record. Tìm user_id = 5,555,555:

MethodProcessRows checkedTime
Full scanTừ row 1 đọc từng rowAvg 5M5-30s
IndexTra index tree, jump3-4 compare0.003s

Diff: vài nghìn lần!

Insight

Index không silver bullet, có cost:

  • Space: extra storage
  • Slower write: mỗi INSERT/UPDATE/DELETE update index

Khi nào index?

  • Column hay query (WHERE, JOIN)
  • Data lớn (<vài nghìn row không cần)

Khi nào không?

  • Column ít query
  • Column update thường xuyên
  • Table nhỏ

4.3 Underlying: B+ Tree

Real index = B+ Tree — tree "thấp lùn":

  • Thấp: từ root → leaf chỉ 3-4 layer
  • Mập: mỗi node store hàng trăm key

Sao "thấp lùn"?

Data trên disk, mỗi I/O disk chậm (vs memory chậm vài nghìn lần). Goal B+ Tree: giảm I/O disk.

  • 3-4 layer = max 3-4 disk read
  • Mỗi layer store nhiều key = tree không cao

Vd: mỗi node store 1000 key:

  • Root: 1000 key → trỏ 1000 sub-node
  • Middle: mỗi node 1000 key → trỏ 1000 leaf
  • Leaf: mỗi leaf 1000 real data

Total = 1000 × 1000 × 1000 = 1 tỷ dataHeight = 3 layer

Tìm 1 trong 1 tỷ chỉ 3 disk I/O! Bí mật DB nhanh.

🌳B+ 树索引演示理解数据库如何快速查找数据
想象你要在字典里找一个字。你会先看目录,定位到首字母的区域,再在这个区域里找具体页码。B+ 树就是这样的多层目录,让数据库在 10 亿条数据中 3 次就能找到目标。
🐢全表扫描
001用户1
002用户2
003用户3
004用户4
005用户5
006用户6
007用户7
008用户8
009用户9
010用户10
011用户11
012用户12
013用户13
014用户14
015用户15
016用户16
017用户17
018用户18
019用户19
020用户20

👆 点击"开始查找"看全表扫描有多慢

索引查找
根节点
1-100
中间节点
1-10
叶子节点
1
2
3
4
5
6
7
8
9
10

👆 点击"开始查找"看索引有多快

数据量
100 万条
全表扫描
平均 50 万次比较
B+ 树索引
仅 3 次比较
速度提升
10 万倍+
💡核心原理:B+ 树通过"矮胖"的设计,让树的高度只有 3-4 层。每层可以存储成百上千个键值,所以 10 亿数据也只需要 3 次磁盘 I/O。这就是数据库查询飞快的秘密。

5. Transaction: data không mất, không loạn

Scene đặt vé tàu:

  • T1: User A query, thấy "G1234 còn 1 vé"
  • T2: User B cũng query, cũng thấy "còn 1 vé"
  • T3: A click buy, system trừ, vé bán cho A
  • T4: B click buy — không protection → system trừ lại, bán cùng vé cho B!

Đây là concurrent conflict.

5.1 Transaction là gì?

Transaction = 1 nhóm op DB, all-or-nothing.

Ẩn dụ

Bank transfer = transaction điển hình:

  1. Trừ account A 100k
  2. Cộng account B 100k

Bước 1 OK, bước 2 fail (mất điện) → gì xảy ra?

  • Không transaction: A mất tiền, B không có → tiền biến mất
  • Có transaction: system phát hiện bước 2 fail → auto rollback bước 1, 2 account về nguyên

Atomicity: all-or-nothing.

5.2 ACID

PropertyENNoteBank vd
AtomicityAtomicAll-or-nothingTrừ + cộng cùng OK
ConsistencyConsistentData luôn legalTổng tiền 2 account không đổi
IsolationIsolatedTransaction không ảnh hưởng nhauA đang transfer, B thấy "before" hoặc "after", không "middle"
DurabilityDurableCommit xong = save vĩnh viễnMất điện balance không revert

5.3 Isolation levels

Lý tưởng isolation full = perf cực tệ (lock nhiều). DB cung cấp 4 levels:

LevelDirty readNon-repeatablePhantomPerfUse
Read UncommittedNhanh nhấtHiếm dùng
Read CommittedKhôngKháĐa số (Oracle default)
Repeatable ReadKhôngKhôngTrungBanking (MySQL default)
SerializableKhôngKhôngKhôngChậm nhấtStrict nhất, hiếm

3 "read" issue

  • Dirty read: đọc data tx khác chưa commit (có thể rollback → sai)
  • Non-repeatable: cùng tx, đọc 2 lần khác nhau (tx khác sửa)
  • Phantom: cùng tx, query 2 lần, row count khác (tx khác insert/delete)
🔒事务 ACID 特性演示理解事务如何保证数据安全
想象银行转账:A 转给 B 100 元。这个操作包含两步:从 A 扣 100,给 B 加 100。如果只扣了钱但没到账,就是灾难。事务保证这两步要么全成功,要么全失败
⚛️
A
原子性
Atomicity
⚖️
C
一致性
Consistency
🔒
I
隔离性
Isolation
💾
D
持久性
Durability
👆 点击上方任意特性,查看详细解释
🎯 12306 抢票场景

场景:用户 A 和 B 同时看到还剩 1 张票,同时点击购买。

没有事务:A 扣库存,B 也扣库存,同一张票卖给了两个人!

有事务(隔离性):A 的操作加锁,B 必须等待。A 买完后,库存变为 0,B 看到的是"已售罄"。

💡核心思想:ACID 四个特性共同保证了数据在高并发环境下的不丢、不乱、不冲突。这就是为什么所有涉及资金、订单的系统都必须使用数据库事务。

6. Performance: query nhanh 1000x

6.1 Index pitfalls

Index invalid

Index sẽ invalid trong:

  1. Function on index column
  2. Implicit type conversion
  3. LIKE starts with %
  4. OR condition (1 số case)
  5. Composite index không thoả leftmost prefix

Pit 1: function on index column

sql
-- ❌ Function → không index
SELECT * FROM users WHERE YEAR(created_at) = 2024;

-- ✅ Range query → index
SELECT * FROM users
WHERE created_at >= '2024-01-01' AND created_at < '2025-01-01';

Pit 2: implicit type conversion

sql
-- user_id là int
-- ❌ Truyền string → implicit conversion → no index
SELECT * FROM users WHERE user_id = '123';

-- ✅
SELECT * FROM users WHERE user_id = 123;

Pit 3: LIKE %

sql
-- ❌
SELECT * FROM users WHERE name LIKE '%Hoàng%';

-- ✅
SELECT * FROM users WHERE name LIKE 'Hoàng%';

-- ✅ Hoặc full-text index
SELECT * FROM users WHERE MATCH(name) AGAINST('Hoàng');

6.2 SQL optimize templates

Pagination deep:

sql
-- ❌ OFFSET lớn → càng chậm
SELECT * FROM orders ORDER BY created_at DESC LIMIT 10 OFFSET 1000000;

-- ✅ Cursor pagination
SELECT * FROM orders
WHERE created_at < '2024-01-15 12:00:00'
ORDER BY created_at DESC LIMIT 10;

-- ✅ PK range
SELECT * FROM orders
WHERE order_id > 1000000
ORDER BY order_id LIMIT 10;

Batch insert:

sql
-- ❌ Multi network round trip
INSERT INTO users (name, age) VALUES ('Hoàng', 25);
INSERT INTO users (name, age) VALUES ('Linh', 30);

-- ✅ 1 SQL
INSERT INTO users (name, age) VALUES ('Hoàng', 25), ('Linh', 30), ('An', 28);

**Avoid SELECT ***:

sql
-- ❌ Trả mọi column
SELECT * FROM users WHERE user_id = 1;

-- ✅ Chỉ column cần
SELECT user_id, name, email FROM users WHERE user_id = 1;

6.3 High concurrency

ScenarioIssueSolution
Hot data1 row read/write nhiều, lock contentionCache (Redis) + read-write split
Flash saleConcurrent trừ stock instantOptimistic lock + stock pre-heat + MQ smoothing
Slow queryComplex query drag DBIndex optimize + query split + read replica
Connection exhaustionQuá nhiều concurrent reqConnection pool optimize + rate limit + circuit breaker

Core principles optimization

  1. Measure first: EXPLAIN find real bottleneck
  2. Index first: 80% perf issue index optimize giải được
  3. Reduce DB pressure: cache + async
  4. Divide and conquer: big table → small, big query → small
查询优化演示常见错误与正确做法对比
很多时候,查询慢不是因为数据库性能差,而是因为 SQL 写错了。下面这些错误,你可能每天都在犯。
1
在索引列上使用函数
SELECT * FROM users WHERE YEAR(created_at) = 2024;
⚠️ 索引失效,全表扫描
SELECT * FROM users WHERE created_at >= '2024-01-01' AND created_at < '2025-01-01';
💡 可以使用索引,查询速度提升 1000 倍
原理:当对列使用函数时,数据库必须先计算每一行的函数值,无法使用索引。把函数移到等号右边,或用范围查询代替。
2
隐式类型转换
3
LIKE 以 % 开头
4
SELECT * 返回所有列
📝 优化建议清单
为 WHERE、JOIN、ORDER BY 的列创建索引
避免在索引列上使用函数或表达式
用 EXPLAIN 分析查询执行计划
只查询需要的列,避免 SELECT *
批量操作代替单条操作
考虑使用覆盖索引减少回表
🎯核心原则:不要让数据库做"多余的工作"。索引失效、全表扫描、返回不必要的数据,这些都是最常见的性能杀手。写出高效 SQL 的关键,是理解数据库如何执行你的查询

7. Tổng kết

Concept1 câuVấn đềKey
Table/Row/ColumnCách tổ chức dataStore structured dataTable = sheet, row = record, column = field
Primary KeyID unique mỗi rowTìm chính xác 1 rowUnique, not null, immutable
Foreign KeyCầu nối tableLink tableTrỏ PK table khác
SQLNgôn ngữ DBCRUDSELECT, INSERT, UPDATE, DELETE
IndexData structure speedup queryTìm nhanhB+ Tree, giảm disk I/O
TransactionMechanism safetyPrevent concurrent conflictACID

Next steps

  1. Practice: install MySQL/PostgreSQL, create table, write SQL
  2. ORM: SQLAlchemy, Prisma, TypeORM, Drizzle
  3. Index advanced: composite, covering, index pushdown
  4. Transaction: MVCC, lock, isolation impl
  5. Distributed: sharding, replication, read-write split

Theory + Practice = mastery.

2026 cho VN dev

  • PostgreSQL 17+ dominant: open-source, mạnh nhất, có extension (pgvector, TimescaleDB, PostGIS)
  • MySQL 8 vẫn phổ biến: legacy, ecosystem lớn
  • Managed DB: Supabase, Neon, PlanetScale, Aurora — không cần DBA
  • VN context: banking dùng Oracle, MS SQL; modern startup dùng PostgreSQL
  • ORM: Drizzle (TS, type-safe), Prisma (popular), TypeORM
  • AI scenario: pgvector cho RAG, ClickHouse cho analytics, TimescaleDB cho logs