Skip to content

Nguyên lý cơ sở dữ liệu (Index / Transaction / Tối ưu truy vấn)

🎯 Vấn đề cốt lõi

Tại sao truy vấn Excel của bạn mất 10 giây, trong khi tìm kiếm trên Taobao chỉ cần 0,01 giây? Khi dữ liệu từ "vài nghìn dòng" biến thành "một tỷ dòng", từ "một người dùng" thành "hàng triệu người truy cập cùng lúc", Excel không còn đủ dùng. Cơ sở dữ liệu ra đời chính để giải quyết vấn đề này — nó là "siêu Excel" chuyên xử lý dữ liệu khổng lồ và truy cập đồng thời lớn. Chương này sẽ hướng dẫn bạn từ con số không hiểu được nguyên lý cốt lõi của cơ sở dữ liệu.


1. Tại sao cần "cơ sở dữ liệu"?

1.1 Từ hiệu sách nhỏ đến Taobao: Sự tiến hóa về quy mô dữ liệu

Hãy tưởng tượng bạn mở một hiệu sách nhỏ, mỗi ngày bán được vài cuốn. Bạn ghi chép tay vào sổ:

2024-01-15: Trương Tam mua "Trăm năm cô đơn", 59 đồng
2024-01-16: Lý Tứ mua "Sống", 39 đồng

Lúc này, cuốn sổ hoàn toàn đủ dùng. Nhưng khi hiệu sách của bạn trở thành "Amazon", mỗi ngày có hàng triệu đơn hàng, vấn đề xuất hiện:

  • Lượng dữ liệu lớn: không phải vài chục dòng, mà hàng tỷ dòng
  • Truy cập đồng thời: không phải một người tra cứu, mà hàng triệu người truy cập cùng lúc
  • Liên kết dữ liệu: đơn hàng liên kết người dùng, sản phẩm, tồn kho, logistics... quan hệ phức tạp cần quản lý hiệu quả
  • Bảo mật dữ liệu: không thể mất toàn bộ đơn hàng chỉ vì mất điện

📓 Excel/Sổ tay

  • Phù hợp cá nhân hoặc nhóm nhỏ
  • Lượng dữ liệu: vài nghìn đến vài vạn dòng
  • Một người sử dụng, truy cập tuần tự
  • Tìm kiếm thủ công, tốc độ chậm

🗄️ Cơ sở dữ liệu

  • Phù hợp ứng dụng doanh nghiệp
  • Lượng dữ liệu: hàng tỷ trở lên
  • Hàng triệu người truy cập đồng thời
  • Tốc độ truy vấn tính bằng mili-giây

Đây chính là vấn đề mà "cơ sở dữ liệu" giải quyết: làm sao lưu trữ, truy vấn và quản lý dữ liệu khổng lồ một cách hiệu quả và an toàn?

1.2 Một câu chuyện thực tế: Tại sao không thể dùng Excel lưu dữ liệu người dùng

Bạn có thể nói: "Dự án của tôi mới vài vạn người dùng, Excel không đủ sao?" Hãy để tôi kể một câu chuyện thực tế.

Câu chuyện khởi nghiệp của Tiểu Lâm

Tiểu Lâm khởi nghiệp làm ứng dụng xã hội, ban đầu người dùng ít, anh dùng Excel lưu thông tin người dùng (tên, số điện thoại, thời gian đăng ký...). Mỗi ngày xuất Excel thống kê tăng trưởng người dùng, mọi thứ ổn.

Khi người dùng vượt 100.000, vấn đề bắt đầu xuất hiện:

  • Mở Excel mất 5 phút
  • Lọc "người dùng ở Bắc Kinh" phải đợi rất lâu
  • Một lần file Excel bị lỗi, hàng nghìn dữ liệu người dùng mất vĩnh viễn

Chí mạng nhất là, anh muốn thực hiện chức năng "xem tất cả đơn hàng của một người dùng" — nhưng thông tin người dùng và đơn hàng nằm ở hai bảng Excel khác nhau, anh chỉ có thể copy-paste thủ công, mỗi lần mất nửa tiếng.

Sau đó anh đi hỏi anh khóa trên, anh kia nhìn một cái rồi cười: "Cậu cần không phải Excel, mà là cơ sở dữ liệu."

