Giới thiệu về Parquet
Chào các bạn! Hôm nay mình muốn chia sẻ với các bạn về Parquet – một định dạng file mã nguồn mở cực kỳ đỉnh cao, được sinh ra để xử lý những bộ dữ liệu khổng lồ. Khác với các kiểu lưu trữ truyền thống theo hàng (row-based), Parquet lưu dữ liệu theo cột (columnar), giúp việc nén và truy vấn dữ liệu nhanh hơn hẳn. Nó được dùng rất nhiều trong các hệ thống như Apache Spark, Apache Hive, và cả loạt công cụ phân tích dữ liệu khác.
Để hiểu Parquet hoạt động thế nào, cùng mình khám phá qua đoạn code đơn giản dưới đây nhé:
import pyarrow as pa
import pyarrow.parquet as pq
import random
# Đặt số hàng, số nhóm hàng và số hàng trong mỗi nhóm
num_rows = 1_000_000 # 1 triệu hàng
num_row_groups = 10 # 10 nhóm
rows_per_group = num_rows // num_row_groups # 100.000 hàng mỗi nhóm
# Tạo dữ liệu mẫu
data = {
"name": ["jonny_" + str(i) for i in range(num_rows)], # Tên kiểu jonny_0, jonny_1...
"score": [i for i in range(num_rows)] # Điểm từ 0 đến 999.999
}
# Chuyển dữ liệu thành bảng PyArrow và ghi vào file Parquet
table = pa.Table.from_pydict(data)
pq.write_table(table, "example.parquet", row_group_size=rows_per_group)
print("✅ Đã tạo file Parquet với 10 nhóm hàng!")
Đoạn code này tạo một file Parquet với 1 triệu hàng, chia thành 10 nhóm, mỗi nhóm 100.000 hàng. Giờ mình sẽ phân tích sâu hơn nhé!
1. Đọc và khám phá metadata của file Parquet
Tiếp theo, mình sẽ dùng code để xem metadata (siêu dữ liệu) của file Parquet này:
# Đọc và hiển thị metadata
parquet_file = "example.parquet"
pf = pq.ParquetFile(parquet_file)
print(pf.metadata)
# Lặp qua từng nhóm hàng và in thông tin cột
for i in range(pf.metadata.num_row_groups):
row_group = pf.metadata.row_group(i)
for j in range(row_group.num_columns):
column = row_group.column(j)
print(f"Cột: {column.path_in_schema}")
print(f" Mã hóa: {column.encodings}")
print(f" Vị trí trang từ điển: {column.dictionary_page_offset}")
print("-" * 50)
Kết quả mẫu:
created_by: parquet-cpp-arrow version 18.1.0
num_columns: 2
num_rows: 1000000
num_row_groups: 10
format_version: 2.6
serialized_size: 2618
Cột: name
Mã hóa: ('PLAIN', 'RLE', 'RLE_DICTIONARY')
Vị trí trang từ điển: 4
--------------------------------------------------
Cột: score
Mã hóa: ('PLAIN', 'RLE', 'RLE_DICTIONARY')
Vị trí trang từ điển: 643237
--------------------------------------------------
... (tiếp tục cho 10 nhóm hàng)
1.1 Tổng quan về metadata
Đầu tiên, ta thấy thông tin tổng quan:
- created_by: File này được tạo bởi thư viện parquet-cpp-arrow phiên bản 18.1.0. Điều này giúp ta biết nguồn gốc file, hữu ích khi cần kiểm tra tương thích hay debug.
- num_columns: 2: Có 2 cột – "name" (tên) và "score" (điểm).
- num_rows: 1000000: Tổng cộng 1 triệu hàng dữ liệu.
- num_row_groups: 10: Dữ liệu chia thành 10 nhóm, mỗi nhóm khoảng 100.000 hàng.
- format_version: 2.6: Định dạng Parquet phiên bản 2.6, khá phổ biến.
- serialized_size: 2618: Metadata chiếm 2618 byte, không tính dữ liệu thật.
1.2 Chi tiết về cột trong mỗi nhóm hàng
Tiếp theo, code in thông tin chi tiết của từng cột trong 10 nhóm hàng:
- Cột "name" và "score": "name" là chuỗi (ví dụ: jonny_0), còn "score" là số (0 đến 999.999).
- Mã hóa: Cả hai cột dùng 3 kiểu:
- PLAIN: Lưu dữ liệu không nén, dùng khi nén không hiệu quả.
- RLE (Run-Length Encoding): Nén các giá trị lặp liên tiếp, ví dụ: lưu "5 lần số 3" thay vì "3, 3, 3, 3, 3".
- RLE_DICTIONARY: Dùng từ điển (danh sách giá trị duy nhất) kết hợp RLE, rất tốt cho dữ liệu lặp không liên tiếp.
- Vị trí trang từ điển: Đây là vị trí byte nơi lưu từ điển của cột. Ví dụ:
- "name" nhóm 1: byte 4.
- "score" nhóm 1: byte 643.237.
1.3 Vị trí từ điển qua các nhóm hàng
Mỗi nhóm hàng có từ điển riêng, và vị trí byte tăng dần:
Quan sát:
- "score" luôn có offset cao hơn "name" trong cùng nhóm, nghĩa là dữ liệu "name" được lưu trước.
- Khoảng cách offset thay đổi, do kích thước dữ liệu mỗi nhóm khác nhau.
Ý nghĩa: Parquet dùng nhiều cách nén linh hoạt để tối ưu lưu trữ, giúp tiết kiệm dung lượng và tăng tốc truy vấn.
1.4 Kết luận
File Parquet này có:
- 1 triệu hàng, chia thành 10 nhóm (100.000 hàng/nhóm).
- 2 cột "name" và "score", nén bằng PLAIN, RLE, RLE_DICTIONARY.
- Mỗi nhóm có từ điển riêng, giúp truy cập dữ liệu nhanh hơn.
2. Hiểu cách Parquet quét dữ liệu siêu nhanh
Giờ mình sẽ cho các bạn thấy cách Parquet quét dữ liệu hiệu quả qua đoạn code sau:
import pyarrow.parquet as pq
parquet_file = "example.parquet"
pf = pq.ParquetFile(parquet_file)
for i in range(pf.metadata.num_row_groups):
row_group = pf.metadata.row_group(i)
print(f"📌 Nhóm hàng {i + 1}/{pf.metadata.num_row_groups}")
for j in range(row_group.num_columns):
column = row_group.column(j)
print(f" 🏷 Cột: {column.path_in_schema}")
print(f" 📉 Min: {column.statistics.min if column.statistics else 'N/A'}")
print(f" 📈 Max: {column.statistics.max if column.statistics else 'N/A'}")
print("-" * 50)
Kết quả:
📌 Nhóm hàng 1/10
🏷 Cột: name
📉 Min: jonny_0
📈 Max: jonny_99999
--------------------------------------------------
🏷 Cột: score
📉 Min: 0
📈 Max: 99999
--------------------------------------------------
📌 Nhóm hàng 2/10
🏷 Cột: name
📉 Min: jonny_100000
📈 Max: jonny_199999
--------------------------------------------------
🏷 Cột: score
📉 Min: 100000
📈 Max: 199999
--------------------------------------------------
... (tiếp tục đến nhóm 10)
Mỗi nhóm hàng lưu giá trị nhỏ nhất (min) và lớn nhất (max) của cột. Ví dụ:
- Nhóm 1: "score" từ 0 đến 99.999.
- Nhóm 2: "score" từ 100.000 đến 199.999.
Lợi ích: Khi bạn lọc dữ liệu (ví dụ: WHERE score > 500000), Parquet chỉ quét các nhóm hàng phù hợp (như nhóm 6-10), bỏ qua những nhóm không cần thiết. Điều này giảm tải I/O, giúp truy vấn nhanh hơn rất nhiều!
3. Parquet tối ưu hóa quét dữ liệu thế nào?
Parquet thông minh nhờ cấu trúc nhóm hàng:
- Chia nhỏ dữ liệu: Thay vì một file lớn, dữ liệu được chia thành các nhóm nhỏ, mỗi nhóm có thống kê min/max riêng.
- Predicate Pushdown: Khi lọc (ví dụ: WHERE score > 700000), Parquet kiểm tra min/max trước. Nếu nhóm nào không thỏa, nó bị bỏ qua luôn.
- Quét hiệu quả: Chỉ đọc nhóm liên quan, giảm lượng dữ liệu cần xử lý.
Ví dụ: Với file 1GB, 10 nhóm hàng:
- Lọc WHERE score > 700000 sẽ bỏ qua nhóm 1-7 (max dưới 700.000), chỉ đọc nhóm 8-10. Nhanh hơn hẳn đúng không?
Kết luận
Parquet là "vũ khí bí mật" cho big data nhờ khả năng nén tốt và quét dữ liệu siêu nhanh. Với thống kê min/max và cơ chế lọc thông minh, nó giúp xử lý dữ liệu lớn hiệu quả hơn bao giờ hết. Nếu bạn làm việc với dữ liệu khủng, hiểu Parquet sẽ giúp bạn tối ưu truy vấn và công việc của mình đấy!
Hy vọng bài viết này giúp bạn hiểu hơn về Parquet nhé! Có gì thắc mắc cứ comment cho mình nha!
Nhận xét
Đăng nhận xét