Chuyển đến nội dung chính

Cách sử dụng hiệu quả PySpark UDF và Pandas UDF

Tìm hiểu cách tạo, tối ưu hóa và sử dụng PySpark UDF, bao gồm Pandas UDF, để xử lý các chuyển đổi dữ liệu tùy chỉnh hiệu quả và cải thiện hiệu năng Spark.
Đã cập nhật 5 thg 10, 2026  · 9 phút đọc

Khám phá cùng AI

ChatGPTClaudePerplexity

User-Defined Functions (UDF) trong PySpark mang đến cho lập trình viên Python một cách mạnh mẽ để xử lý các tác vụ đặc thù mà các hàm dựng sẵn của Spark không thể đáp ứng. Nếu bạn là kỹ sư dữ liệu, nhà phân tích, hoặc nhà khoa học dữ liệu thành thạo Python, nắm vững khái niệm UDF sẽ giúp bạn giải quyết hiệu quả những thách thức dữ liệu phức tạp trong thực tế. 

Hướng dẫn này sẽ đưa bạn qua các khái niệm về PySpark UDF, cách triển khai thực tế, cùng các thực hành tốt về tối ưu hóa, kiểm thử, gỡ lỗi và các mẫu sử dụng nâng cao. Kết thúc bài, bạn sẽ tự tin viết, tối ưu và triển khai UDF hiệu quả ở quy mô lớn.

Nếu bạn mới làm quen với PySpark, tôi khuyên bạn nên xem trước hướng dẫn Bắt đầu với PySpark vì nội dung ở đây thuộc các khái niệm Spark nâng cao.

PySpark UDF là gì?

PySpark UDF là các hàm Python tùy chỉnh được tích hợp vào khuôn khổ phân tán của Spark để thao tác với dữ liệu lưu trong Spark DataFrame. Khác với các hàm dựng sẵn của Spark, UDF cho phép áp dụng logic tùy chỉnh, phức tạp ở mức hàng hoặc cột. 

Cheat Sheet PySpark của chúng tôi bao quát mọi điều bạn cần biết về Spark DataFrame, giúp bạn hiểu UDF của Spark dễ dàng hơn. 

Khi nào bạn nên dùng PySpark UDF?

Hãy dùng UDF khi:

  • Bạn cần logic không thể biểu đạt bằng các hàm dựng sẵn của Spark.
  • Việc chuyển đổi của bạn bao gồm các thao tác phức tạp thuần Python (ví dụ: xử lý regex, logic NLP tùy chỉnh).
  • Bạn chấp nhận đánh đổi hiệu năng để lấy tính linh hoạt, nhất là khi tạo mẫu hoặc với tập dữ liệu nhỏ đến trung bình.

Tránh dùng UDF khi:

  • Chức năng tương đương có trong pyspark.sql.functions; các hàm gốc của Spark nhanh hơn, được tối ưu và có thể được đẩy xuống engine thực thi.
  • Bạn làm việc với tập dữ liệu lớn nơi hiệu năng là rất quan trọng. UDF tạo thêm chi phí tuần tự hóa và làm Spark mất khả năng tối ưu kế hoạch thực thi.
  • Bạn có thể biểu đạt logic bằng biểu thức SQL, các hàm dựng sẵn của Spark SQL, hoặc Pandas UDF (cho phép vector hóa).

Ứng dụng chiến lược trong kỹ thuật dữ liệu

Dưới đây là các trường hợp sử dụng chính của PySpark UDF:

  • Chuyển đổi dữ liệu phức tạp như phân tích cú pháp văn bản nâng cao, trích xuất dữ liệu, hoặc thao tác chuỗi.
  • Tích hợp với thư viện Python của bên thứ ba, bao gồm các framework machine learning phổ biến như TensorFlow và XGBoost.
  • Kết nối các hệ thống kế thừa và hỗ trợ tiến hóa schema mượt mà khi cấu trúc dữ liệu thay đổi.

UDF đơn giản hóa các tác vụ kỹ thuật dữ liệu lộn xộn trong thực tế, giúp đội ngũ xử lý linh hoạt và hiệu quả nhiều yêu cầu đa dạng. 