Sau khi chuyển sang cơ sở dữ liệu, mọi thứ thay đổi:

  • Truy vấn "người dùng Bắc Kinh" chỉ mất 0,01 giây
  • Thông qua "quan hệ" tự động liên kết người dùng và đơn hàng, một câu lệnh SQL搞定
  • Tự động sao lưu, không còn sợ lỗi file

Tiểu Lâm từ đó hiểu một chân lý: Khi dữ liệu ít, cái gì cũng dùng được; nhưng khi dữ liệu lớn, Excel là thảm họa.

💡 Bài học cốt lõi

Cơ sở dữ liệu không phải "Excel phức tạp hơn", mà là thiết kế hoàn toàn khác:

  • Excel: thiết kế cho dữ liệu nhỏ, một người dùng
  • Cơ sở dữ liệu: thiết kế cho dữ liệu lớn, đồng thời cao, quan hệ phức tạp

Chọn đúng công cụ có thể cải thiện hiệu suất hệ thống hàng nghìn lần.


2. Khái niệm cốt lõi: Bảng, hàng, cột, khóa chính

🤔 Các khái niệm này liên quan gì đến cơ sở dữ liệu?

Bảng, hàng, cột, khóa chính chính là các "khối xây dựng" của cơ sở dữ liệu.

Hãy tưởng tượng bạn muốn xây nhà:

  • Bảng = một căn phòng (lưu một loại dữ liệu)
  • Hàng = một cái thùng trong phòng (một bản ghi hoàn chỉnh)
  • Cột = nhãn trên thùng (tên, tuổi, v.v.)
  • Khóa chính = số serial duy nhất của thùng (tuyệt đối không trùng)

Hiểu các khái niệm cơ bản này, bạn mới biết dữ liệu được tổ chức như thế nào.

Trước khi đi sâu, chúng ta cần nắm rõ vài khái niệm cốt lõi. Sẽ dùng phép so sánh với thư viện để giúp bạn hiểu.

2.1 Hiểu cấu trúc cơ sở dữ liệu qua phép so sánh thư viện

Hãy tưởng tượng bạn bước vào một thư viện, cách tổ chức bên trong giống hệt cơ sở dữ liệu:

Khái niệm📚 So sánh thư việnTác dụng thực tếVí dụ cụ thể
Cơ sở dữ liệu (Database)Toàn bộ thư việnContainer lưu tất cả dữ liệuCSDL của một website thương mại điện tử
Bảng (Table)Một giá sáchTập hợp lưu cùng loại dữ liệuBảng người dùng, bảng sản phẩm, bảng đơn hàng
Cột (Column)Nhãn trên gáy sáchThuộc tính của dữ liệu (trường)Tên, tuổi, số điện thoại
Hàng (Row)Mỗi cuốn sách trên giáMột bản ghi dữ liệu cụ thể"Trương Tam, 25 tuổi, Bắc Kinh"
Khóa chính (Primary Key)Mã ISBN của mỗi sáchID định danh duy nhất mỗi hànguser_id = 1001

Xem ví dụ thực tế: Bảng người dùng (users)

user_id (khóa chính)nameagecityemail
1001张三25北京zhangsan@example.com
1002李四30上海lisi@example.com
1003王五28北京wangwu@example.com
  • Bảng: users (lưu tất cả dữ liệu người dùng)
  • Cột: user_id, name, age, city, email (thuộc tính mỗi người dùng)
  • Hàng: mỗi hàng là một người dùng (như "张三, 25 tuổi, 北京")
  • Khóa chính: user_id (1001, 1002, 1003, không bao giờ trùng)

2.2 Khóa chính (Primary Key): "Số CMND" của dữ liệu

📖 Khóa chính là gì?

Khóa chính là định danh duy nhất mỗi hàng trong bảng, giống như số CMND.

Đặc điểm chính:

  • Tính duy nhất: tuyệt đối không trùng (không có hai người cùng số CMND)
  • Không null: bắt buộc phải có giá trị (không thể có người "không có số CMND")
  • Bất biến:一旦 thiết lập, không thay đổi (số CMND của bạn không đổi)

Cách phổ biến:

  • Dùng số nguyên tự tăng: 1, 2, 3, 4...
  • Dùng UUID (định danh duy nhất toàn cầu): 550e8400-e29b-41d4-a716-446655440000

Tại sao cần khóa chính? Hãy tưởng tượng thế giới không có khóa chính:

Kịch bản: Bạn muốn sửa tuổi của "张三", nhưng bảng có 3 người tên "张三", hệ thống sửa ai?

