Polars lazy xử lý kết quả crawl: đọc kế hoạch trước khi chạy
Polars lazy xử lý kết quả crawl là cách viết truy vấn trên nhiều tệp dữ liệu crawl dưới dạng kế hoạch, để Polars tối ưu trước khi đọc. Thay vì nạp cả bảy ngày dữ liệu vào bộ nhớ rồi lọc, Polars chỉ đọc đúng tệp của ngày cần, đúng các cột cần, và áp điều kiện ngay lúc quét. Người dùng còn có thể đọc kế hoạch đó trước khi chạy. ProxyVN chạy Polars 2.1.0 ngày 03/11/2026 trên bảy ngày dữ liệu crawl giả lập, mỗi ngày 100.000 dòng. Lab so kế hoạch đã tối ưu và chưa tối ưu, so lazy với eager, và thử hai cái bẫy làm truy vấn lỗi hoặc chậm. Bài ghi lại kế hoạch thật và con số thật.
Polars lazy xử lý kết quả crawl: mục tiêu và phạm vi không lặp
Phần này chốt bài toán, đầu ra và ranh giới.
Bài toán
Một nhóm lưu dữ liệu crawl dạng Parquet, mỗi ngày một thư mục. Mỗi dòng có URL, tên miền, giá, mã trạng thái, thời điểm tải và cả nội dung HTML rút gọn. Họ cần tính số trang và giá trung vị theo tên miền cho một ngày cụ thể. Đọc cả thư mục vào bộ nhớ mất vài trăm megabyte chỉ để lấy vài con số.
Đầu ra nghiệm thu
Đầu ra gồm truy vấn lazy trên nhiều tệp và kế hoạch đã tối ưu cho thấy chỉ đọc tệp và cột cần. Kèm theo là so sánh thời gian và bộ nhớ với cách đọc eager, và hai ca dễ sai: so cột ngày với chuỗi, dùng hàm Python trong điều kiện lọc. Kết quả của lazy và eager phải trùng nhau.
Ranh giới với DuckDB
DuckDB cũng đọc được nhiều tệp Parquet với tối ưu tương tự, bằng SQL. Bài này không so hai công cụ. Trọng tâm là kế hoạch truy vấn của Polars: đọc nó thế nào, nhận ra pushdown thế nào, và những gì làm pushdown thất bại.
Phiên bản đã khóa
Lab dùng polars 2.1.0, pyarrow 26.0.1 để sinh dữ liệu, Python 3.12 trên Windows. Kế hoạch in ra bằng explain có định dạng phụ thuộc phiên bản. Bản Polars khác có thể in khác chữ nhưng cùng ý nghĩa.
Ai nên đọc
Người phân tích dữ liệu crawl bằng pandas và thấy đọc tệp ngày càng chậm. Người đã dùng Polars nhưng chủ yếu ở chế độ eager. Người cần chứng minh một truy vấn không đọc thừa dữ liệu trước khi đưa vào lịch chạy.
Vì sao giữ cột html
Nhiều pipeline giữ một phần HTML gốc cùng dữ liệu đã trích để tiện kiểm lại khi parser sai. Cột này nặng nhất bảng và hiếm khi cần trong phân tích thường ngày. Lab cố ý thêm nó để thấy rõ lợi ích của việc không đọc cột không cần. Dữ liệu thật thường có cột tương tự như mô tả sản phẩm hay danh sách ảnh.
Dữ liệu lab
Bảy thư mục ngay bằng 2026-11-01 tới 2026-11-07, mỗi thư mục một tệp Parquet 100.000 dòng, nén zstd, row group 20.000 dòng. Có sáu cột: url, domain, price, status, html, fetched_at. Cột html là chuỗi khoảng 400 ký tự, mô phỏng phần nội dung nặng thường bị giữ lại cùng dữ liệu crawl. Tổng thư mục 7.444 KB trên đĩa.
Dùng lazy scan projection/predicate trên nhiều tệp crawl với Polars lazy xử lý kết quả crawl
Phần này giải thích các khái niệm.
Lazy và eager
Theo trang Usage trong hướng dẫn lazy của Polars, ở chế độ lazy, Polars không chạy từng bước ngay mà dựng một kế hoạch truy vấn, tối ưu nó, rồi mới chạy khi gọi collect. Chế độ eager chạy từng lệnh ngay khi gọi. Cùng một chuỗi phép tính, lazy có cơ hội bỏ qua phần không cần.
scan và read
scan_parquet trả về LazyFrame, chưa đọc dữ liệu. read_parquet đọc ngay thành DataFrame. Mọi tối ưu trong bài chỉ có với scan. Dùng read rồi gọi lazy trên kết quả thì dữ liệu đã nằm trong bộ nhớ, không còn gì để tiết kiệm ở bước đọc.
Collect là lúc dữ liệu được đọc
Mọi lệnh trước collect chỉ thêm bước vào kế hoạch. collect mới đọc tệp, chạy kế hoạch, trả DataFrame. Gọi collect nhiều lần trên cùng LazyFrame sẽ đọc tệp nhiều lần. Nếu cần nhiều kết quả từ cùng một lần đọc, Polars có hàm collect_all để chạy nhiều kế hoạch cùng lúc và chia sẻ phần đọc chung.
Projection pushdown
Projection pushdown nghĩa là chỉ đọc những cột truy vấn thực sự dùng. Nếu truy vấn chỉ cần domain, price, status và cột phân vùng, Polars không đọc url, html, fetched_at. Với Parquet dạng cột, bỏ qua cột nghĩa là không đọc phần đó trên đĩa.
Predicate pushdown
Predicate pushdown nghĩa là đưa điều kiện lọc xuống tận bước quét. Polars dùng điều kiện để bỏ qua tệp không khớp phân vùng và bỏ qua row group không khớp thống kê, rồi lọc từng dòng ngay khi đọc. Dữ liệu không khớp không bao giờ được dựng thành bảng.
Thống kê row group
Bên trong mỗi tệp, Parquet lưu min và max của từng cột cho từng row group. Polars có thể dùng chúng để bỏ qua row group không khớp điều kiện. Trong lab, cột status có cả 200 lẫn 404 ở mọi row group, nên điều kiện status không bỏ qua được row group nào. Điều kiện trên cột được sắp xếp như thời điểm tải thì có.
Phân vùng kiểu hive
Với hive_partitioning bằng True, Polars đọc tên thư mục ngay bằng giá trị thành một cột. Điều kiện trên cột này cho phép bỏ qua cả thư mục. Đây là cách bỏ qua dữ liệu rẻ nhất, không cần mở tệp.
Biểu thức thay vì hàm
Polars viết điều kiện và phép tính bằng biểu thức như pl.col("status") bằng 200, không bằng hàm Python. Biểu thức mô tả việc cần làm, Polars quyết định làm thế nào. Nhờ biết biểu thức làm gì, Polars mới đẩy được điều kiện xuống bước quét và dùng được thống kê của tệp.
explain
LazyFrame có phương thức explain in ra kế hoạch dạng cây. Mặc định in kế hoạch đã tối ưu. Truyền optimized bằng False in kế hoạch thô, đúng thứ tự bạn viết. So hai bản là cách thấy tối ưu đã làm gì.
Viết truy vấn lazy và đọc kế hoạch tối ưu của Polars lazy xử lý kết quả crawl
Phần này viết truy vấn và in kế hoạch.
Truy vấn
lf = pl.scan_parquet("kho/**/*.parquet", hive_partitioning=True)
q = (lf.filter((pl.col("ngay") == pl.lit("2026-11-06").str.to_date())
& (pl.col("status") == 200))
.group_by("domain")
.agg(pl.len().alias("so_trang"),
pl.col("price").median().alias("gia_trung_vi"))
.sort("domain"))
Truy vấn lọc ngày 06/11 và mã 200, gom theo tên miền, đếm số trang và tính giá trung vị. Chưa có dữ liệu nào được đọc khi dòng này chạy xong.
Kế hoạch đã tối ưu