Giờ hãy khám phá cách bạn có thể triển khai PySpark UDF. 

Triển khai PySpark UDF

Phần này nêu cách định nghĩa và triển khai PySpark UDF trong thực tiễn.

Các phương thức khai báo UDF tiêu chuẩn

Có ba cách tiếp cận phổ biến để khai báo UDF trong PySpark:

1. UDF dùng lambda: Định nghĩa nhanh trực tiếp trong truy vấn DataFrame; phù hợp cho thao tác đơn giản.

UDF dựa trên lambda (Python UDF cơ bản) phù hợp cho các chuyển đổi nhanh và đơn giản. Tránh dùng cho các tác vụ quy mô lớn nơi hiệu năng quan trọng.

Ví dụ: 

from pyspark.sql.functions import udf
from pyspark.sql.types import StringType

uppercase = udf(lambda x: x.upper() if x else None, StringType())
df = spark.createDataFrame([("Ada",), (None,)], ["name"])
df.withColumn("upper_name", uppercase("name")).show()

2. Hàm Python có decorator: Được chú thích rõ bằng @pyspark.sql.functions.udf, hỗ trợ tái sử dụng và tăng tính dễ đọc.

Ví dụ:

from pyspark.sql import functions as F
from pyspark.sql.types import IntegerType

@F.udf(returnType=IntegerType())
def str_length(s):
    return len(s) if s else 0

df.withColumn("name_length", str_length("name")).show()

3. UDF đăng ký trong SQL: Đăng ký trực tiếp trong ngữ cảnh Spark SQL, cho phép dùng trong truy vấn SQL.

from pyspark.sql.types import StringType

def reverse_string(s):
    return s[::-1] if s else ""

spark.udf.register("reverse_udf", reverse_string, StringType())

df.createOrReplaceTempView("people")
spark.sql("""SELECT name, reverse_udf(name) AS reversed FROM people""").show()

Mỗi phương thức có điểm mạnh yếu: UDF lambda ngắn gọn nhưng hạn chế, trong khi hàm có chú thích giúp tăng khả năng đọc, bảo trì và bám sát thực hành tốt.

Pandas UDF cho phép thao tác vector hóa trên các lô Arrow. Chúng thường nhanh hơn UDF thông thường và tích hợp tốt hơn với engine thực thi của Spark.

Pandas UDF dạng vô hướng (element-wise, giống map)

Phù hợp nhất cho các chuyển đổi theo hàng nhanh trên tập dữ liệu lớn. Ví dụ: 

from pyspark.sql.functions import pandas_udf
from pyspark.sql.types import IntegerType
import pandas as pd

@pandas_udf(IntegerType())
def pandas_strlen(s: pd.Series) -> pd.Series:
    return s.str.len()

df.withColumn("name_len", pandas_strlen("name")).show()

Pandas UDF kiểu Grouped Map

Phù hợp nhất cho logic tùy chỉnh theo nhóm, tương tự groupby().apply() trong Pandas.  

from pyspark.sql import functions as F
from pyspark.sql.types import StructType, StructField, StringType, DoubleType
import pandas as pd

schema = StructType([
    StructField("group", StringType()),
    StructField("avg_val", DoubleType())
])

@pandas_udf(schema, F.PandasUDFType.GROUPED_MAP)
def group_avg(pdf: pd.DataFrame) -> pd.DataFrame:
    return pd.DataFrame({
        "group": [pdf["group"].iloc[0]],
        "avg_val": [pdf["value"].mean()]
    })

df.groupBy("group").apply(group_avg).show()

Pandas UDF dạng tổng hợp (Aggregate)

Thực hiện tổng hợp theo nhóm, nhanh hơn grouped map. Ví dụ: 

from pyspark.sql.functions import pandas_udf
from pyspark.sql.types import DoubleType
import pandas as pd

@pandas_udf(DoubleType(), functionType="grouped_agg")
def mean_udf(v: pd.Series) -> float:
    return v.mean()