sql
-- Không có khóa chính, lệnh này sửa tất cả người tên "张三"!
UPDATE users SET age = 26 WHERE name = '张三';

-- Có khóa chính, sửa chính xác
UPDATE users SET age = 26 WHERE user_id = 1001;

Quy tắc vàng của khóa chính: mỗi bảng nên có một khóa chính, và không bao giờ sửa nó.

2.3 Khóa ngoại (Foreign Key): Cầu nối giữa các bảng

Đây là điểm then chốt khiến CSDL mạnh hơn Excel — các bảng có thể tạo quan hệ với nhau.

📖 Khóa ngoại là gì?

Khóa ngoại là cột trỏ đến khóa chính của bảng khác, dùng để tạo liên kết giữa các bảng.

Hiểu đơn giản:

  • Khóa chính = số CMND của tôi
  • Khóa ngoại = số CMND của người khác mà tôi tham chiếu

Ví dụ: user_id trong bảng đơn hàng là khóa ngoại, nó trỏ đến khóa chính của bảng người dùng.

Xem một ví dụ thực tế:

Bảng người dùng (users):

user_id (khóa chính)namephone
1001张三138xxxx
1002李四139xxxx

Bảng đơn hàng (orders):

order_id (khóa chính)product_namepriceuser_id (khóa ngoại)
5001iPhone 1559991001
5002MacBook149991001
5003AirPods19991002

Hiểu chính:

  • user_id = 1001 trong bảng đơn hàng trỏ đến user_id = 1001 trong bảng người dùng (张三)
  • Khi bạn muốn biết "đơn hàng 5001 là của ai", CSDL tự động tìm trong bảng người dùng user_id = 1001

Lợi ích:

  • Dữ liệu không trùng: Trương Tam mua 100 đơn hàng, thông tin của anh ấy cũng chỉ lưu một lần trong bảng người dùng
  • Dễ bảo trì: Trương Tam đổi số điện thoại, chỉ cần sửa bảng người dùng, tất cả đơn hàng tự động liên kết số mới
  • Truy vấn linh hoạt: có thể dễ dàng trả lời "tổng chi tiêu của mỗi người dùng là bao nhiêu"