Kế hoạch đã tối ưu
SORT BY [col("domain")]
AGGREGATE [len(), median(price)] BY [col("domain")]
simple π 2/2 ["domain", "price"]
Parquet SCAN [kho/ngay=2026-11-06/part.parquet]
PROJECT 4/7 COLUMNS
SELECTION: (col("ngay") == "2026-11-06") & (col("status") == 200)
ESTIMATED ROWS: 100000
Đọc kế hoạch từ dưới lên
Kế hoạch đọc từ dưới lên. Dòng Parquet SCAN chỉ liệt kê một tệp: thư mục ngày 06/11. Sáu thư mục khác đã bị loại nhờ điều kiện trên cột ngay. Dòng PROJECT 4/7 COLUMNS cho biết chỉ đọc bốn trong bảy cột. Dòng SELECTION cho biết cả hai điều kiện lọc đã được đẩy xuống bước quét.
ESTIMATED ROWS
Dòng ESTIMATED ROWS ghi 100.000, là số dòng của tệp được quét, chưa tính điều kiện status. Polars ước tính trước khi đọc, nên chỉ biết số dòng từ siêu dữ liệu tệp. Kết quả thật sau lọc là 98.000. Con số ước tính giúp so nhanh khối lượng giữa các kế hoạch.
Bốn cột nào
Bốn cột cần là domain, price, status và ngay. status cần cho điều kiện lọc, ngay cho phân vùng, domain và price cho phép gom và tính. url, html và fetched_at không được đọc. Riêng html chiếm phần lớn dung lượng, bỏ qua nó là tiết kiệm lớn nhất.
Tệp nào bị loại
Kế hoạch chỉ ghi tệp được quét, không liệt kê tệp bị loại. Biết có bảy tệp trong thư mục và chỉ một tệp xuất hiện trong SCAN là đủ để biết sáu tệp đã bị bỏ qua. Với thư mục hàng trăm ngày, kiểm số tệp trong SCAN là cách nhanh để xác nhận điều kiện ngày hoạt động.
Sau khi lọc chỉ còn hai cột
Dòng simple π 2/2 phía trên cho biết sau khi lọc, chỉ hai cột domain và price được chuyển lên bước gom. status và ngay đã xong nhiệm vụ ở bước lọc, bị bỏ ngay.
Kế hoạch chưa tối ưu
SORT BY [col("domain")]
AGGREGATE ... BY [col("domain")]
FILTER col("status") == 200
FROM
FILTER col("ngay") == "2026-11-06"
FROM
Parquet SCAN [kho/ngay=2026-11-01/part.parquet, ... 6 other sources]
PROJECT */7 COLUMNS
ESTIMATED ROWS: 700000
Kế hoạch thô quét cả bảy tệp, đọc mọi cột, ước tính 700.000 dòng, rồi mới lọc. Đây là đúng những gì bạn viết, theo đúng thứ tự. Tối ưu đã biến nó thành quét một tệp, bốn cột.
Những tối ưu khác
Ngoài projection và predicate pushdown, Polars còn gộp các phép lọc liên tiếp, bỏ phép tính không ai dùng, đơn giản hóa biểu thức. Tài liệu lazy của Polars liệt kê các tối ưu này. Lab chỉ kiểm hai loại pushdown vì chúng ảnh hưởng nhiều nhất tới lượng dữ liệu đọc.
Tối ưu không đổi kết quả
Hai kế hoạch cho cùng kết quả về mặt logic. Tối ưu chỉ đổi cách thực hiện. Đây là lý do có thể yên tâm viết truy vấn theo thứ tự dễ đọc, và để Polars sắp xếp lại cho hiệu quả.
Chạy lazy và so với eager trên cùng dữ liệu trong Polars lazy xử lý kết quả crawl
Phần này chạy truy vấn bằng hai cách và so.
Truy vấn tương đương bằng SQL
Polars còn cho viết truy vấn bằng SQL trên LazyFrame. Câu SQL được dịch sang cùng kế hoạch, hưởng cùng tối ưu. Người quen SQL có thể dùng cách này, rồi vẫn in explain để kiểm. Lab chỉ dùng API biểu thức.
Kết quả
domain |
so_trang |
gia_trung_vi |
|---|---|---|
shop-0.vn |
32.667 |
537.526 |
shop-1.vn |
32.667 |
537.488 |
shop-2.vn |
32.666 |
537.495 |
Ba tên miền, tổng 98.000 trang mã 200 trong ngày 06/11, khớp với dữ liệu sinh ra: 100.000 dòng, cứ 50 dòng có một dòng 404.
Lazy
collect trên truy vấn lazy mất 9 mili giây.
Kiểm kết quả trùng
Lab so kết quả lazy với eager bằng equals, trả True. Khi viết một truy vấn lazy mới, so với cách eager trên một mẫu nhỏ là cách kiểm rẻ rằng tối ưu không làm sai logic. Với dữ liệu lớn, so trên một ngày thay vì cả tháng.
Eager
read_parquet cả thư mục rồi lọc, gom, sắp xếp: mất 34 mili giây. Bảng đọc vào bộ nhớ ước tính 317 MB. Kết quả trùng khớp với lazy, kiểm bằng equals trả True.
Bộ nhớ là khác biệt lớn nhất