df.groupBy("category").agg(mean_udf("value").alias("mean_value")).show()

Pandas Iterator UDF

Pandas Iterator UDF phù hợp cho tập dữ liệu lớn cần xử lý tiết kiệm bộ nhớ (theo lô). Ví dụ: 

from pyspark.sql.functions import pandas_udf
from pyspark.sql.types import IntegerType
from typing import Iterator
import pandas as pd

@pandas_udf(IntegerType(), functionType="iterator")
def batch_sum(it: Iterator[pd.Series]) -> Iterator[pd.Series]:
    for batch in it:
        yield batch + 1

df.withColumn("incremented", batch_sum("id")).show()

Xử lý kiểu dữ liệu và an toàn với giá trị null

Kiểu dữ liệu và giá trị null thường gây thách thức cho PySpark UDF. PySpark áp dụng kiểm tra kiểu nghiêm ngặt, dễ dẫn đến chuyển đổi kiểu ngầm hoặc lỗi lúc chạy. Hơn nữa, Spark truyền trực tiếp giá trị null vào UDF, có thể gây sập nếu không xử lý tường minh.

Đảm bảo UDF vững chắc với các chiến lược sau:

  • Chỉ định rõ kiểu trả về.
  • Thêm kiểm tra null (ví dụ: câu lệnh điều kiện) trong hàm Python của bạn.
  • Áp dụng thực hành mã phòng thủ; các kiểm tra null đơn giản giúp tránh lỗi lúc chạy khó chịu.

Tối ưu hiệu năng UDF

Hiệu năng thường là “gót chân Achilles” của UDF tiêu chuẩn do mô hình thực thi theo từng hàng. Tận dụng UDF vector hóa và các công cụ tối ưu của Spark sẽ cải thiện đáng kể thời gian chạy.

UDF vector hóa với tích hợp Pandas

Pandas UDF đưa cách tiếp cận vector hóa vào UDF trong PySpark bằng cách truyền các lô dữ liệu dưới dạng Pandas Series cho hàm Python. Thiết kế này cải thiện hiệu năng đáng kể nhờ giảm chi phí tuần tự hóa so với UDF xử lý theo hàng.

Được hỗ trợ bởi Apache Arrow cho truyền dữ liệu “zero-copy” giữa JVM và tiến trình Python, Pandas UDF cho phép thực thi hiệu quả ở quy mô lớn. Chúng đặc biệt hiệu quả cho tính toán chuyên sâu và thao tác chuỗi phức tạp trên hàng triệu bản ghi. 

Chúng tôi trình bày chi tiết hơn về thao tác dữ liệu với PySpark trong khóa học Làm sạch Dữ liệu với PySpark. 

Ngoài ra, Pandas UDF cho phép tích hợp liền mạch với hệ sinh thái khoa học dữ liệu Python rộng lớn, tận dụng các công cụ và quy trình quen thuộc.

Loại UDF

Kiểu thực thi

Tốc độ

Tối ưu hóa Spark

Phù hợp nhất cho

Ghi chú

UDF tiêu chuẩn

Theo hàng (Python)

Chậm

Không tối ưu

Logic đơn giản, tập dữ liệu nhỏ

Dễ viết nhưng tốn kém

Pandas Scalar UDF

Vector hóa (theo cột)

Nhanh

Dựa trên Arrow

Phép toán số, biến đổi chuỗi

Ưu tiên dùng thay UDF tiêu chuẩn khi có thể

Pandas Grouped Map UDF

Theo nhóm (Pandas DataFrame)

Trung bình–Nhanh

Dựa trên Arrow

Chuyển đổi theo nhóm

Phải tự định nghĩa schema đầu ra

Pandas Aggregate UDF

Theo nhóm (Series đầu vào → vô hướng đầu ra)

Nhanh

Được tối ưu

Tổng hợp như mean, sum

Đơn giản hơn grouped map

Pandas Iterator UDF

Bộ lặp theo lô (streaming)

Nhanh

Được tối ưu

Xử lý các lô lớn an toàn

Tiết kiệm bộ nhớ

