Trong bài viết này mình sẽ so sánh hiệu suất Query của PostgreSQL và MySQL để bạn có bức tranh toàn cảnh về hai DBMS này. Bài viết sẽ không dựa trên đánh giá chủ quan cá nhân mình mà luôn kèm theo các số liệu benchmark cụ thể, hy vọng có thể giúp bạn trả lời được các câu hỏi sau đây:
- Kiến trúc của PostgreSQL và MySQL có gì khác biệt ?
- Vì sao Point Query trong MySQL lại nhanh hơn nhưng Range Query lại chậm hơn PostgreSQL ?
- Nên lựa chọn DBMS nào cho phù hợp ?
Phiên bản mới nhất của 2 DBMS này là PostgreSQL 17 và MySQL 8.1, bài viết này sẽ không so sánh hiệu suất Query toàn diện giữa PostgreSQL và MySQL: thiếu các kịch bản đọc, ghi đồng thời trên dữ liệu lớn, do mình chưa tìm được nguồn benchmark đáng tin cậy.
1. So sánh MySQL và PostgreSQL
DB-Engines Award là một giải thưởng hàng năm, vinh danh hệ quản trị cơ sở dữ liệu (DBMS) có mức độ phổ biến tăng trưởng nhanh nhất trong năm, dựa trên DB-Engines Ranking. PostgreSQL đã nhiều lần nhận được giải thưởng này, khẳng định vị thế và sự phổ biến ngày càng tăng của nó trong cộng đồng phát triển phần mềm.
Có vẻ MySQL khá là hụt hơi trên đường đua này, có nhiều ý kiến cho rằng MySQL, mặc dù có những cải tiến lớn trong phiên bản 8.0, nhưng tính năng của nó vẫn chưa mạnh mẽ hoặc đa dạng như PostgreSQL trong nhiều lĩnh vực. Mình không rõ lắm về định hướng của Oracle (công ty mẹ) về MySQL trong tương lai nhưng có vẻ như PostgreSQL đang có tốc độ phát triển nhanh hơn.
PostgreSQL là một dự án mã nguồn mở thực sự, không bị kiểm soát bởi một công ty lớn như Oracle. Điều này có nghĩa là cộng đồng phát triển của PostgreSQL có tự do để quyết định hướng đi và phát triển nhanh chóng các tính năng mới mà không bị giới hạn bởi chiến lược kinh doanh của một công ty cụ thể.
1.1 Architecture
1.1.1 PostgreSQL
- Process-Based Model: Mỗi kết nối mới tạo ra một process riêng biệt, có nghĩa là nếu một kết nối gặp sự cố (ví dụ: lỗi truy cập bộ nhớ không hợp lệ), chỉ kết nối đó bị ảnh hưởng, còn các kết nối khác vẫn hoạt động bình thường, mang lại khả năng isolation tốt hơn, để đạt được độ ổn định cao.
Giả sử bạn có 1000 người dùng đồng thời kết nối vào hệ thống Postgres, mỗi kết nối sẽ tạo ra một tiến trình riêng, điều này sẽ sử dụng rất nhiều tài nguyên (RAM, CPU). Vì mô hình sử dụng process của Postgres tiêu tốn tài nguyên hơn, nên trên production, để tối ưu hoá việc sử dụng tài nguyên, bạn nên dùng các công cụ connection pooler như PgBouncer hay pgcat để quản lý và giới hạn số lượng kết nối.
- Heap-Organized Tables: Là cách PostgreSQL lưu trữ dữ liệu mà không sắp xếp theo thứ tự của bất kỳ cột cụ thể nào, bao gồm cả primary key. Dữ liệu trong bảng heap được lưu trữ theo thứ tự thêm vào, và không có sự liên kết vật lý giữa vị trí của dữ liệu và giá trị của primary key hoặc bất kỳ chỉ mục nào khác.
1.1.2 MySQL
- Thread-Based Model: Mỗi kết nối tạo ra một thread (luồng) riêng, nhiều thread cùng chia sẻ tài nguyên của một tiến trình. Điều này làm cho MySQL tiết kiệm tài nguyên hơn, nhưng nếu một luồng gặp lỗi nghiêm trọng, toàn bộ hệ thống có thể bị ảnh hưởng do các luồng khác chia sẻ chung tài nguyên.
- Clustered Indexes: InnoDB, engine mặc định trong MySQL, hỗ trợ clustered indexes, giúp lưu trữ dữ liệu vật lý theo thứ tự của primary key. Clustered index giúp dữ liệu và chỉ số (employee_id = 10) được lưu gần nhau, vì vậy chỉ cần truy cập vào chỉ số là có thể tìm được dữ liệu tương ứng mà không cần phải tìm thêm trong các bảng khác.
1.2 Query Performance (Read Only)
Cá nhân mình nghĩ không thể dựa trên kinh nghiệm quá khứ để đánh giá về 2 DBMS này, vì chúng luôn luôn được đội phát triển cải tiến liên tục, nhiều tính năng mới sẽ được thêm vào, chúng ta cũng cần benchmark nhiều trường hợp (ít/nhiều/quá nhiều dữ liệu, ...) để có cái nhìn khách quan hơn.
Mark Callaghan đã có một bài benchmark khá chi tiết về hiệu suất của PostgresSQL và MySQL, để tìm hiểu thêm các bạn có thể tham khảo tại đây, anh ấy còn so sánh hiệu suất của MySQL v5 và v8, trong đó:
- Point query: chỉ trả về một record hoặc một nhóm nhỏ record, điều kiện truy vấn thường là khoá chính hoặc các cột đã đánh index. VD:
SELECT * FROM khach_hang WHERE ID = 2
- Range query: Thay vì tìm kiếm một record cụ thể, truy vấn này trả về nhiều record nằm trong phạm vi nhất định.VD:
SELECT * FROM khach_hang WHERE Tuổi BETWEEN 25 AND 40
. Tác giả đã tạo secondary index cho các column này trước khi thực hiện bài test với range query.
Thử nghiệm này kiểm tra hiệu suất của PostgresSQL và MySQL trong 3 tình huống khác nhau: từ dữ liệu vừa với bộ nhớ đến trường hợp dữ liệu lớn, phải phụ thuộc vào việc đọc ghi từ đĩa.
- cached - dữ liệu hoàn toàn nằm trong bộ nhớ đệm.
- less IO-bound - dữ liệu lớn hơn dung lượng bộ nhớ, 64 triệu dòng trong mỗi bảng.
- more IO-bound - dữ liệu vượt dung lượng bộ nhớ rất nhiều, 200 triệu dòng trong mỗi bảng.
PostgresSQL | MySQL | |
---|---|---|
cached | Postgres nhanh hơn trong các tác vụ ghi dữ liệu lớn (write-heavy) và range query, tốc độ xử lý point query tương đương MySQL | Tốc độ xử lý point query tương đương PostgreSQL |
less IO-bound | PostgreSQL nhanh hơn trong các tác vụ ghi dữ liệu lớn (write-heavy) và range query | MySQL nhanh hơn trong các point query. MySQL thực hiện khoảng 0.1 thao tác đọc mỗi truy vấn, trong khi Postgres thực hiện khoảng 3 thao tác đọc. Với InnoDB, các point query trong MySQL chỉ cần truy cập chỉ mục chính (PK index), trong khi Postgres phải truy cập cả chỉ mục chính và bảng dữ liệu để lấy tất cả các cột |
more IO-bound | PostgreSQL nhanh hơn trong các tác vụ ghi dữ liệu lớn (write-heavy) và range query,Postgres phải thực hiện số lần đọc I/O gấp 2 lần so với MySQL cho mỗi point query. | MySQL nhanh hơn trong các point query, thực hiện nhiều thao tác I/O hơn cho mỗi range query |
1.2.1 Point Query
Khi bạn thực hiện một point query trong PostgreSQL, hệ thống sẽ phải thực hiện các bước sau:
- Trước hết, PostgreSQL sẽ tìm kiếm vị trí của row thông qua primary key index. Chỉ mục này chỉ lưu trữ các vị trí của row trong bảng, chứ không lưu trữ dữ liệu thực tế.
- Sau đó, PostgreSQL sẽ sử dụng kết quả từ chỉ mục để tìm đến vị trí cụ thể trong bảng heap để lấy dữ liệu thực tế.
MySQL sẽ thực hiện các bước sau đây cho một point query:
- MySQL sẽ tìm kiếm giá trị vị trí của hàng trong clustered index, dữ liệu của hàng này đã được lưu trữ trực tiếp cùng với chỉ mục.
- Do dữ liệu được lưu trữ trong clustered index, MySQL không cần thực hiện thêm bước nào để tìm dữ liệu từ một cấu trúc khác, và có thể trả về kết quả ngay lập tức.
1.2.2 Range Query
MySQL sẽ thực hiện các bước sau đây cho một range query:
- Tìm kiếm B-tree trên secondary index dựa trên cột tìm kiếm (order_date).
- Lấy primary key (order_id) từ secondary index.
- Tra cứu clustered index bằng
order_id
để lấy đầy đủ các hàng. - Trả về kết quả.
Bạn sẽ thấy MySQL sẽ thực hiện 2 lần tra cứu: một lần trên secondary index và một lần trên clustered index (primary key).
Với PostgreSQL, dữ liệu của bảng được lưu trữ trong một vùng gọi là heap (không sắp xếp theo primary key hay bất kỳ cột nào). Các secondary index trong PostgreSQL chỉ lưu trữ giá trị của cột được đánh index và một TID (Tuple Identifier), là con trỏ chỉ đến vị trí thực tế của bản ghi trong heap:
- Tìm kiếm B-tree trên secondary index dựa trên cột tìm kiếm (order_date).
- Lấy TID từ secondary index, ví dụ như lấy được các TID là
(2,3)
,(2,4)
,(3,1)
(vị trí chính xác trong heap). - Truy cập heap để lấy các hàng thực tế.
- Trả về kết quả.
Như vậy PostgreSQL chỉ thực hiện 1 lần tra cứu TID từ secondary index và truy cập trực tiếp dữ liệu trong heap, không cần phải qua primary key.
2. Khi nào nên lựa chọn MySQL hay PostgreSQL?
Dựa trên kịch bản benchmark ở trên mình rút ra những kết luận sau đây, mong rằng có thể giúp bạn dễ dàng đưa ra quyết định nên lựa chọn loại DMBS nào:
- MySQL thích hợp khi ứng dụng có truy vấn đơn giản, nhiều truy vấn đọc hơn ghi: MySQL được tối ưu hóa rất tốt cho các ứng dụng có tỷ lệ đọc cao, chẳng hạn như các trang web hoặc ứng dụng có lưu lượng truy cập lớn với nhiều thao tác lấy dữ liệu (read-heavy), ví dụ: đọc 1 bài blog, tin tức, ...
- PostgreSQL Thích hợp cho các ứng dụng phức tạp hoặc cần ghi nhiều: PostgreSQL thường vượt trội hơn MySQL trong các tình huống cần xử lý nhiều dữ liệu và thao tác ghi, đặc biệt khi ứng dụng cần các thao tác như các range query, aggregation phức tạp, hoặc truy vấn có tính toàn vẹn dữ liệu cao.
3. Kết luận
Khi sử dụng hai hệ quản trị này trong trường hợp có ít dữ liệu, bạn có thể không để ý đến sự khác biệt trong performance giữa chúng, mình cũng thế, với những nhu cầu CRUD cực kì cơ bản, dùng cái nào cũng như nhau. Hy vọng bài viết này của mình giúp bạn có cái nhìn tổng quan về hiệu suất của chúng trong trường hợp phải xử lý truy vấn dữ liệu lớn.
Các bài viết liên quan:
Bài viết liên quan
Giới thiệu Kiến trúc Backend for Frontend (BFF)
Nov 16, 2024 • 10 min read
Flask là gì? Hướng dẫn tạo Ứng dụng Web với Flask
Nov 15, 2024 • 7 min read
Webhook là gì? So sánh Webhook và API
Nov 15, 2024 • 8 min read
Spring Boot là gì? Hướng dẫn Khởi tạo Project Spring Boot với Docker
Nov 14, 2024 • 6 min read
Two-Factor Authentication (2FA) là gì? Vì sao chỉ Mật khẩu thôi là chưa đủ?
Nov 13, 2024 • 7 min read
Test-Driven Development (TDD) là gì? Hướng dẫn thực hành TDD
Nov 13, 2024 • 6 min read