Bộ nhớ lazy và eager
Thời gian chênh khoảng bốn lần, nhưng bộ nhớ chênh nhiều hơn nhiều. Eager nạp 317 MB, phần lớn là cột html của bảy ngày. Lazy chỉ dựng bốn cột của một ngày, rồi lọc ngay khi đọc. Với dữ liệu crawl nhiều tháng, eager sẽ hết bộ nhớ trước khi chậm.
Khi eager vẫn hợp lý
Với dữ liệu nhỏ và cần nhiều phép tính khám phá liên tiếp, đọc eager một lần rồi làm việc trên DataFrame trong bộ nhớ tiện hơn. Mỗi lần collect lazy sẽ đọc lại tệp. Nguyên tắc là: lọc và chọn cột bằng lazy, collect một tập vừa đủ, rồi khám phá bằng eager trên tập đó.
Dữ liệu trên đĩa nhỏ, trong bộ nhớ lớn
Thư mục chỉ 7.444 KB trên đĩa nhờ nén zstd và cột html lặp lại. Nhưng khi đọc vào bộ nhớ, dữ liệu được giải nén thành 317 MB. Kích thước trên đĩa không cho biết kích thước trong bộ nhớ. Đây là lý do pushdown quan trọng ngay cả khi tệp có vẻ nhỏ.
Median là phép tính nặng
Giá trung vị cần sắp xếp hoặc chọn giá trị giữa, nặng hơn đếm hay trung bình. Với một ngày 100.000 dòng, vẫn rất nhanh. Với hàng chục triệu dòng, median có thể là bước chậm nhất. Engine streaming xử lý median theo cách riêng, lab chưa đo với dữ liệu lớn.
Streaming
collect với engine streaming cho cùng kết quả trong 4 mili giây. Engine streaming xử lý dữ liệu theo từng khúc, hợp với dữ liệu lớn hơn bộ nhớ. Với dữ liệu nhỏ như lab, chênh lệch với engine mặc định không có ý nghĩa thống kê.
Giá trung vị ba tên miền gần nhau
Ba giá trung vị chỉ chênh vài chục đồng vì dữ liệu giả sinh theo cùng công thức. Dữ liệu thật sẽ khác xa. Con số trong bảng chỉ dùng để kiểm lazy và eager cho cùng kết quả.
Một lần đo
Mọi thời gian trong phần này là một lần chạy trên một máy, sau khi hệ điều hành đã đệm tệp. Lần chạy đầu tiên sau khi khởi động máy có thể chậm hơn vì phải đọc đĩa. Con số dùng để so tương đối, không phải để dự đoán thời gian trên dữ liệu thật.
Phân biệt query plan với cách đọc thẳng và các bẫy làm hỏng pushdown của Polars lazy xử lý kết quả crawl
Phần này thử hai cái bẫy.
Kiểm schema trước khi viết điều kiện
collect_schema trả schema của LazyFrame mà không đọc dữ liệu. Chạy nó một lần khi bắt đầu làm việc với thư mục mới giúp biết kiểu của từng cột, kể cả cột phân vùng. Mất vài mili giây, tránh được lỗi so sánh kiểu và ép kiểu ngầm.
Cột ngày được suy thành Date
Polars đọc cột ngay từ tên thư mục và suy kiểu là Date, vì giá trị có dạng năm tháng ngày. collect_schema xác nhận kiểu Date. Điều này tiện cho so sánh theo khoảng ngày, nhưng có hệ quả khi viết điều kiện.
Lỗi xảy ra lúc collect
Viết điều kiện sai kiểu không báo lỗi ngay, vì LazyFrame chưa chạy. Lỗi chỉ xuất hiện khi collect. Trong mã dài, khoảng cách giữa chỗ viết điều kiện và chỗ collect có thể xa, làm khó tìm nguyên nhân. Thêm một lần collect trên mẫu nhỏ ngay sau khi viết truy vấn giúp phát hiện sớm.
Bẫy 1: so ngày với chuỗi
Viết điều kiện pl.col("ngay") bằng chuỗi 2026-11-06 làm truy vấn báo InvalidOperationError: không thể so date với giá trị chuỗi. Lỗi xảy ra lúc collect, không phải lúc viết điều kiện.
Cách sửa là chuyển chuỗi sang ngày bằng str.to_date, như truy vấn chính làm, hoặc dùng đối tượng date của Python. Lỗi này rõ ràng, không làm sai kết quả. Nhưng nó cho thấy kiểu cột phân vùng phụ thuộc cách Polars suy, và cần kiểm schema trước khi viết điều kiện.
Đọc kỹ thông điệp lỗi
Thông điệp lỗi của Polars gợi ý cách sửa: tạo giá trị date của Python hoặc so với một cột thời gian. Polars thường in gợi ý khá cụ thể. Đọc hết thông điệp trước khi tìm trên mạng thường nhanh hơn.
Bẫy 2: hàm Python trong điều kiện lọc