🔗外键关系演示理解表与表之间如何关联
想象你在管理一个家族谱系:有"家谱表"记录每个人,有"婚姻表"记录谁和谁结婚了。两张表通过"人名"关联起来,这就是外键的作用。
👥用户表 (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. Làm sao giao tiếp với CSDL? Nhập môn SQL và thực chiến

Bạn không thể dùng chuột "click" trực tiếp vào CSDL (dù có công cụ đồ họa, bản chất vẫn là chuyển thành lệnh), bạn cần dùng một ngôn ngữ đặc biệt để chỉ đạo CSDL hoạt động.

Ngôn ngữ đó chính là SQL (Structured Query Language, Ngôn ngữ truy vấn có cấu trúc).

Tin tốt là: SQL rất gần với tiếng Anh tự nhiên, đọc giống như đang nói chuyện.

3.1 Các thao tác cốt lõi của SQL: CRUD

Phần lớn thời gian, bạn chỉ cần nắm bốn thao tác, dân trong ngành gọi là CRUD:

Thao tácTiếng AnhTừ khóa SQLHiểu dân dã
Create (Tạo)TạoINSERTThêm một dữ liệu
Read (Đọc)ĐọcSELECTTruy vấn dữ liệu
Update (Cập nhật)SửaUPDATESửa dữ liệu
Delete (Xóa)XóaDELETEXóa dữ liệu

📊 Từ bảng bạn thấy gì?

Bốn thao tác này bao phủ mọi kịch bản xử lý dữ liệu:

  • Create: khi người dùng đăng ký, chèn một bản ghi mới
  • Read: khi người dùng đăng nhập, truy vấn tên và mật khẩu
  • Update: khi người dùng sửa thông tin cá nhân, cập nhật dữ liệu trong bảng
  • Delete: khi người dùng xóa tài khoản, xóa dữ liệu người dùng

Nhớ bốn thao tác này, bạn đã nắm 80% thao tác SQL hàng ngày.

3.2 Truy vấn dữ liệu (SELECT): Thao tác phổ biến nhất

Truy vấn là chức năng quan trọng nhất của CSDL và là then chốt tối ưu hiệu suất.

Ví dụ 1: Tìm tất cả người dùng ở Bắc Kinh

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

Hiểu từng từ:

  • SELECT name, age: chọn cột name và age
  • FROM users: từ bảng users
  • WHERE city = '北京': trong điều kiện city bằng "Bắc Kinh"

Kết quả:

nameage
张三25
王五28

Ví dụ 2: Tìm sản phẩm có giá từ 5000 đến 15000

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

Ví dụ 3: Tìm kiếm mờ (tìm người có tên chứa "张")

sql
SELECT name FROM users WHERE name LIKE '%张%';

⚠️ Bẫy hiệu suất: Sử dụng LIKE

LIKE '%张%' gây quét toàn bảng, rất chậm khi dữ liệu lớn.

Khuyến nghị tối ưu:

  • ❌ Không dùng LIKE '%张%' (có % cả trước và sau)
  • ✅ Có thể dùng LIKE '张%' (chỉ có % phía sau)

LIKE '张%' có thể dùng index, còn LIKE '%张%' không thể dùng index.

3.3 Chèn dữ liệu (INSERT): Thêm bản ghi

Ví dụ: Thêm một người dùng mới

sql
INSERT INTO users (user_id, name, age, city, email)
VALUES (1004, '赵六', 35, '广州', 'zhaoliu@example.com');

Hiểu từng từ:

  • INSERT INTO users: chèn vào bảng users
  • (user_id, name, age, city, email): chỉ định các cột cần chèn
  • VALUES (1004, '赵六', ...): các giá trị tương ứng

Chèn hàng loạt (hiệu quả hơn):

sql
INSERT INTO users (name, age, city) VALUES
('小明', 25, '北京'),
('小红', 28, '上海'),
('小刚', 30, '广州');

3.4 Cập nhật dữ liệu (UPDATE): Sửa bản ghi

Ví dụ: Tăng tuổi tất cả người dùng ở Bắc Kinh lên 1

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

❌ Rất nguy hiểm: Đừng quên WHERE!

Nếu bạn quên mệnh đề WHERE, sẽ sửa tất cả các hàng!

sql
-- Nguy hiểm! Sẽ sửa tuổi tất cả người dùng thành 26
UPDATE users SET age = 26;

-- Đúng: chỉ sửa người dùng có user_id = 1001
UPDATE users SET age = 26 WHERE user_id = 1001;

Bài học thực tế: Năm 2012, một công ty nổi tiếng do kỹ sư quên viết WHERE, dẫn đến dữ liệu hàng triệu người dùng trong môi trường production bị cập nhật sai, hệ thống sập 4 tiếng, thiệt hại khổng lồ.

3.5 Xóa dữ liệu (DELETE): Xóa bản ghi

Ví dụ: Xóa người dùng có user_id = 1004

sql
DELETE FROM users WHERE user_id = 1004;

❌ Nguy hiểm kép: DELETE càng cần WHERE hơn!

sql
-- Nguy hiểm! Sẽ xóa toàn bộ dữ liệu trong bảng!
DELETE FROM users;

-- Đúng: chỉ xóa hàng được chỉ định
DELETE FROM users WHERE user_id = 1004;

Thực hành tốt nhất:

  1. Trước khi xóa, dùng SELECT xác nhận dữ liệu
  2. Trong hệ thống quan trọng, dùng "xóa mềm" (thêm trường is_deleted đánh dấu xóa)
  3. Sao lưu dữ liệu trước khi thao tác trên môi trường production

3.6 Truy vấn đa bảng (JOIN): Khoảnh khắc kỳ diệu của CSDL

Nhớ "khóa ngoại" chúng ta đã nói? Điểm mạnh nhất của SQL là có thể truy vấn nhiều bảng liên quan cùng lúc.

Kịch bản: Truy vấn "tất cả sản phẩm Trương Tam đã mua"

Giả sử chúng ta có ba bảng:

Bảng người dùng (users):

user_idname
1001张三

Bảng sản phẩm (products):

product_idnameprice
201iPhone 155999
202MacBook14999

Bảng đơn hàng (orders):

order_iduser_idproduct_idquantity
500110012011
500210012022

Truy vấn SQL:

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 = '张三';

Kết quả:

nameproduct_namepricequantity
张三iPhone 1559991
张三MacBook149992

Hiểu quá trình JOIN:

  1. FROM orders o: bắt đầu từ bảng đơn hàng
  2. JOIN users u ON o.user_id = u.user_id: liên kết bảng người dùng qua user_id
  3. JOIN products p ON o.product_id = p.product_id: liên kết bảng sản phẩm qua product_id
  4. WHERE u.name = '张三': lọc đơn hàng của Trương Tam
💻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. Tại sao CSDL nhanh như vậy? Bí mật của Index

Đây là phần kỳ diệu nhất của CSDL, cũng là câu hỏi được hỏi nhiều nhất trong phỏng vấn.

Nếu bạn tìm trong Excel "tất cả những người họ 张", Excel phải quét từ dòng đầu đến dòng cuối. Đây gọi là quét toàn bảng — dữ liệu càng nhiều, càng chậm.

Nhưng trong CSDL, dù có 1 tỷ dòng, tìm kiếm chỉ mất vài mili-giây.

Bí mật chính là: Index (Chỉ mục).

4.1 Hiểu trực quan: Cảm hứng từ từ điển

Hãy tưởng tượng bạn cần tìm một từ trong cuốn sách 1000 trang không có mục lục. Bạn làm sao?

Chỉ lật từng trang — đây là quét toàn bảng, trung bình phải lật 500 trang.

Nhưng nếu cuốn sách đó có bảng tra theo pinyin?

Bạn tìm từ "数据库":

  1. Lật đến phần mục lục, tìm khu vực bắt đầu bằng chữ "数"
  2. Trong khu vực "数", tìm chữ "据"
  3. Mục lục cho biết: trang 256

Bạn chỉ cần tra 3 lần là tìm thấy! Đây là tra theo index.

Index của CSDL giống như mục lục của sách:

  • Không có index: quét từng dòng (1 tỷ dòng = vài phút)
  • Có index: nhảy thẳng đến (1 tỷ dòng = 3-4 lần đọc đĩa = mili-giây)

4.2 Quét toàn bảng vs Tra index: So sánh tốc độ

Giả sử bảng người dùng có 10 triệu bản ghi.

Kịch bản: Tìm người dùng có user_id = 5,555,555

Phương phápQuá trìnhSố hàng cần kiểm traThời gian ước tính
Quét toàn bảngBắt đầu từ hàng 1, kiểm tra từng hàngTrung bình 5 triệu5-30 giây
Tra indexTra cây index, nhảy thẳng đến vị trí3-4 lần so sánh0,003 giây

Chênh lệch tốc độ: Hàng nghìn lần!

💡 Bài học cốt lõi

Index không phải đạn bạc, nó có cái giá:

  • Chiếm dung lượng: index cần không gian lưu trữ thêm
  • Làm chậm ghi: mỗi INSERT/UPDATE/DELETE phải cập nhật index

Khi nào nên tạo index?

  • Cột thường xuyên dùng để truy vấn (điều kiện WHERE, JOIN)
  • Dữ liệu lớn (vài nghìn dòng trở xuống không cần)

Khi nào KHÔNG nên tạo index?

  • Cột ít khi truy vấn
  • Cột cập nhật thường xuyên
  • Bảng có ít dữ liệu

4.3 Cấu trúc dữ liệu nền tảng: Cây B+

Index thực tế không phải là "danh sách chữ cái" đơn giản, mà là cấu trúc dữ liệu được thiết kế tinh tế, gọi là Cây B+ (B+ Tree).

📖 Cây B+ là gì?

Cây B+ là cấu trúc dữ liệu cây "lùn và béo":

  • Lùn: từ gốc đến lá thường chỉ 3-4 tầng
  • Béo: mỗi nút có thể lưu hàng trăm key

Tại sao phải "lùn và béo"?

Vì dữ liệu lưu trên đĩa, mỗi lần đọc đĩa (I/O) rất chậm (chậm hơn bộ nhớ hàng nghìn lần). Mục tiêu thiết kế của Cây B+ là giảm thiểu số lần I/O đĩa.

  • 3-4 tầng = tối đa 3-4 lần đọc đĩa
  • Mỗi tầng lưu nhiều dữ liệu = đảm bảo cây không bị cao

Ví dụ thực tế:

Giả sử mỗi nút của Cây B+ lưu được 1000 key:

  • Nút gốc: 1000 key → trỏ đến 1000 nút con
  • Nút trung gian: mỗi nút lưu 1000 key → trỏ đến 1000 nút lá
  • Nút lá: mỗi nút lưu 1000 bản ghi thực tế

Tổng dữ liệu = 1000 × 1000 × 1000 = 1 tỷ bản ghi

Chiều cao cây = 3 tầng

Nghĩa là: trong 1 tỷ bản ghi, tìm bất kỳ bản ghi nào chỉ cần 3 lần đọc đĩa!

Đó là bí mật tốc độ thần tốc của CSDL.

🌳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: Làm sao đảm bảo dữ liệu không mất, không loạn?

Hãy tưởng tượng cảnh mua vé tàu dịp Tết:

  • Thời điểm T1: Người dùng A truy vấn, thấy "tàu G1234 còn 1 vé"
  • Thời điểm T2: Người dùng B cũng truy vấn, cũng thấy "còn 1 vé"
  • Thời điểm T3: Người dùng A bấm "mua", hệ thống trừ tồn kho, vé bán cho A
  • Thời điểm T4: Người dùng B bấm "mua" — nếu không có cơ chế bảo vệ, hệ thống sẽ trừ tồn kho lần nữa, bán cùng một vé cho B!

Đây là vấn đề xung đột đồng thời điển hình.

5.1 Transaction là gì?

Transaction là một nhóm thao tác của CSDL, các thao tác này hoặc tất cả thành công, hoặc tất cả thất bại, không xuất hiện tình trạng "làm dở dang".

🤖 Ví dụ đời thường

Chuyển khoản ngân hàng là transaction điển hình:

  1. Trừ 100 đồng từ tài khoản A
  2. Cộng 100 đồng vào tài khoản B

Nếu bước 1 thành công nhưng bước 2 thất bại (ví dụ mất điện), chuyện gì xảy ra?

  • Không có transaction: Tiền tài khoản A mất, tài khoản B không nhận được — tiền bốc hơi
  • Có transaction: Hệ thống phát hiện bước 2 thất bại, tự động rollback bước 1, cả hai tài khoản trở về trạng thái ban đầu

Đây chính là tính nguyên tử của transaction: hoặc tất cả, hoặc không gì cả.

5.2 Bốn đặc tính của Transaction (ACID)

Transaction có bốn đặc tính, gọi tắt là ACID:

Đặc tínhTiếng AnhÝ nghĩaVí dụ chuyển khoản
Atomicity (Nguyên tử)AtomicityHoặc tất cả hoặc không gì cảTrừ tiền và nhận tiền phải thành công cùng lúc, không thể chỉ trừ mà không nhận
Consistency (Nhất quán)ConsistencyDữ liệu luôn ở trạng thái hợp lệTrước và sau chuyển khoản, tổng số tiền hai tài khoản không đổi
Isolation (Cách ly)IsolationNhiều transaction không ảnh hưởng lẫn nhauKhi A đang chuyển, B thấy số dư phải là "trước chuyển" hoặc "sau chuyển", không thấy trạng thái trung gian
Durability (Bền vững)DurabilityKhi đã commit, dữ liệu lưu vĩnh viễnSau khi chuyển khoản thành công, dù mất điện, số dư cũng không quay lại

📊 Từ bảng bạn thấy gì?

Bốn đặc tính này đảm bảo an toàn dữ liệu:

  • Nguyên tử: ngăn "làm dở" (trừ tiền nhưng chưa đến tài khoản)
  • Nhất quán: ngăn dữ liệu phi lý (tổng tiền thay đổi sau chuyển khoản)
  • Cách ly: ngăn xung đột đồng thời (hai người sửa cùng dữ liệu)
  • Bền vững: ngăn mất dữ liệu (mất điện sau commit không ảnh hưởng)

Không có các đảm bảo này, hệ thống ngân hàng không thể hoạt động.

5.3 Mức độ cách ly của Transaction: Cân bằng an toàn và hiệu suất

Về lý thuyết, chúng ta muốn transaction hoàn toàn cách ly. Nhưng cách ly hoàn toàn = hiệu suất rất thấp (vì cần khóa nhiều, transaction khác phải chờ).

Do đó, CSDL cung cấp bốn mức cách ly:

Mức cách lyĐọc bẩnKhông lặp lạiĐọc ảoHiệu suấtKịch bản sử dụng
Read UncommittedCó thểCó thểCó thểNhanh nhấtGần như không dùng (dữ liệu có thể sai)
Read CommittedKhông thểCó thểCó thểKhá nhanhNghiệp vụ thông thường (mặc định Oracle)
Repeatable ReadKhông thểKhông thểCó thểTrung bìnhChuyển khoản ngân hàng (mặc định MySQL)
SerializableKhông thểKhông thểKhông thểChậm nhấtKịch bản cực kỳ nghiêm ngặt (hiếm khi dùng)

📖 Ba loại "đọc" là gì?

  • Đọc bẩn: đọc được dữ liệu transaction khác chưa commit (có thể rollback, dữ liệu không chính xác)
  • Không lặp lại: trong cùng transaction, hai lần đọc cùng dữ liệu nhưng kết quả khác nhau (bị transaction khác sửa)
  • Đọc ảo: trong cùng transaction, hai lần truy vấn số hàng khác nhau (transaction khác chèn/xóa dữ liệu)

Ví dụ dễ hiểu (tra số dư ngân hàng):

  • Đọc bẩn: bạn thấy số dư 1000, nhưng transaction kia bị rollback, thực tế chỉ có 100
  • Không lặp lại: lần đầu thấy số dư 1000, lần hai thấy 800 (bị trừ giữa chừng)
  • Đọc ảo: lần đầu thấy 5 giao dịch, lần hai thấy 6 (thêm một giao dịch mới)
🔒事务 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. Tối ưu hiệu suất: Kỹ thuật thực chiến tăng tốc truy vấn 1000 lần

Bây giờ bạn đã hiểu các khái niệm cốt lõi như index và transaction. Nhưng trong dự án thực tế, bạn có thể gặp nhiều vấn đề hiệu suất.

Phần này sẽ đưa ra các chiến lược tối ưu có thể áp dụng ngay.

6.1 Hướng dẫn tránh bẫy khi dùng index

⚠️ Lỗi phổ biến: Bẫy index mất hiệu lực

Nhiều khi, bạn đã tạo index nhưng truy vấn vẫn chậm — vì index bị vô hiệu hóa.

Nguyên nhân phổ biến khiến index mất hiệu lực:

  1. Dùng hàm trên cột có index
  2. Ép kiểu ngầm
  3. LIKE bắt đầu bằng %
  4. Điều kiện OR (một số trường hợp)
  5. Index tổ hợp không tuân thủ nguyên tắc tiền tố trái nhất

Bẫy 1: Dùng hàm trên cột có index

sql
-- ❌ Sai: dùng hàm trên cột index, không thể dùng index
SELECT * FROM users WHERE YEAR(created_at) = 2024;

-- ✅ Đúng: viết lại thành truy vấn phạm vi, có thể dùng index
SELECT * FROM users
WHERE created_at >= '2024-01-01' AND created_at < '2025-01-01';

Bẫy 2: Ép kiểu ngầm

sql
-- Giả sử user_id là kiểu int
-- ❌ Sai: truyền chuỗi, gây ép kiểu ngầm, không dùng được index
SELECT * FROM users WHERE user_id = '123';

-- ✅ Đúng: truyền đúng kiểu
SELECT * FROM users WHERE user_id = 123;

Bẫy 3: LIKE bắt đầu bằng %

sql
-- ❌ Sai: bắt đầu bằng %, không dùng được index
SELECT * FROM users WHERE name LIKE '%张三%';

-- ✅ Đúng: bắt đầu bằng tiền tố cố định, dùng được index
SELECT * FROM users WHERE name LIKE '张三%';

-- ✅ Hoặc dùng full-text index (phù hợp tìm kiếm văn bản)
SELECT * FROM users WHERE MATCH(name) AGAINST('张三');

6.2 Mẫu tối ưu SQL thực chiến

Mẫu 1: Tối ưu phân trang (vấn đề phân trang sâu)

Xem vấn đề và giải pháp
sql
-- ❌ Vấn đề: OFFSET lớn, truy vấn ngày càng chậm
SELECT * FROM orders
ORDER BY created_at DESC
LIMIT 10 OFFSET 1000000;

-- ✅ Giải pháp 1: Dùng timestamp lần truy vấn trước làm con trỏ
SELECT * FROM orders
WHERE created_at < '2024-01-15 12:00:00'
ORDER BY created_at DESC
LIMIT 10;

-- ✅ Giải pháp 2: Dùng truy vấn phạm vi khóa chính
SELECT * FROM orders
WHERE order_id > 1000000
ORDER BY order_id
LIMIT 10;

Mẫu 2: Tối ưu chèn hàng loạt

sql
-- ❌ Kém hiệu quả: chèn đơn nhiều lần (nhiều chuyến đi lại mạng)
INSERT INTO users (name, age) VALUES ('张三', 25);
INSERT INTO users (name, age) VALUES ('李四', 30);
INSERT INTO users (name, age) VALUES ('王五', 28);

-- ✅ Hiệu quả: chèn hàng loạt trong một câu SQL (chỉ một chuyến mạng)
INSERT INTO users (name, age) VALUES
('张三', 25),
('李四', 30),
('王五', 28);

**Mẫu 3: Tránh SELECT ***

sql
-- ❌ Kém hiệu quả: trả tất cả cột (kể cả trường lớn không cần)
SELECT * FROM users WHERE user_id = 1;

-- ✅ Hiệu quả: chỉ trả các cột cần thiết
SELECT user_id, name, email FROM users WHERE user_id = 1;

6.3 Chiến lược đối phó kịch bản đồng thời cao

Kịch bảnVấn đềGiải pháp
Dữ liệu điểm nóngMột hàng được đọc/ghi liên tục, gây tranh chấp khóaDùng cache (Redis) + tách đọc/ghi
Flash saleGiảm tồn kho đồng thời tức thờiKhóa lạc quan + làm nóng tồn kho + hàng đợi tin nhắn cắt đỉnh
Truy vấn chậmTruy vấn phức tạp kéo sập CSDLTối ưu index + tách truy vấn + tách đọc/ghi
Hết kết nốiQuá nhiều request đồng thời làm cạn pool kết nốiTối ưu pool kết nối + giới hạn流量 + hạ cấp dịch vụ

💡 Bài học cốt lõi

Nguyên tắc cơ bản của tối ưu hiệu suất:

  1. Đo trước, tối ưu sau: dùng EXPLAIN phân tích kế hoạch truy vấn, tìm nút thắt thực sự
  2. Index ưu tiên: 80% vấn đề hiệu suất giải quyết được bằng tối ưu index
  3. Giảm áp lực CSDL: dùng được cache thì dùng, được bất đồng bộ thì bất đồng bộ
  4. Chia để trị: bảng lớn chia thành bảng nhỏ, truy vấn lớn chia thành truy vấn nhỏ
查询优化演示常见错误与正确做法对比
很多时候,查询慢不是因为数据库性能差,而是因为 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 và lộ trình học

Hãy ôn lại các khái niệm cốt lõi của CSDL qua một bảng:

Khái niệmGiải thích một câuVấn đề giải quyếtĐiểm chính
Bảng, hàng, cộtCách tổ chức dữ liệuLưu dữ liệu có cấu trúc như thế nàoBảng = Sheet Excel, Hàng = bản ghi, Cột = trường
Khóa chínhĐịnh danh duy nhất mỗi hàngTìm chính xác một hàng như thế nàoDuy nhất, không null, bất biến
Khóa ngoạiCầu nối giữa các bảngLiên kết dữ liệu bảng khác như thế nàoTrỏ đến khóa chính bảng khác
SQLNgôn ngữ giao tiếp với CSDLThêm/xóa/sửa/truy vấn dữ liệu như thế nàoSELECT, INSERT, UPDATE, DELETE
IndexCấu trúc dữ liệu tăng tốc truy vấnTìm dữ liệu nhanh như thế nàoCây B+, giảm I/O đĩa
TransactionCơ chế đảm bảo an toàn dữ liệuNgăn xung đột đồng thời và mất dữ liệu như thế nàoACID: Nguyên tử, Nhất quán, Cách ly, Bền vững

Lời cuối

Cơ sở dữ liệu là một chủ đề bao la và sâu sắc, bài viết này chỉ là nhập môn. Nếu bạn muốn tiếp tục học sâu, đề nghị lộ trình sau:

Bước tiếp theo:

  1. Thực hành: cài MySQL hoặc PostgreSQL, tạo bảng, chèn dữ liệu, viết truy vấn SQL
  2. Framework ORM: học cách dùng CSDL trong code (như SQLAlchemy, Prisma, TypeORM)
  3. Tối ưu index: nghiên cứu sâu index tổ hợp, index phủ, đẩy index xuống
  4. Nguyên lý transaction: tìm hiểu MVCC (kiểm soát đồng thời đa phiên bản), cơ chế khóa, triển khai mức cách ly
  5. CSDL phân tán: học chia库分表, tách đọc/ghi, đồng bộ主从

Nhớ: Lý thuyết + Thực hành = Nắm vững thực sự.