Kỹ thuật tối ưu hóa Arrow

Định dạng bộ nhớ dạng cột của Apache Arrow cho phép truyền dữ liệu hiệu quả, “zero-copy” giữa Spark JVM và tiến trình Python. Bằng cách bật Arrow (spark.sql.execution.arrow.pyspark.enabled=true) trong cấu hình Spark, dữ liệu sẽ di chuyển nhanh giữa JVM và Python, tăng tốc đáng kể việc thực thi UDF.

Tối ưu kế hoạch thực thi

Tối ưu job PySpark đòi hỏi hiểu cách tác động đến bộ tối ưu Catalyst của Spark. Các chiến lược nâng cao bao gồm đẩy điều kiện lọc xuống nguồn (predicate pushdown), cắt tỉa cột (column pruning), và dùng gợi ý broadcast join để cải thiện lập kế hoạch và hiệu quả thực thi truy vấn.

Để tối đa hiệu năng, cần thu hẹp phạm vi thực thi UDF và ưu tiên dùng các hàm dựng sẵn của Spark SQL khi có thể. Việc sử dụng cache có chiến lược và thiết kế kế hoạch cẩn trọng còn giúp tăng tốc độ thực thi và tận dụng tài nguyên tốt hơn.

Tối ưu hiệu năng là một trong những câu hỏi chính bạn có thể gặp trong phỏng vấn PySpark. Tìm hiểu cách trả lời câu này và nhiều câu hỏi Spark khác trong Top 36 Câu hỏi và Trả lời Phỏng vấn PySpark cho 2025 trên blog. 

Mẫu nâng cao và phản-mẫu

Hiểu các mẫu dùng đúng và sai giúp đảm bảo triển khai UDF ổn định và hiệu quả.

Triển khai UDF có trạng thái

Các UDF có trạng thái và không xác định (nondeterministic) đặt ra những thách thức riêng trong PySpark. Các hàm này cho kết quả phụ thuộc vào trạng thái bên ngoài hoặc điều kiện thay đổi, như biến môi trường, thời gian hệ thống, hoặc ngữ cảnh phiên làm việc.

Dù đôi khi cần thiết—ví dụ tạo timestamp, theo dõi phiên người dùng, hoặc đưa yếu tố ngẫu nhiên—chúng có thể làm phức tạp việc gỡ lỗi, tái lập và tối ưu hóa.

Triển khai UDF có trạng thái đòi hỏi mẫu thiết kế cẩn trọng: ghi chú rõ hành vi, cô lập tác dụng phụ, và bổ sung log chi tiết để hỗ trợ xử lý sự cố và đảm bảo nhất quán qua các lần chạy.

Khi dùng hợp lý, chúng mở khóa năng lực mạnh mẽ, nhưng để duy trì pipeline dữ liệu tin cậy cần quản trị kỷ luật. Khóa học Nền tảng Dữ liệu Lớn với PySpark của chúng tôi trình bày chi tiết hơn cách xử lý big data trong PySpark. 

Các phản-mẫu phổ biến

Các phản-mẫu phổ biến khi dùng UDF có thể làm suy giảm đáng kể hiệu năng PySpark:

  • Xử lý theo hàng thay vì theo lô: Áp dụng UDF lên từng hàng thay vì dùng cách tiếp cận vector hóa như Pandas UDF dẫn tới chậm đáng kể.
  • Lồng các thao tác DataFrame bên trong UDF: Nhúng truy vấn DataFrame trong UDF gây tính toán quá mức và cản trở khả năng tối ưu kế hoạch của Spark.
  • Đăng ký UDF lặp lại trực tiếp trong truy vấn: Định nghĩa và đăng ký UDF nhiều lần bên trong truy vấn thêm chi phí không cần thiết; tốt hơn là khai báo một lần và tái sử dụng giữa các job.
  • Lạm dụng logic Python tùy chỉnh cho thao tác đơn giản: Các tác vụ như lọc cơ bản, số học hay chuyển đổi đơn giản nên ưu tiên dùng hàm dựng sẵn tối ưu cao của Spark thay vì UDF tùy chỉnh.