Hàm Python chặn tối ưu
Lab viết điều kiện status bằng 200 bằng map_elements với hàm lambda của Python, thay vì biểu thức gốc của Polars. Polars phát cảnh báo PolarsInefficientMapWarning, gợi ý thay bằng biểu thức pl.col("status") bằng 200.
Kế hoạch với hàm Python
Kế hoạch vẫn chỉ quét một tệp, vì điều kiện ngày vẫn được đẩy xuống. Dòng SELECTION ghi điều kiện ngày cộng với col("status").python_udf(). Hàm Python được gọi trên từng giá trị status của tệp ngày 06/11. Kết quả 98.000 dòng, đúng.
Khi nào buộc phải dùng hàm Python
Có những phép xử lý không có biểu thức gốc, như gọi một thư viện phân tích URL đặc biệt. Khi đó, nên lọc bằng biểu thức gốc trước để giảm số dòng, rồi mới áp hàm Python lên phần còn lại. Hàm Python chạy trên 1.000 dòng sau lọc rẻ hơn nhiều so với chạy trên 100.000 dòng trước lọc.
Vì sao hàm Python chậm
Biểu thức gốc của Polars chạy trên cả cột bằng mã biên dịch, song song. Hàm Python được gọi từng giá trị, qua trình thông dịch, khóa toàn cục của Python. Với 100.000 giá trị, chênh lệch nhỏ. Với hàng chục triệu, chênh lệch thành nhiều lần. Polars cũng không dùng được thống kê row group với hàm Python, vì không biết hàm làm gì.
Kiểm cảnh báo trong lịch chạy
Cảnh báo của Polars in ra luồng lỗi, dễ bị bỏ qua trong lịch chạy tự động. Nên bật chế độ coi cảnh báo là lỗi khi kiểm thử truy vấn mới, để không đưa truy vấn có hàm Python thừa vào chạy thật.
Thứ tự viết không quan trọng, cách viết thì có
Lab viết filter hàm Python trước, filter ngày sau. Polars vẫn gộp và đẩy điều kiện ngày xuống. Thứ tự các bước không quyết định hiệu quả. Cách viết từng điều kiện thì có: biểu thức gốc tối ưu được, hàm Python thì không.
So kế hoạch giữa hai phiên bản truy vấn
Khi sửa một truy vấn, in explain của bản cũ và bản mới, so dòng SCAN và PROJECT. Nếu bản mới đọc nhiều tệp hay nhiều cột hơn mà không có lý do, đó là dấu hiệu sửa đổi đã phá pushdown. Lưu explain cùng truy vấn trong kho mã giúp so được theo thời gian.
Đọc kế hoạch thành thói quen
Trước khi đưa truy vấn mới vào lịch chạy, in explain và kiểm ba điều: Parquet SCAN liệt kê đúng tệp cần, PROJECT chỉ có cột cần, SELECTION chứa điều kiện lọc. Nếu thiếu một trong ba, truy vấn đang đọc thừa.
Đầu ra nghiệm thu: truy vấn lazy, kế hoạch và so sánh với eager của Polars lazy xử lý kết quả crawl
Phần cuối gom kết quả thành bộ bàn giao.
Bộ bàn giao
Bộ bàn giao gồm lab.py sinh bảy ngày dữ liệu, chạy truy vấn lazy và eager, in hai kế hoạch, thử hai bẫy, chạy streaming. Người nhận chạy lại, phải thấy kế hoạch tối ưu quét một tệp, bốn cột, lazy và eager cho cùng kết quả, so chuỗi báo lỗi, map_elements có cảnh báo.
Đưa explain vào kiểm thử
Có thể viết kiểm thử tự động gọi explain và kiểm chuỗi kết quả có chứa PROJECT với số cột mong đợi, và SCAN chỉ có một tệp khi lọc một ngày. Kiểm thử này bắt được khi ai đó sửa truy vấn làm mất pushdown. Định dạng explain có thể đổi giữa các phiên bản, nên kiểm thử cần cập nhật khi nâng Polars.
Tiêu chí đạt
Truy vấn dùng scan, không dùng read. Kế hoạch tối ưu cho thấy pushdown trên phân vùng, cột và điều kiện. Kết quả trùng với cách đọc eager. Điều kiện viết bằng biểu thức gốc, kiểu khớp với schema.
Phân vùng phục vụ truy vấn
Phân vùng theo ngày giúp truy vấn theo ngày. Nếu phần lớn truy vấn lọc theo tên miền, phân vùng theo ngày không giúp gì cho điều kiện tên miền, Polars phải mở mọi tệp của các ngày được chọn. Chọn cột phân vùng theo cách dữ liệu được truy vấn, không theo cách nó được ghi.
Quy tắc viết truy vấn lazy cho dữ liệu crawl
Luôn bắt đầu bằng scan. Kiểm schema, nhất là kiểu cột phân vùng. Viết điều kiện bằng biểu thức gốc. Không chọn cột nặng như html nếu không cần. In explain trước khi chạy thật. Dùng engine streaming khi dữ liệu lớn hơn bộ nhớ.
Khi nào cần proxy với Polars lazy xử lý kết quả crawl
Polars đọc tệp trên đĩa, không cần mạng, không cần proxy. Nếu đọc Parquet trực tiếp từ kho đối tượng trên đám mây, Polars kết nối qua mạng, và mạng công ty có thể bắt đi qua proxy. Đó là cấu hình hạ tầng, không liên quan tới dữ liệu crawl.
Proxy liên quan tới bước crawl trước đó. Với website được phép thu thập nhưng hiển thị khác theo vùng, có lúc cần IP tại Việt Nam để lấy đúng giá. Khi đó anh em có thể mua proxy tĩnh Việt Nam tại trang chủ ProxyVN. Proxy không làm truy vấn nhanh hơn và không sửa kiểu cột sai. Vai trò hợp lệ của proxy được giải thích ở bài Web Scraping proxy là gì.
Cột vùng trong dữ liệu
Nếu dữ liệu crawl qua nhiều vùng, nên có cột vùng tải và lọc theo nó trong truy vấn. Giá trung vị trộn hai vùng không có ý nghĩa. Cột vùng có ít giá trị, nén tốt, và có thể dùng làm cột phân vùng nếu truy vấn hay lọc theo nó. Không ghi chuỗi proxy có mật khẩu vào dữ liệu, xem cách viết chuỗi proxy ở bài định dạng proxy.
Thu kết quả nhỏ, không thu dữ liệu lớn
Truy vấn trong lab trả ba dòng. Mọi phép gom diễn ra trong Polars, chỉ kết quả nhỏ được đưa về Python. Nếu viết truy vấn trả cả 98.000 dòng rồi gom bằng vòng lặp Python, lợi ích của lazy mất phần lớn. Đẩy càng nhiều phép tính vào biểu thức càng tốt.
Lưu truy vấn thành hàm
Truy vấn hay dùng nên viết thành hàm nhận LazyFrame và tham số như ngày, trả LazyFrame. Hàm không gọi collect, để người gọi tự quyết định thời điểm chạy và có thể ghép thêm bước. Cách viết này giữ được toàn bộ cơ hội tối ưu khi ghép nhiều hàm với nhau.
Những gì lab chưa thử
Lab chưa thử đọc từ kho đối tượng, chưa thử dữ liệu lớn hơn bộ nhớ thật, chưa so với DuckDB hay pandas có pyarrow. Lab chưa đo số row group bị bỏ qua bên trong tệp, chỉ thấy số tệp bị bỏ qua. Thời gian là một lần chạy, một máy.
Checklist Polars lazy xử lý kết quả crawl

