Một cái máy không thể béo lên mãi
Giả sử công ty bán lẻ có một database MySQL.
Ngày đầu tiên, database chứa 10.000 đơn hàng.
Nó chạy trên một máy chủ bình thường, có CPU, RAM và hai ổ SSD đang sống một cuộc đời tương đối yên bình.
Một năm sau, công ty có 100 cửa hàng.
Ba năm sau, có thêm website, ứng dụng, chương trình thành viên, giao hàng trong hai giờ và một đội marketing có khả năng tạo ra năm chương trình khuyến mãi trong cùng một buổi chiều.
Database bắt đầu chứa:
- Đơn hàng.
- Chi tiết đơn hàng.
- Thanh toán.
- Tồn kho.
- Giá bán.
- Khuyến mãi.
- Lịch sử khách hàng.
- Trạng thái giao hàng.
- Điểm thành viên.
- Hàng đổi trả.
Mỗi ngày có một triệu đơn hàng mới.
Mỗi đơn có năm sản phẩm.
Một triệu đơn hàng khi đi xuống tầng vật lý không còn là một triệu dòng dữ liệu.
Nó là hàng triệu lần ghi vào bảng chính, bảng chi tiết, index, log giao dịch, replica và các cấu trúc dữ liệu bên dưới.
Hàng tỷ electron bắt đầu chạy qua CPU.
Ổ đĩa quay hoặc các cell flash liên tục nhận điện tích.
Quạt máy chủ gầm lên.
Kỹ sư nhìn biểu đồ CPU rồi đưa ra giải pháp truyền thống:
Nâng máy lên.
CPU tăng từ 16 core lên 32 core.
RAM tăng từ 64 GB lên 256 GB.
Ổ cứng nhanh hơn.
Máy chạy khỏe trở lại.
Nhưng một chiếc máy tính không thể béo lên mãi.
Đến một lúc nào đó, doanh nghiệp đứng trước một con quái vật có 2 TB RAM, hàng chục ổ NVMe, một hóa đơn phần cứng rất đẹp và một nỗi sợ rất thật:
Nếu cái máy này chết thì sao?
Lúc đó, người ta bắt đầu chia database.
Khách hàng từ 1 đến 1 triệu nằm ở shard A.
Khách hàng từ 1 triệu đến 2 triệu nằm ở shard B.
Đơn hàng miền Nam nằm máy này.
Đơn hàng miền Bắc nằm máy kia.
Nghe thì có vẻ đơn giản.
Cho đến khi một khách hàng chuyển địa chỉ.
Một báo cáo phải tổng hợp toàn quốc.
Một chương trình khuyến mãi áp dụng trên mọi cửa hàng.
Một đơn hàng được tạo ở TP.HCM nhưng giao từ kho Bình Dương.
Một giao dịch cần cập nhật ba shard cùng lúc.
Khi đó, database không còn chỉ là nơi chứa dữ liệu.
Nó trở thành một tấm bản đồ hành chính mà mỗi câu SQL phải xin visa trước khi đi qua biên giới.
TiDB sinh ra để giải quyết lớp vấn đề đó.
TiDB là gì?
TiDB là một cơ sở dữ liệu SQL phân tán mã nguồn mở do PingCAP phát triển. Dự án được khởi tạo từ năm 2015 bởi ba kỹ sư hạ tầng muốn xây một hệ quản trị cơ sở dữ liệu có thể mở rộng theo chiều ngang, duy trì tính nhất quán mạnh và vẫn cho lập trình viên sử dụng giao thức cũng như phần lớn cú pháp quen thuộc của MySQL. Mã nguồn TiDB được phát hành theo giấy phép Apache 2.0; lớp lưu trữ TiKV của nó đã trở thành một dự án tốt nghiệp của Cloud Native Computing Foundation. (TiDB)
TiDB được đọc gần giống “Tai-Đi-Bi”. Chữ “Ti” là viết tắt của Titanium, tức titan.
Cái tên khá đúng tinh thần.
Mục tiêu của nó không phải trở thành một database nhỏ nhắn, dễ thương, chạy trong góc máy tính cá nhân.
Nó muốn trở thành một khối kim loại đủ cứng để giữ dữ liệu khi hệ thống đã lớn đến mức một máy không còn gánh nổi.
Nhưng cần làm rõ một điều:
TiDB không phải MySQL được cài thêm vài plugin để chạy trên nhiều máy.
TiDB hỗ trợ giao thức MySQL, nhiều cú pháp và công cụ trong hệ sinh thái MySQL, nhưng phần mềm được xây mới với kiến trúc phân tán riêng. Chính tài liệu của TiDB cũng nhấn mạnh rằng nó không được xây trên mã nguồn MySQL. (PingCAP Docs)
Ứng dụng có thể vẫn dùng:
1MySQL Driver
2 ↓
3MySQL Protocol
4 ↓
5TiDB
Nhưng phía sau cánh cửa quen thuộc đó không còn là một tiến trình mysqld giữ toàn bộ dữ liệu trên một máy.
Nó là cả một thành phố.
Vì sao một phần mềm Trung Quốc như TiDB đáng chú ý?
Khi nhắc đến phần mềm công nghệ Trung Quốc, phần lớn mọi người nghĩ đến:
- Ứng dụng mua sắm.
- Mạng xã hội.
- Video ngắn.
- Thanh toán điện tử.
- Giao đồ ăn.
Nhưng phía sau những ứng dụng đó là một bài toán hạ tầng dữ liệu rất dữ dội.
Hàng trăm triệu người dùng.
Lượng giao dịch khổng lồ.
Những ngày siêu khuyến mãi có thể làm lưu lượng tăng đột ngột.
Dữ liệu phải được phân phối qua nhiều máy, nhiều trung tâm dữ liệu và nhiều vùng địa lý.
Những hệ thống như TiDB không sinh ra từ nhu cầu làm một website giới thiệu công ty có 20 người truy cập mỗi ngày.
Nó sinh ra từ nỗi đau của việc vận hành những hệ thống đã vượt khỏi giới hạn của database đơn máy.
Điều đáng chú ý không phải chỉ vì TiDB được khởi tạo trong hệ sinh thái công nghệ Trung Quốc.
Điều đáng chú ý là nó được đưa ra dưới dạng mã nguồn mở.
Người dùng có thể:
- Đọc mã nguồn.
- Tự triển khai.
- Kiểm tra cách transaction hoạt động.
- Theo dõi issue.
- Đóng góp code.
- Chạy trên hạ tầng của mình.
- Không bắt buộc phải mua dịch vụ cloud của PingCAP.
Một sản phẩm thương mại có thể nói:
Hãy tin chúng tôi.
Một dự án mã nguồn mở tốt phải đặt phần ruột của mình lên bàn và nói:
Đây, kiểm tra đi.
Bản chất của TiDB
Nếu phải mô tả TiDB bằng một câu, mình sẽ viết:
TiDB là một hệ thống biến nhiều máy tính thành một database SQL duy nhất.
Ứng dụng nhìn thấy:
1mysql://database.company.local:4000
Nhưng bên dưới có thể là:
- Nhiều TiDB Server xử lý SQL.
- Một cụm PD quản lý metadata và điều phối dữ liệu.
- Nhiều TiKV lưu dữ liệu dạng hàng.
- Nhiều TiFlash lưu bản sao dạng cột.
- Load balancer.
- Hệ thống giám sát.
- Công cụ backup.
- Công cụ replication.
- Hàng nghìn nhóm Raft đang liên tục trao đổi log với nhau.
Người lập trình vẫn viết:
1BEGIN;
2
3UPDATE accounts
4SET balance = balance - 1000000
5WHERE account_id = 1001;
6
7UPDATE accounts
8SET balance = balance + 1000000
9WHERE account_id = 2002;
10
11COMMIT;
TiDB phải biến câu lệnh tưởng như đơn giản đó thành một giao dịch phân tán an toàn, ngay cả khi hai tài khoản nằm ở hai Region khác nhau, trên hai TiKV khác nhau, mỗi Region lại có nhiều bản sao.
Đó mới là bản chất của TiDB.
Nó không chỉ chia dữ liệu.
Nó che giấu phần lớn sự chia cắt đó khỏi ứng dụng.
Bốn thành phần chính của TiDB
Một cụm TiDB cơ bản có thể hình dung như sau:
1 ┌────────────────────┐
2 │ Application │
3 │ MySQL Driver / ORM │
4 └─────────┬──────────┘
5 │
6 Load Balancer
7 │
8 ┌─────────────────┴─────────────────┐
9 │ │
10 TiDB Server 1 TiDB Server 2
11 │ │
12 └─────────────────┬─────────────────┘
13 │
14 PD Cluster
15 │
16 ┌─────────────────┴─────────────────┐
17 │ │
18 TiKV Cluster TiFlash Cluster
19 Row-based storage Columnar storage
Bốn phần quan trọng nhất là:
- TiDB Server.
- Placement Driver.
- TiKV.
- TiFlash.
TiDB Server: người tiếp nhận câu SQL
TiDB Server là lớp SQL.
Nó nhận kết nối qua giao thức MySQL, sau đó:
- Phân tích cú pháp.
- Kiểm tra quyền.
- Tối ưu câu truy vấn.
- Tạo execution plan.
- Gửi công việc xuống TiKV hoặc TiFlash.
- Tổng hợp kết quả.
- Trả dữ liệu về ứng dụng.
TiDB Server không giữ dữ liệu nghiệp vụ trên ổ đĩa của chính nó. Vì lớp này gần như stateless, doanh nghiệp có thể đặt nhiều TiDB Server phía sau load balancer và tăng số lượng node khi cần phục vụ thêm kết nối hoặc thêm năng lực tính toán SQL. (PingCAP Docs)
Ta có thể tưởng tượng TiDB Server là những nhân viên tiếp nhận hồ sơ ở một trung tâm hành chính.
Người dân không cần biết giấy tờ đang nằm ở kho nào.
Họ chỉ đưa yêu cầu:
Tìm cho tôi tất cả đơn hàng chưa giao của khách hàng 123.
TiDB Server phân tích câu hỏi, tìm xem dữ liệu nằm ở đâu, chia công việc cho các kho rồi gom kết quả lại.
Nếu một nhân viên tiếp nhận quá tải, ta mở thêm quầy.
Vì quầy không giữ hồ sơ gốc, việc thêm quầy dễ hơn rất nhiều so với việc chia đôi một database đơn máy.
PD: bộ não điều phối của cụm
PD là viết tắt của Placement Driver.
Tài liệu TiDB gọi PD là “bộ não” của toàn bộ cụm.
PD giữ:
- Topology của cluster.
- Metadata phân bố dữ liệu.
- Region nào đang nằm ở TiKV nào.
- Replica nào đang là leader.
- Tình trạng tải của các node.
- Quy tắc đặt bản sao.
- Timestamp cho giao dịch phân tán.
- Lệnh điều chuyển dữ liệu giữa các node.
PD thường được triển khai thành một cụm có số node lẻ, ít nhất ba node, để duy trì tính sẵn sàng cao. (PingCAP Docs)
Giả sử vừa thêm một TiKV mới vào cluster.
Ổ đĩa của nó đang trống.
Các TiKV cũ thì đầy và nóng như bếp lẩu.
PD quan sát trạng thái rồi từ từ điều chuyển các Region sang node mới.
Nó không bê nguyên database từ máy này sang máy kia trong một lần.
Nó chuyển từng mảnh nhỏ, cân bằng dần tải đọc, tải ghi và dung lượng.
PD cũng cấp timestamp cho transaction.
Trong một hệ thống phân tán, thời gian không chỉ là con số hiển thị ở góc màn hình.
Nó là trật tự của thế giới.
Transaction nào bắt đầu trước?
Transaction nào commit sau?
Một câu truy vấn được phép nhìn thấy phiên bản dữ liệu nào?
Nếu nhiều máy tự nhìn đồng hồ riêng rồi tranh luận với nhau, hệ thống sẽ nhanh chóng biến thành một gia đình có năm chiếc đồng hồ treo tường, mỗi cái chỉ một giờ khác nhau và ai cũng tin mình đúng.
PD cung cấp một trục thời gian logic để toàn cluster thống nhất về thứ tự giao dịch.
TiKV: nơi dữ liệu thực sự nằm xuống
TiKV là lớp lưu trữ dạng key-value phân tán.
TiDB Server hiểu bảng, cột, index và SQL.
TiKV chủ yếu hiểu:
1Key → Value
Một dòng SQL như:
1order_id = 1001
2customer_id = 88
3total_amount = 12.000.000
khi đi xuống TiKV sẽ được mã hóa thành các key-value có thứ tự.
Có thể hình dung đơn giản:
1table/orders/row/1001
2 → {customer_id: 88, total_amount: 12000000}
Index cũng trở thành những key riêng:
1table/orders/index/customer_id/88/1001
2 → empty
Cách mã hóa thực tế phức tạp hơn, nhưng ý chính là:
Một dòng trong bảng không còn là một cục duy nhất nằm yên trên đĩa.
Nó có thể tạo ra nhiều entry:
- Dữ liệu của dòng.
- Primary key.
- Secondary index.
- Thông tin phiên bản.
- Lock tạm thời của transaction.
Không gian key-value của TiKV được chia thành những khoảng liên tục gọi là Region.
Mỗi Region giữ một đoạn key:
1[StartKey, EndKey)
Khi Region lớn lên đến một ngưỡng nhất định, nó có thể được tách thành những Region nhỏ hơn. Những Region này được phân phối trên nhiều TiKV để hệ thống mở rộng dung lượng và tải xử lý theo chiều ngang. (PingCAP Docs)
Nếu database là một đất nước, Region giống các tỉnh.
Mỗi tỉnh quản lý một dải địa chỉ nhất định.
PD biết tỉnh nào đang nằm trên máy nào.
TiDB Server biết phải gửi câu hỏi đến đâu.
Raft: một dữ liệu, nhiều bản sao
Dữ liệu quan trọng không nên chỉ có một bản.
Một ổ SSD có thể chết.
Một máy chủ có thể mất nguồn.
Một rack có thể mất mạng.
Một trung tâm dữ liệu có thể gặp sự cố.
TiKV thường giữ nhiều replica cho mỗi Region; cấu hình phổ biến mặc định là ba replica. Các replica của cùng một Region tạo thành một nhóm Raft. Mọi thay đổi được ghi thành Raft log và được sao chép sang các thành viên khác; một lần ghi chỉ được xem là thành công sau khi đạt sự đồng thuận của đa số replica. (GitHub)
Ví dụ một Region có ba replica:
1TiKV A: Leader
2TiKV B: Follower
3TiKV C: Follower
Khi có dữ liệu mới:
1Ứng dụng
2 ↓
3TiDB Server
4 ↓
5Region Leader trên TiKV A
6 ↓
7Raft Log
8 ├── TiKV B
9 └── TiKV C
Nếu A và B đã ghi thành công, nhóm có đa số hai trên ba.
Transaction có thể tiếp tục.
Nếu TiKV A chết, Raft có thể bầu một leader mới từ các replica còn sống.
Cả quá trình này không miễn phí.
Một dòng dữ liệu được ứng dụng ghi một lần có thể phải:
- Đi qua mạng.
- Ghi log.
- Nhân ra nhiều bản.
- Chờ đa số xác nhận.
- Đồng bộ trạng thái transaction.
Đó là cái giá vật lý của tính nhất quán và khả năng chịu lỗi.
Không có chữ “high availability” nào hoàn toàn ảo.
Phía sau nó luôn là thêm máy, thêm ổ đĩa, thêm dây mạng, thêm điện và thêm những gói tin đang chạy trong bóng tối.
TiFlash: cùng dữ liệu, một cách xếp khác
TiKV lưu dữ liệu theo hướng phù hợp với transaction.
Ví dụ:
1SELECT *
2FROM orders
3WHERE order_id = 1001;
Ta cần lấy một số ít dòng bằng primary key hoặc index.
Đây là thế giới OLTP:
- Nhiều giao dịch nhỏ.
- Đọc ít dòng.
- Ghi thường xuyên.
- Phản hồi nhanh.
- Cần nhất quán.
Nhưng một báo cáo có thể hỏi:
1SELECT
2 province,
3 SUM(total_amount)
4FROM orders
5WHERE order_date >= '2026-07-01'
6GROUP BY province;
Câu truy vấn này có thể phải đọc hàng trăm triệu dòng nhưng chỉ cần vài cột.
Đây là thế giới phân tích.
Nếu đọc toàn bộ dữ liệu dạng hàng, máy phải kéo qua nhiều cột không cần thiết.
TiFlash giải quyết vấn đề bằng cách giữ bản sao dữ liệu dạng cột.
Thay vì xếp:
1Đơn 1: ngày, tỉnh, khách hàng, doanh thu
2Đơn 2: ngày, tỉnh, khách hàng, doanh thu
3Đơn 3: ngày, tỉnh, khách hàng, doanh thu
TiFlash có thể tổ chức gần với:
1Cột ngày: ngày 1, ngày 2, ngày 3
2Cột tỉnh: tỉnh 1, tỉnh 2, tỉnh 3
3Cột doanh thu: tiền 1, tiền 2, tiền 3
Khi báo cáo chỉ cần tỉnh và doanh thu, hệ thống không phải đọc mọi cột khác.
TiFlash là thành phần giúp TiDB phục vụ mô hình HTAP — Hybrid Transactional and Analytical Processing. Dữ liệu được ghi vào TiKV rồi sao chép sang TiFlash dưới vai trò Raft Learner. Việc sao chép diễn ra bất đồng bộ, nhưng khi đọc, TiFlash kiểm tra tiến độ Raft và MVCC để cung cấp snapshot nhất quán. Ứng dụng không ghi trực tiếp vào TiFlash. (PingCAP Docs)
Ta có thể hình dung:
1 ┌── TiKV ───── OLTP
2Ứng dụng → TiDB ─────┤
3 └── TiFlash ── OLAP
Cùng một cánh cửa SQL.
Nhưng phía sau có hai cách sắp xếp dữ liệu cho hai loại công việc.
Một câu SQL đi qua TiDB như thế nào?
Giả sử ứng dụng gửi:
1SELECT order_id, total_amount
2FROM orders
3WHERE customer_id = 88
4 AND order_date >= '2026-07-01';
TiDB Server thực hiện một chuỗi công việc.
Bước 1: Phân tích SQL
TiDB xác định:
- Bảng nào được truy vấn.
- Cột nào được lấy.
- Điều kiện nào được sử dụng.
- Có index phù hợp không.
- Người dùng có quyền không.
Bước 2: Tạo execution plan
Optimizer cân nhắc:
- Full table scan.
- Index scan.
- Đọc TiKV.
- Đọc TiFlash.
- Thứ tự join.
- Phần xử lý nào có thể đẩy xuống storage.
Nếu thống kê sai hoặc thiếu index, optimizer có thể chọn một con đường rất dài để đến một căn nhà ngay đầu hẻm.
Distributed database không chữa khỏi SQL tệ.
Nó chỉ cho câu SQL tệ thêm nhiều máy để chạy.
Tài liệu TiDB vẫn xem full table scan, thiếu index và schema tạo ra contention hoặc hotspot là những nguyên nhân phổ biến của hiệu năng kém. (PingCAP Docs)
Bước 3: Tìm Region
TiDB lấy thông tin từ PD hoặc cache cục bộ để biết những Region nào chứa key cần đọc.
Bước 4: Đẩy công việc xuống storage
Thay vì kéo toàn bộ dữ liệu về TiDB Server rồi mới lọc, một phần filter và aggregation có thể được đẩy xuống TiKV hoặc TiFlash.
Ví dụ, TiKV chỉ trả những dòng có customer_id = 88.
TiFlash có thể tính một phần SUM() ngay gần nơi dữ liệu đang nằm.
Đây được gọi là đẩy tính toán xuống gần dữ liệu.
Bởi di chuyển vài con số tổng hợp thường rẻ hơn kéo hàng triệu dòng qua mạng.
Bước 5: Gom kết quả
TiDB Server nhận kết quả từ nhiều Region, merge, sort hoặc aggregate thêm rồi trả về ứng dụng.
Một câu SQL ngắn có thể khiến hàng chục máy cùng làm việc.
Nhưng người viết ứng dụng vẫn chỉ nhìn thấy một kết nối database.
Transaction phân tán hoạt động như thế nào?
Giả sử khách hàng đặt một đơn hàng.
Hệ thống cần:
- Tạo đơn.
- Trừ tồn kho.
- Ghi thanh toán.
- Cộng điểm thành viên.
Nếu một bước thất bại, toàn bộ giao dịch cần được hủy.
Trên một database đơn máy, engine có thể điều phối bằng log và lock bên trong một tiến trình.
Trong TiDB, các key liên quan có thể nằm trên nhiều Region.
TiDB hỗ trợ cả transaction lạc quan và transaction bi quan; chế độ bi quan được sử dụng mặc định trong các phiên bản hiện đại. Các giao dịch phân tán dựa trên MVCC và quy trình commit nhiều giai đoạn, trong đó transaction phải phối hợp các key nằm trên nhiều TiKV. (PingCAP Docs)
Một luồng đơn giản có thể hình dung:
1BEGIN
2 ↓
3Lấy start timestamp
4 ↓
5Đọc snapshot tương ứng
6 ↓
7Thực hiện thay đổi
8 ↓
9Prewrite các key
10 ↓
11Kiểm tra conflict
12 ↓
13Commit primary
14 ↓
15Commit các key còn lại
16 ↓
17COMMIT
Trong transaction bi quan, hệ thống có thể đặt lock sớm để ngăn các giao dịch khác cùng sửa một tài nguyên.
Trong transaction lạc quan, hệ thống giả định xung đột ít xảy ra, sau đó kiểm tra conflict lúc chuẩn bị commit.
Nếu hai nhân viên cùng sửa số lượng tồn kho của một sản phẩm, TiKV là nơi phát hiện xung đột vì các TiDB Server phía trên là stateless và không giữ trạng thái chung về mọi transaction đang chạy. (PingCAP Docs)
Điều này giải thích một nguyên tắc rất quan trọng:
Transaction phân tán càng dài và càng chạm nhiều dữ liệu thì càng đắt.
Một transaction lớn phải giữ nhiều thay đổi, nhiều lock, nhiều phiên bản và thực hiện nhiều lần trao đổi qua mạng. Tài liệu TiDB cũng đặt giới hạn và khuyến cáo riêng cho kích thước transaction vì memory của TiDB Server có thể tăng lên nhiều lần so với dữ liệu transaction thực tế. (PingCAP Docs)
Đừng lấy một triệu dòng rồi nhét vào một transaction chỉ vì câu lệnh SQL cho phép viết như vậy.
Cái database có thể làm được và cái database nên bị bắt làm là hai chuyện khác nhau.
TiDB giải quyết bài toán sharding như thế nào?
Trong mô hình sharding thủ công, ứng dụng thường phải biết:
1customer_id % 16 = shard
Điều này kéo theo:
- Routing logic trong code.
- Không dễ thay đổi số shard.
- Join xuyên shard phức tạp.
- Transaction xuyên shard khó.
- Rebalance dữ liệu đau đầu.
- Global index khó quản lý.
- Backup phải ghép nhiều nơi.
- Schema migration phải chạy trên mọi shard.
TiDB đẩy phần lớn việc phân mảnh xuống lớp storage.
Ứng dụng gửi SQL tới TiDB Server.
TiDB tìm Region.
PD điều phối Region.
TiKV lưu và sao chép dữ liệu.
Khi thêm TiKV, các Region có thể được di chuyển để sử dụng dung lượng mới.
Ứng dụng không cần tính:
1shard_id = customer_id % 16
Nhưng “không cần sharding thủ công” không có nghĩa “không cần hiểu dữ liệu phân bố”.
Nếu thiết kế key khiến toàn bộ lượt ghi dồn vào một Region, cluster có 100 máy vẫn có thể bị nghẽn tại một điểm.
Hotspot: có nhiều đường nhưng mọi người cùng chen vào một cổng
Giả sử bảng dùng primary key tự tăng:
1CREATE TABLE orders (
2 id BIGINT PRIMARY KEY AUTO_INCREMENT,
3 ...
4);
Các giá trị mới lần lượt là:
11001
21002
31003
41004
Vì key tăng liên tục, dữ liệu mới thường được ghi vào phần cuối của dải key.
Trong một thời điểm, phần cuối đó thuộc về một Region.
Hàng nghìn lượt ghi cùng lao vào Region này.
Những Region khác ngồi chơi.
Đây là write hotspot.
TiDB có cơ chế cân bằng tải, nhưng một access pattern dồn mọi lượt ghi vào một key range vẫn có thể tạo ra điểm nóng. Tài liệu TiDB khuyến nghị xem xét AUTO_RANDOM, SHARD_ROW_ID_BITS hoặc thiết kế partition phù hợp cho những bảng ghi đồng thời cao. (PingCAP Docs)
Ví dụ:
1CREATE TABLE orders (
2 id BIGINT PRIMARY KEY AUTO_RANDOM,
3 customer_id BIGINT NOT NULL,
4 total_amount DECIMAL(18, 2) NOT NULL
5);
Nhưng AUTO_RANDOM không phải thần dược.
Nó làm key khó đoán và không còn tăng tuần tự đẹp đẽ như số thứ tự xếp hàng.
Nếu ứng dụng phụ thuộc vào ID tăng dần để phân trang hoặc thể hiện thứ tự thời gian, cần thiết kế lại.
Trong distributed system, rất nhiều thứ có hai mặt.
Ta rải dữ liệu ra để ghi nhanh hơn.
Đổi lại, ta mất sự tuần tự đơn giản.
HTAP có thật sự thay thế Data Warehouse không?
TiDB có thể cho ứng dụng giao dịch trên TiKV và chạy phân tích gần thời gian thực trên TiFlash.
Điều này rất hữu ích trong các bài toán như:
- Dashboard đơn hàng trong ngày.
- Theo dõi tồn kho gần thời gian thực.
- Phát hiện gian lận.
- Quan sát hiệu quả chương trình khuyến mãi.
- Phân tích hành vi ngay sau khi phát sinh.
- Tra cứu lịch sử khách hàng trong lúc tư vấn.
Nhưng HTAP không đồng nghĩa với việc mọi Data Warehouse và Lakehouse nên bị xóa.
Một hệ thống phân tích vẫn có thể cần:
- Lưu lịch sử nhiều năm với chi phí thấp.
- Kết hợp hàng chục nguồn dữ liệu.
- Chạy ETL rất lớn.
- Lưu file, ảnh, log và dữ liệu phi cấu trúc.
- Huấn luyện machine learning.
- Quản trị dữ liệu Bronze, Silver, Gold.
- Truy vấn hàng petabyte trên object storage.
TiDB là database phân tán phục vụ dữ liệu đang sống và thay đổi.
Apache Iceberg là table format quản lý các file phân tích trong Data Lake.
Hai hệ thống đứng ở hai tầng khác nhau.
Một kiến trúc thực tế có thể là:
1Ứng dụng nghiệp vụ
2 ↓
3 TiDB
4 ↓
5 TiCDC
6 ↓
7 Kafka / Object Storage
8 ↓
9Apache Iceberg Lakehouse
TiDB giữ sự thật vận hành hiện tại.
Lakehouse giữ lịch sử dài hạn và phục vụ những phân tích rộng hơn.
Đừng bắt một công cụ làm mọi việc chỉ vì brochure của nó có nhiều mũi tên.
Hệ sinh thái xung quanh TiDB
TiDB không chỉ có bốn tiến trình chính.
PingCAP xây thêm nhiều công cụ để giải quyết những việc xảy ra trước, trong và sau khi database chạy.
TiUP
TiUP là công cụ quản lý package và cluster trong hệ sinh thái TiDB.
Nó có thể hỗ trợ:
- Cài các component.
- Triển khai cluster.
- Scale node.
- Upgrade.
- Kiểm tra trạng thái.
- Chạy môi trường thử nghiệm.
TiUP được đưa vào hệ sinh thái từ TiDB 4.0 để đơn giản hóa việc quản lý các thành phần TiDB, PD, TiKV và những công cụ liên quan. (PingCAP Docs)
TiUP không khiến distributed database trở nên đơn giản như SQLite.
Nó chỉ giúp con quái vật có dây cương.
TiDB Data Migration
TiDB Data Migration, thường gọi là DM, được dùng để chuyển dữ liệu từ những database tương thích MySQL như MySQL, MariaDB hoặc Aurora MySQL sang TiDB.
DM hỗ trợ:
- Full migration.
- Incremental replication.
- Đọc MySQL binlog.
- Di chuyển DML và DDL.
- Gom dữ liệu từ nhiều shard MySQL vào một TiDB.
Khả năng gom nhiều shard là một use case đáng chú ý.
Một công ty từng chia MySQL thành 32 shard có thể dùng DM để hợp nhất chúng vào một logical database TiDB, thay vì tiếp tục duy trì routing logic trong ứng dụng. (PingCAP Docs)
TiDB Lightning
TiDB Lightning phục vụ việc import khối lượng dữ liệu lớn.
Nếu DM giống xe tải chạy đều đặn mang dữ liệu từ MySQL sang TiDB, Lightning giống một đoàn tàu hàng được dùng trong đợt chuyển kho lớn.
Nó thường được dùng khi cần:
- Nạp dữ liệu lịch sử.
- Khởi tạo cluster mới.
- Di chuyển lượng dữ liệu lớn.
- Khôi phục dữ liệu từ file dump.
TiCDC
TiCDC đọc các thay đổi từ TiKV, sắp xếp và hợp nhất chúng rồi gửi sang hệ thống downstream thông qua các changefeed.
Downstream có thể là:
- Kafka.
- Một TiDB khác.
- Một hệ thống tương thích MySQL.
- Pipeline dữ liệu khác.
TiCDC là cây cầu để dữ liệu đang thay đổi trong TiDB chảy sang các hệ thống phía sau. (PingCAP Docs)
Ví dụ:
1TiDB chính
2 ↓
3TiCDC
4 ├── Kafka
5 ├── Lakehouse
6 ├── Search engine
7 └── TiDB dự phòng
Backup & Restore
BR là công cụ backup và restore phân tán của TiDB.
Nó hỗ trợ:
- Snapshot backup.
- Log backup.
- Full restore.
- Khôi phục đến một thời điểm cụ thể.
Một hệ thống có ba replica vẫn cần backup.
Replica bảo vệ khỏi hỏng máy.
Nó không luôn bảo vệ khỏi:
1DELETE FROM orders;
Nếu câu lệnh xóa được commit hợp lệ, Raft sẽ rất tận tâm sao chép sai lầm đó sang các replica còn lại.
High availability giữ cho hệ thống tiếp tục hoạt động.
Backup giúp quay lại trước khi con người làm điều ngu ngốc.
BR hỗ trợ cả snapshot và Point-in-Time Recovery, nhưng tài liệu khuyến nghị sử dụng phiên bản BR tương thích với phiên bản cluster và phải xây quy trình phục hồi rõ ràng. (PingCAP Docs)
TiDB Dashboard
TiDB Dashboard giúp quan sát:
- Slow query.
- Top SQL.
- Cluster topology.
- Region.
- Hotspot.
- Node health.
- Query plan.
- CPU và latency.
Top SQL có thể chỉ ra những nhóm câu lệnh đang ăn nhiều CPU nhất trên TiDB và TiKV. (PingCAP Docs)
Một database phân tán không được giám sát giống một đàn voi chạy trong sương mù.
Chúng rất mạnh.
Nhưng ta không biết con nào sắp đạp lên hàng rào.
Ví dụ TiDB trong hệ thống bán lẻ
Giả sử doanh nghiệp có:
- 3.000 cửa hàng.
- Một triệu đơn mỗi ngày.
- Hàng chục triệu thay đổi tồn kho.
- Website và ứng dụng.
- Nhiều chương trình khuyến mãi.
- Nhu cầu báo cáo gần thời gian thực.
Một kiến trúc có thể như sau:
1Website / Mobile / POS
2 ↓
3 Load Balancer
4 ↓
5 TiDB Servers
6 ↓
7 ┌──────┴──────┐
8 │ │
9 TiKV TiFlash
10 OLTP OLAP
11 │ │
12 └──────┬──────┘
13 │
14 TiCDC
15 │
16 Kafka / Lakehouse
TiKV phục vụ
- Tạo đơn hàng.
- Cập nhật trạng thái thanh toán.
- Trừ tồn kho.
- Ghi lịch sử giao nhận.
- Tra cứu khách hàng.
- Kiểm tra mã khuyến mãi.
TiFlash phục vụ
- Doanh thu theo tỉnh.
- Tỷ lệ hết hàng.
- Hiệu quả khuyến mãi.
- Sản phẩm bán nhanh trong một giờ.
- Số đơn đang trễ.
- Tồn kho theo vùng.
Lakehouse phục vụ
- Lịch sử nhiều năm.
- Phân tích tài chính.
- Dữ liệu từ nhiều công ty.
- Machine learning.
- Forecast.
- Recommendation.
- Báo cáo quản trị lớn.
Không có một database duy nhất nuốt cả công ty.
Có một dòng chảy dữ liệu, trong đó mỗi hệ thống chịu trách nhiệm cho một loại sự thật.
Khi nào nên dùng TiDB?
Khi database đơn máy đã chạm giới hạn
Dữ liệu hoặc lưu lượng đã vượt khỏi khả năng mở rộng kinh tế của một máy.
Mỗi lần tăng trưởng lại phải mua máy lớn hơn.
Thời gian bảo trì và failover ngày càng đáng sợ.
Khi doanh nghiệp đang đau vì sharding
Ứng dụng có quá nhiều logic:
1Nếu khách miền Nam → shard A
2Nếu khách miền Bắc → shard B
3Nếu khách VIP → shard C
Cross-shard query, transaction và schema migration đã trở thành một ngành nghề riêng trong công ty.
Khi cần tính nhất quán mạnh
Các bài toán như:
- Thanh toán.
- Tồn kho.
- Điểm thành viên.
- Đặt hàng.
- Giao dịch tài chính.
không thể chấp nhận việc hai bản sao cùng tin vào hai số dư khác nhau.
TiDB hỗ trợ ACID transaction xuyên các node và dùng Raft để sao chép dữ liệu theo đa số. (GitHub)
Khi đội ngũ đã quen MySQL
Ứng dụng dùng MySQL driver, ORM và cú pháp phổ biến.
TiDB có thể giảm lượng code phải thay đổi so với việc chuyển sang một hệ thống có protocol hoàn toàn khác.
Nhưng vẫn phải kiểm tra compatibility, không được tin bằng cảm xúc.
Khi cần transaction và phân tích gần thời gian thực
Doanh nghiệp không muốn chờ pipeline ETL vài giờ mới nhìn thấy dữ liệu mới.
TiKV và TiFlash cho phép phục vụ hai kiểu workload trên cùng hệ thống logic.
Khi có đội vận hành đủ trưởng thành
TiDB giảm khó khăn của sharding thủ công.
Nó không xóa sự phức tạp.
Nó chuyển sự phức tạp vào:
- Raft.
- Region.
- PD.
- TiKV.
- Network.
- Transaction phân tán.
- Capacity planning.
- Observability.
- Backup.
- Upgrade.
Công ty cần người hiểu những thứ đó.
Khi nào không nên dùng TiDB?
Khi database chỉ có vài chục GB
Một ứng dụng nhỏ, tải thấp và có một PostgreSQL hoặc MySQL đang chạy tốt chưa chắc cần một cluster phân tán.
Ba PD, ba TiKV, nhiều TiDB Server, monitoring và backup có thể là một đội quân quá lớn chỉ để bảo vệ một cửa hàng bán ba tô phở mỗi ngày.
Khi cần latency cực thấp trên một máy
Một database đơn máy có thể hoàn thành transaction mà không cần trao đổi qua mạng giữa nhiều node.
TiDB phải trả giá cho:
- Network hop.
- Timestamp.
- Lock.
- Replication.
- Raft majority.
- Distributed commit.
Không thể vừa yêu cầu dữ liệu có ba bản trên ba máy, vừa tức giận vì nó không nhanh như một lần ghi vào RAM cục bộ.
Khi ứng dụng phụ thuộc sâu vào tính năng riêng của MySQL
TiDB tương thích cao với giao thức và nhiều cú pháp MySQL, nhưng không tương thích hoàn toàn. Một số tính năng như stored procedure, stored function, trigger, event và một số dạng full-text search không được hỗ trợ trong nhiều mô hình triển khai. TiDB cũng không dùng giao thức replication MySQL truyền thống mà cung cấp các công cụ riêng. (PingCAP Docs)
Nếu toàn bộ nghiệp vụ đang nằm trong 600 stored procedure và 200 trigger, việc di chuyển sẽ không phải là:
1Đổi connection string rồi đi ăn trưa.
Khi workload chủ yếu là phân tích lịch sử khổng lồ
Nếu dữ liệu hầu như chỉ được ghi theo batch, ít cập nhật và chủ yếu được scan trên object storage, một Lakehouse hoặc Data Warehouse chuyên dụng có thể kinh tế hơn.
TiDB không nên được dùng như một ổ cứng đắt tiền để chứa mọi log từ năm 2007.
Khi đội ngũ chưa muốn học distributed system
Một database phân tán không biến mất chỉ vì giao diện bên ngoài trông giống MySQL.
Nếu không muốn hiểu failure domain, quorum, hotspot, network latency và backup phân tán, tốt hơn hết chưa nên đặt dữ liệu quan trọng nhất của công ty vào đó.
Những việc cần làm khi triển khai TiDB
Kiểm kê mức độ tương thích MySQL
Trước khi di chuyển, cần kiểm tra:
- Kiểu dữ liệu.
- Stored procedure.
- Trigger.
- Event.
- Full-text index.
- SQL mode.
- Collation.
- Transaction isolation.
- Auto increment.
- Cách sử dụng DDL.
- Công cụ backup.
- Cơ chế replication.
- ORM và driver.
“Mysql-compatible” là một dải phổ.
Không phải dấu bằng.
Benchmark bằng workload thật
Đừng chỉ chạy một bài benchmark có ba câu SQL rất đẹp.
Hãy lấy:
- Top SQL production.
- Tỷ lệ đọc và ghi.
- Kích thước transaction.
- Số kết nối.
- Query join lớn.
- Batch job.
- Tình huống lock conflict.
- Dữ liệu lệch phân bố.
- Ngày khuyến mãi.
- Thời điểm cuối tháng.
Sau đó kiểm tra:
- P50.
- P95.
- P99.
- QPS.
- CPU.
- Network.
- Disk latency.
- Hot Region.
- Thời gian failover.
- Thời gian phục hồi.
Database thường chạy tốt trong demo.
Production là nơi khách hàng, dữ liệu bẩn và định luật Murphy cùng đăng nhập.
Thiết kế primary key để tránh hotspot
Không nên bê nguyên mọi bảng có AUTO_INCREMENT từ MySQL sang mà không kiểm tra access pattern.
Với bảng có lượng ghi lớn, cần cân nhắc:
AUTO_RANDOM.- UUID được thiết kế phù hợp.
- Hash partition.
- Sharding implicit RowID.
- Phân bố khóa theo nghiệp vụ.
Nhưng cũng không nên ngẫu nhiên hóa mọi key một cách vô thức, vì điều đó ảnh hưởng đến locality và cách truy vấn.
Giữ transaction ngắn
Nên:
- Commit sớm.
- Chia batch hợp lý.
- Tránh giữ transaction lúc chờ người dùng.
- Không gọi API bên ngoài giữa
BEGINvàCOMMIT. - Tránh cập nhật hàng triệu dòng trong một transaction bình thường.
- Theo dõi lock conflict.
Ví dụ xấu:
1BEGIN
2 ↓
3Đọc database
4 ↓
5Gọi API thanh toán mất 30 giây
6 ↓
7Chờ hệ thống kho
8 ↓
9Gửi email
10 ↓
11UPDATE
12 ↓
13COMMIT
Transaction không phải căn nhà nghỉ.
Đừng để nó thuê phòng quá lâu.
Tối ưu SQL và index như database bình thường
Thêm node không sửa được:
1SELECT *
2FROM orders
3WHERE customer_phone = '090...';
nếu customer_phone không có index và bảng có vài tỷ dòng.
Vẫn phải:
- Đọc execution plan.
- Tạo index đúng.
- Cập nhật statistics.
- Giảm số dòng scan.
- Chọn kiểu join phù hợp.
- Tránh lấy tất cả cột.
- Quan sát Top SQL.
Distributed SQL vẫn là SQL.
Thiết kế failure domain
Ba replica đặt trên cùng một máy chủ không tạo ra high availability.
Ba máy chủ cùng nằm trên một rack vẫn có thể cùng chết khi rack mất điện.
Cần tính:
- Host.
- Rack.
- Availability Zone.
- Data center.
- Network.
- Placement rule.
- Số replica.
- Vị trí leader.
Raft không thể cứu ba máy cùng bị rút một ổ điện.
Thử restore định kỳ
Backup thành công không đồng nghĩa restore thành công.
Cần thực hành:
- Khôi phục toàn cluster.
- Khôi phục một database.
- Khôi phục một bảng.
- Point-in-Time Recovery.
- Đo Recovery Time Objective.
- Kiểm tra dữ liệu sau phục hồi.
- Xác nhận ứng dụng kết nối được.
File backup chưa từng được restore chỉ là một niềm tin được nén lại.
Chỉ tạo TiFlash replica cho bảng cần thiết
Không phải bảng nào cũng cần một bản sao dạng cột.
TiFlash làm tăng:
- Dung lượng.
- Network replication.
- Chi phí máy.
- Công việc vận hành.
Nên chọn các bảng:
- Lớn.
- Hay scan.
- Có aggregation.
- Cần báo cáo gần thời gian thực.
- Cần cách ly OLAP khỏi OLTP.
Những điều không nên làm
Không nghĩ TiDB miễn phí vì là mã nguồn mở
Mã nguồn có thể tải miễn phí.
Nhưng production cần:
- Máy chủ.
- Ổ đĩa.
- Network.
- Backup storage.
- Monitoring.
- Nhân sự.
- On-call.
- Nâng cấp.
- Kiểm thử.
- Xử lý sự cố.
Open source xóa chi phí giấy phép trong một số trường hợp.
Nó không xóa định luật kinh tế.
Không triển khai tất cả component trên một máy
Một máy chạy:
- PD.
- TiDB.
- TiKV.
- TiFlash.
- Prometheus.
- Grafana.
có thể phù hợp để học.
Nó không chứng minh khả năng chịu lỗi.
Nếu máy đó chết, cả “cluster phân tán” biến mất cùng lúc, giống một gia đình bốn người cùng ngồi trên một chiếc xe máy rồi tuyên bố đã phân tán rủi ro giao thông.
Không dùng thêm node để che SQL xấu
Một full table scan trên một tỷ dòng có thể được chia cho nhiều máy.
Nhưng một tỷ dòng vẫn phải được đọc ở đâu đó.
Thêm máy đôi khi chỉ biến một sai lầm nhỏ thành một sai lầm chạy song song.
Không tạo transaction khổng lồ
Một transaction quá lớn có thể:
- Ăn nhiều memory.
- Giữ lock lâu.
- Tăng thời gian commit.
- Tăng chi phí retry.
- Ảnh hưởng replication.
- Làm hệ thống khó phục hồi khi thất bại.
Hãy chia công việc thành những đơn vị có thể kiểm soát và chạy lại.
Không xem HTAP là bữa trưa miễn phí
OLTP và OLAP vẫn cạnh tranh:
- CPU.
- Network.
- Storage.
- Cache.
- Băng thông.
- Lịch điều phối.
TiFlash giúp cách ly workload tốt hơn.
Nhưng nếu đặt TiKV và TiFlash trên cùng một máy rồi chạy báo cáo toàn quốc giữa giờ cao điểm, các electron vẫn phải chen nhau qua cùng CPU và bus memory.
Không bỏ qua network
Trong database đơn máy, gọi một function có thể chỉ đi qua memory.
Trong database phân tán, một transaction có thể đi:
1Application
2 ↓
3Load Balancer
4 ↓
5TiDB Server
6 ↓
7PD
8 ↓
9TiKV Leader
10 ↓
11TiKV Followers
Mỗi mũi tên là:
- Một socket.
- Một packet.
- Một lần chờ.
- Một khả năng timeout.
- Một điểm có thể mất kết nối.
Khi triển khai TiDB, network không còn là sợi dây cắm phía sau máy.
Nó là một phần của database.
Không dùng TiDB để thay mọi hệ thống dữ liệu
TiDB không tự động thay thế:
- Redis.
- Kafka.
- Elasticsearch.
- Data Lake.
- Apache Iceberg.
- Data Warehouse.
- Object Storage.
- Time-series database.
Nó có thể làm giảm số hệ thống trong một số kiến trúc.
Nhưng mỗi workload vẫn cần được xem xét bằng dữ liệu thật.
Một chiếc búa titan vẫn không phải dụng cụ tốt nhất để cắt tóc.
Tổng kết
TiDB là một dự án cơ sở dữ liệu mã nguồn mở đáng chú ý được PingCAP khởi tạo trong hệ sinh thái công nghệ Trung Quốc và phát triển cùng cộng đồng toàn cầu.
Nó không phải MySQL được kéo giãn.
Nó là một database SQL phân tán được xây lại từ phần móng:
- TiDB Server nhận và tối ưu SQL.
- PD giữ metadata, timestamp và điều phối cluster.
- TiKV lưu dữ liệu dạng key-value theo Region.
- Raft sao chép mỗi Region thành nhiều replica.
- TiFlash giữ bản sao dạng cột cho phân tích.
- TiUP quản lý cluster.
- DM hỗ trợ migration từ MySQL.
- TiCDC đưa thay đổi ra downstream.
- BR backup và phục hồi dữ liệu.
Điều TiDB bán không phải chỉ là khả năng chứa nhiều terabyte hay petabyte.
Nó bán một ảo giác có ích:
Rất nhiều máy tính, nhưng ứng dụng được phép nhìn chúng như một database.
Phía sau ảo giác đó là một thế giới vật lý phức tạp.
Dữ liệu bị bẻ thành key-value.
Key-value được chia thành Region.
Region được nhân thành replica.
Replica trao đổi Raft log.
Transaction lấy timestamp, giữ lock, kiểm tra conflict rồi commit qua nhiều máy.
Một câu SQL ngắn có thể làm hàng chục CPU thức giấc.
Một lần ghi đơn hàng có thể chạy qua nhiều card mạng, nhiều ổ SSD và nhiều trung tâm dữ liệu trước khi ứng dụng nhận được chữ:
1COMMIT OK
TiDB che giấu nhiều sự phức tạp.
Nhưng nó không xóa sự phức tạp khỏi thế giới.
Nó đóng gói sự phức tạp thành một hệ thống có quy tắc, có metadata, có consensus và có những công cụ đủ tốt để con người vận hành.
Bởi vậy, đừng dùng TiDB chỉ vì nó là distributed, cloud-native, HTAP hay mã nguồn mở.
Hãy dùng nó khi bài toán thật sự cần:
- Mở rộng theo chiều ngang.
- Giao dịch phân tán.
- Tính nhất quán mạnh.
- Khả năng chịu lỗi.
- Giảm gánh nặng sharding thủ công.
- Phân tích gần thời gian thực.
Còn nếu một máy MySQL vẫn đang chạy khỏe, backup tốt, tải thấp và chưa ai phải thức lúc hai giờ sáng để di chuyển shard, có lẽ chưa cần gọi cả một thành phố đến để làm công việc của một căn nhà.
Cảm ơn các bạn đã quan tâm và theo dõi bài viết, hẹn gặp lại ở các bài viết tiếp theo.
Bình luận