Tránh các sai lầm này sẽ đảm bảo hiệu năng tốt hơn, giúp Catalyst dễ tối ưu hơn và mã PySpark dễ bảo trì hơn.

Gỡ lỗi và kiểm thử PySpark UDF

Kiểm thử và gỡ lỗi UDF đảm bảo độ tin cậy và độ vững chắc trong môi trường sản xuất.

Mẫu xử lý ngoại lệ

Triển khai cơ chế bắt lỗi có cấu trúc trong UDF là điều thiết yếu để xây dựng pipeline PySpark bền bỉ và dễ bảo trì. Sử dụng khối try-except trong UDF để xử lý nhẹ nhàng các tình huống bất ngờ lúc chạy, như giá trị null, lệch kiểu, hoặc lỗi chia cho 0.

Xử lý ngoại lệ vững chắc giúp ổn định pipeline trước dữ liệu bất thường và đơn giản hóa gỡ lỗi bằng cách hiển thị thông điệp lỗi rõ ràng, có thể hành động. Việc bắt và ghi log lỗi đúng cách làm hành vi UDF minh bạch hơn, đẩy nhanh xử lý sự cố và cải thiện độ tin cậy tổng thể của pipeline.

Khung kiểm thử đơn vị

Dùng lớp kiểm thử tích hợp của PySpark, pyspark.testing.utils.ReusedPySparkTestCase, cùng các khung như pytest, để viết kiểm thử đơn vị tin cậy cho UDF của bạn. Cấu trúc bài kiểm thử rõ ràng và tập trung đảm bảo tính đúng đắn, ổn định và khả năng bảo trì của logic UDF theo thời gian.

Thực hành tốt khi kiểm thử UDF gồm bao phủ cả trường hợp điển hình và biên, xác thực đầu ra với kết quả đã biết, và cô lập hành vi UDF khỏi các phụ thuộc bên ngoài. Bài kiểm thử được thiết kế tốt không chỉ chống thoái lui mà còn đơn giản hóa phát triển và tái cấu trúc về sau.

Sự phát triển và hướng đi tương lai

Hệ sinh thái PySpark tiếp tục phát triển nhanh chóng, đưa vào nhiều khả năng mới nâng cao UDF hơn nữa.

Tích hợp Unity Catalog

Những phát triển gần đây đã tích hợp việc đăng ký UDF vào Unity Catalog, giúp tinh gọn cách UDF được quản lý, khám phá và quản trị ở quy mô lớn. Unity Catalog cho phép kiểm soát tập trung vòng đời UDF, gồm đăng ký, tạo phiên bản và kiểm soát truy cập, tất cả đều quan trọng cho môi trường doanh nghiệp.

Sự tích hợp này tăng cường quản trị, thực thi chính sách bảo mật nhất quán, và cải thiện khả năng khám phá giữa các nhóm, giúp UDF dễ tái sử dụng, kiểm toán và quản lý trong hệ sinh thái dữ liệu lớn, phức tạp.

UDF tăng tốc bằng GPU

Các framework như RAPIDS Accelerator cho phép offload GPU cho các tác vụ UDF tính toán nặng trong PySpark, mang lại cải thiện hiệu năng đột phá. Bằng cách chuyển các phép nặng như phân tích số, suy luận học sâu và mô hình hóa dữ liệu quy mô lớn sang GPU, RAPIDS có thể rút ngắn thời gian chạy từ hàng giờ xuống còn vài phút với khối lượng công việc phù hợp.

Tăng tốc GPU đặc biệt hữu ích cho các kịch bản liên quan đến tập dữ liệu khổng lồ, phép tính vector hóa phức tạp và pipeline machine learning, mở rộng mạnh mẽ hiệu năng và khả năng mở rộng của PySpark cho các tác vụ kỹ thuật dữ liệu hiện đại. Khóa học Machine Learning với PySpark của chúng tôi đi sâu hơn vào các khái niệm này. 

Kết luận

