Có bao giờ bạn tự hỏi sao có người ở quận 8, người ở quận 5, quận abc không ?? Dù có hay không thì mình tin rằng công tác phân lô chia đất sẽ rất thuận tiện cho việc quản lý. Câu chuyện đó cũng là ý tưởng để mình liên hệ với một chiêu thức trong database - chiêu thức mang tên Partition. Nay mình sẽ cùng các bạn đi khám phá xem chiêu thức này có đáng đồng tiền bạc gạo để bỏ công sức tập luyện hay không.
1. Lịch sử ra đời
Khi hệ thống phát triển qua năm tháng, đi kèm với niềm vui chúng ta phải đối mặt với rất nhiều thử thách. Một trong số đó là việc quản lý, truy vấn trên tập dữ liệu khổng lồ nhưng vẫn đảm bảo tốc độ hiệu suất của hệ thống phải xịn để đáp ứng yêu cầu của người dùng. Ngoài tuyệt kỹ Indexing (Mình sẽ giới thiệu ở bài sau) khá lợi hại, thì Partition - chiêu thức giúp phân chia dữ liệu từ một bảng siêu to ra nhiều bảng con giúp db cải thiện tốc độ truy vấn và dễ dàng quản lý.
Thời gian thoi đưa, qua nhiều năm chinh chiến, các vị tiền bối đã nâng tầm chiêu thức lên để phục vụ cho từng mục đích và được chia ra làm 2 trường phái :
- Horizontal Partitioning: Chia theo dòng.
Từ sơ đồ trên, nếu mình muốn truy vấn thông tin user ở quận 3 thì không cần duyệt qua toàn bộ bảng (24 quận huyện cả thành phố) mà chỉ cần tìm kiếm trên partition users-district-3 thôi nên sẽ tiết kiệm được rất nhiều thời gian.
- Vertical Partitioning: Chia theo cột. Lấy ví dụ mình cần phải thao tác trên một bảng có 200 cột (siêu to) tuy nhiên thông thường mình chỉ cần truy vấn trên 50 cột thôi thì mình sẽ chọn giải pháp phân chia theo 2 bảng: 1 bảng (gồm 50 cột thường truy vấn) + 1 bảng (gồm 150 cột ít truy vấn)
2. Tuyệt chiêu Partition có gì ?
Lướt nhẹ qua lịch sử xong rồi, mình sẽ cùng các bạn tìm hiểu chi tiết từng loại Partition trong PostgresSQL. Mình sẽ cố gắng minh họa qua việc quản lý một tập dữ liệu gồm có 10 triệu học sinh. Bắt đầu thôi nào ...
Trước tiên, mình sẽ dựng 1 bảng students bình thường, chưa áp dụng tuyệt chiêu partition.
-- Tạo bảng Students
CREATE TABLE students (
id SERIAL NOT NULL,
name TEXT,
year TEXT,
grade INT
);
-- Insert 10 triệu dữ liệu
INSERT INTO students (name, year, grade)
SELECT
concat('student-name-', floor(random()*10)::text),
concat('K-', (floor(random() * 10) + 1)::text),
floor(random() * 10)
FROM generate_series(0, 10000000);
-- Kiểm tra lại dữ liệu đã insert
SELECT * FROM students LIMIT 10;
-- Ví dụ 1 record
-- | id: 1
-- | name: 'student-name-1'
-- | year: 'K-1'
-- | grade: 8
Range partition
"Ngày chưa giông bão, mặt hồ lăng im. Khi bão tràn về, tốc độ ghì db"
Dòng đời đẩy đưa, đẩy bạn vào yêu cầu phải xếp loại học sinh theo điểm số. Nếu may mắn thì dừng lại ở mức 10 triệu record, lỡ xui thì chục triệu, cỡ này mà truy vấn thì db ná thở luôn. Nhớ khi xưa ta bé:
- tháng nào điểm dưới 4 (loại trung bình)
- từ 5 -> 7 (loại khá rồi)
- từ 8 trở lên (loại giỏi nè).
CREATE TABLE students_partition_range (
id SERIAL NOT NULL,
name TEXT,
year TEXT,
grade INT
) PARTITION BY RANGE (grade);
-- Loại trung bình
CREATE TABLE students_range_average PARTITION OF students_partition_range FOR VALUES FROM (0) TO (5);
-- Loại khá
CREATE TABLE students_range_good PARTITION OF students_partition_range FOR VALUES FROM (5) TO (8);
-- Loại giỏi
CREATE TABLE students_range_excellent PARTITION OF students_partition_range FOR VALUES FROM (8) TO (10);
-- Insert dữ liệu
INSERT INTO students_partition_range (name, year, grade)
SELECT
concat('student-name-', floor(random()*10)::text),
concat('K-', (floor(random() * 10) + 1)::text),
floor(random() * 10)
FROM generate_series(0, 10000000);
Sau khi insert xong, dữ liệu sẽ được phân chia về đúng partition theo điều kiện xếp loại tương ứng theo khung điểm.
Giờ là lúc so sánh xem tuyệt chiêu partition có thật sự lợi hại như lời đồn không
Yeahh, kết quả là range partition xịn hơn.
- Thứ nhất: cost scan của bảng students cao hơn hẳn so với cost scan của students_partition_range (khung cam). Cost càng lớn thì càng ghì db.
- Thứ hai: execute time của partitions cũng nhanh hơn hẳn (khung xanh)
List partition
"Mỗi năm đến hè lòng man mác buồn"
Hè về, cũng là lúc các sĩ tử bước vào kì thi tuyển sinh Đại học rất quan trọng. Dòng đời lại tiếp tục đẩy đưa chúng ta đến một yêu cầu mới là thống kê số lượng của thí sinh các khóa trước (theo thống kê năm 2020 có gần 1 triệu sĩ tử). Và rồi thử thách lại đến, viễn cảnh db ná thở lại hiện ra. Giờ phải làm sao ta ?? Hay thử chia theo các niên khóa:
- K-10: 1.000.000 thí sinh
- K-9: 1.200.000 thí sinh
- K-8: 980.000 thí sinh
CREATE TABLE students_partition_list (
id SERIAL NOT NULL,
name TEXT,
year TEXT,
grade INT
) PARTITION BY LIST (year);
-- Niên khóa 10
CREATE TABLE students_list_k10 PARTITION OF students_partition_list FOR VALUES IN ('K-10');
-- Niên khóa 9
CREATE TABLE students_list_k10 PARTITION OF students_partition_list FOR VALUES IN ('K-9');
-- Niên khóa 8
CREATE TABLE students_list_k10 PARTITION OF students_partition_list FOR VALUES IN ('K-8');
-- Và các partitions cho các niên khóa tương ứng
-- Insert dữ liệu
INSERT INTO students_partition_list (name, year, grade)
SELECT
concat('student-name-', floor(random()*10)::text),
concat('K-', (floor(random() * 10) + 1)::text),
floor(random() * 10)
FROM generate_series(0, 10000000);
Sau khi đã chuẩn bị xong mọi thứ, tiếp theo mình cùng các bạn kiểm tra performance xem độ hiệu quả có được cải thiện hay không
Chắc bạn cũng đoán được list partition xịn hơn phải không ?
- Về tốc độ thì chắc là điều hiển nhiên rồi.
- Còn về cost scan khi có partition (qua ví dụ tìm học sinh của K-9) chỉ bằng 1/10 so với bảng gốc => đáng mong đợi.
3. Lời kết
Bài viết cũng khá dài rồi, mình xin dừng bút. Vậy là mình đã cùng các bạn khám phá tuyệt chiêu partition trong database. Chiêu thức được nâng tầm lên 2 trường phái horizontal và vertical. Đi sâu vào chiêu thức sẽ có từng cách giao chiến mà ở trên mình mới mò được 2 thôi: Range partition và List partition đành hẹn lại mọi người ở phần sau. Bài viết chắc chắn còn rất nhiều thiếu sót, mình hi vọng được mọi người đóng góp và chia sẻ để mình có thể học hỏi và cải thiện. Xin chân thành cảm ơn mọi người đã dành thời gian đọc bài viết của mình.
4. Tham khảo:
- Postgres Table Partitioning: chriserwin.com/table-partitioning
- Postgres Partitioning Guide: buckenhofer.com/2021/01/postgresql-partitio..