Checklist
- scan_parquet với hive_partitioning, không read_parquet.
- Kiểm collect_schema, nhất là kiểu cột phân vùng.
- So cột Date với giá trị ngày, không với chuỗi.
- Điều kiện bằng biểu thức gốc, tránh map_elements.
- Không chọn cột nặng nếu không cần.
- In explain, kiểm SCAN, PROJECT, SELECTION.
- So kết quả lazy với một lần eager khi viết truy vấn mới.
- Dùng engine streaming cho dữ liệu lớn.
Câu hỏi thường gặp về Polars lazy xử lý kết quả crawl
Lazy khác eager thế nào?
Lazy dựng kế hoạch, tối ưu rồi mới chạy khi collect. Eager chạy từng lệnh ngay. Lab thấy lazy chỉ đọc một tệp và bốn cột.
Làm sao biết truy vấn có đọc thừa không?
In explain. Kiểm Parquet SCAN liệt kê đúng tệp, PROJECT đúng số cột, SELECTION có điều kiện lọc.
Vì sao so cột ngày với chuỗi bị lỗi?
Vì Polars suy cột phân vùng ngay là kiểu Date. Lab nhận InvalidOperationError khi so với chuỗi.
map_elements có làm sai kết quả không?
Không. Lab vẫn ra 98.000 dòng, nhưng Polars cảnh báo chậm và không tối ưu được điều kiện đó.
Eager tốn bộ nhớ bao nhiêu?
Lab đọc bảy ngày vào bộ nhớ ước tính 317 MB, trong khi thư mục trên đĩa chỉ 7.444 KB.
Polars có cần proxy không?
Không. Chỉ bước crawl tạo ra dữ liệu mới có thể cần.
Polars lazy xử lý kết quả crawl và những điều cần nhớ
Polars lazy xử lý kết quả crawl cho phép viết truy vấn tự nhiên rồi để Polars bỏ qua phần không cần, và quan trọng hơn, cho phép kiểm điều đó trước khi chạy. Lab trên Polars 2.1.0 cho thấy kế hoạch tối ưu quét một trong bảy tệp, bốn trong bảy cột, trong khi đọc eager nạp 317 MB. Hai bẫy cần tránh là so cột ngày với chuỗi và dùng hàm Python trong điều kiện. Cần IP tĩnh cho bước crawl trước khi chạy Polars lazy xử lý kết quả crawl, liên hệ Proxy.vn - Nhà cung cấp dịch vụ proxy chất lượng hàng đầu Việt Nam.