PySpark UDF là công cụ mạnh để mở rộng khả năng của Spark, cho phép đội ngũ xử lý các tác vụ xử lý dữ liệu phức tạp, tùy chỉnh vượt ngoài phạm vi các hàm dựng sẵn. Khi áp dụng đúng, chúng mở khóa tính linh hoạt và đổi mới trong pipeline dữ liệu quy mô lớn. 

Tuy nhiên, tối ưu hiệu năng UDF đòi hỏi chú ý cẩn trọng, tránh các sai lầm phổ biến như thao tác theo hàng, quản lý ngoại lệ mượt mà, và tận dụng các kỹ thuật như vector hóa Pandas UDF với tích hợp Arrow.

Những tiến bộ mới nổi như tăng tốc GPU qua framework RAPIDS đang mở rộng hơn nữa khả năng của quy trình làm việc dựa trên UDF. Dù bạn đang chuyển đổi dữ liệu thực tế lộn xộn hay nhúng phân tích nâng cao vào hệ thống sản xuất, thành thạo thực hành tốt về UDF là điều thiết yếu để xây dựng pipeline dữ liệu nhanh, hiệu quả và tin cậy. 

Tìm hiểu những chi tiết “nhọc nhằn” mà các nhà khoa học dữ liệu dành 70–80% thời gian cho chúng: xử lý dữ liệu và kỹ thuật đặc trưng, trong khóa học Feature Engineering với PySpark của chúng tôi.

PySpark UDF FAQs

Khi nào tôi nên dùng PySpark UDF thay vì hàm dựng sẵn?

Bạn chỉ nên dùng PySpark UDF khi chuyển đổi của bạn không thể thực hiện bằng các hàm dựng sẵn của Spark. Các hàm dựng sẵn được tối ưu và chạy nhanh hơn UDF vì chúng hoạt động trực tiếp trên JVM mà không có chi phí tuần tự hóa.

Vì sao Pandas UDF nhanh hơn UDF Python thông thường trong PySpark?

Pandas UDF (UDF được vector hóa) nhanh hơn vì chúng dùng Apache Arrow để tuần tự hóa dữ liệu hiệu quả và xử lý dữ liệu theo lô thay vì theo từng hàng, giúp giảm chi phí di chuyển dữ liệu giữa JVM và trình thông dịch Python.

Tôi có luôn cần chỉ định kiểu trả về cho UDF trong PySpark không?

Có. PySpark yêu cầu chỉ định tường minh kiểu dữ liệu trả về khi định nghĩa UDF. Yêu cầu này đảm bảo tuần tự hóa đúng giữa Java và Python và ngăn lỗi lúc chạy.

Làm thế nào để bật Apache Arrow trong ứng dụng PySpark của tôi?

Bạn có thể bật Apache Arrow bằng cách đặt cấu hình sau trước khi chạy bất kỳ UDF nào:

spark.conf.set("spark.sql.execution.arrow.pyspark.enabled", "true")

Cách tốt nhất để xử lý giá trị null trong PySpark UDF là gì?

Luôn thêm kiểm tra điều kiện với giá trị None (null) trong UDF để tránh ngoại lệ. Ví dụ: if product_name is None: return None.


Derrick Mwiti's photo
Author
Derrick Mwiti
Chủ đề
PySpark
Python
Kỹ thuật Dữ liệu

Top DataCamp Courses

Khóa học

Nhập môn PySpark

4 giờ
34.8K
Thành thạo PySpark để xử lý big data dễ dàng—học cách xử lý, truy vấn và tối ưu hóa các bộ dữ liệu khổng lồ cho phân tích mạnh mẽ!
Xem chi tiếtRight Arrow
Bắt Đầu Khóa Học
Xem thêmRight Arrow
Liên quan

blog

Claude Opus 4.6: Tính năng, Điểm chuẩn, Bài kiểm tra thực hành và hơn thế nữa

Mô hình mới nhất của Anthropic dẫn đầu ở mã hóa tác tử và lập luận phức tạp. Thêm vào đó, nó có cửa sổ ngữ cảnh 1M.
Matt Crabtree's photo

Matt Crabtree

10 phút

Xem ThêmXem Thêm