MaxCompute memperkenalkan tipe yang didefinisikan pengguna (UDT) berdasarkan mesin SQL generasi baru. UDT memungkinkan Anda mereferensikan kelas atau objek dari bahasa pemrograman pihak ketiga dalam pernyataan SQL untuk memanggil metode atau mengambil data.
Kapan menggunakan UDT vs. UDF
Baik UDT maupun fungsi yang didefinisikan pengguna (UDF) memperluas MaxCompute SQL dengan logika kustom. Pilih pendekatan berdasarkan alur kerja Anda:
| Situasi | Pendekatan yang direkomendasikan |
|---|---|
Memanggil metode kelas Java bawaan secara langsung (misalnya, Integer.MAX_VALUE) |
UDT — tidak memerlukan definisi fungsi |
| Menggunakan kembali pustaka pihak ketiga secara langsung dalam ekspresi SQL | UDT — mereferensikan kelas secara inline tanpa pembungkus |
| Menyertakan objek bahasa sumber terkompilasi di berbagai tahap pekerjaan | UDT — mengenkapsulasi status JVM lintas tahap secara otomatis |
| Menerapkan logika bisnis yang dapat digunakan ulang di beberapa proyek | UDF — pendaftaran fungsi eksplisit membuatnya dapat dibagikan |
Kasus penggunaan
-
Memanggil metode pustaka standar Java tanpa mendefinisikan fungsi. Ketika suatu tugas memerlukan metode kelas Java bawaan yang tidak diekspos secara native oleh MaxCompute SQL, UDT memungkinkan Anda memanggilnya langsung dalam suatu ekspresi.
-
Mereferensikan pustaka pihak ketiga secara inline. Alih-alih membungkus fungsi pustaka pihak ketiga di dalam UDF, referensikan kelas tersebut langsung dalam pernyataan SQL.
-
Menyematkan kode sumber terkompilasi dalam SQL. Untuk bahasa seperti Java yang memerlukan kompilasi, UDT memungkinkan Anda mereferensikan objek dan kelas dalam ekspresi SQL tanpa langkah pendaftaran terpisah. Lihat SELECT TRANSFORM untuk alternatif berbasis skrip.
Prasyarat
Sebelum menggunakan UDT, pastikan bahwa:
-
JDK 1.8 tersedia di lingkungan Anda. Versi yang lebih baru dari JDK 1.8 mungkin tidak didukung.
-
Tipe data baru diaktifkan jika Anda menggunakan tipe seperti INT:
set odps.sql.type.system.odps2=true;
Cara kerja
Berbeda dengan UDT di mesin SQL lain (yang biasanya mendefinisikan alias tipe mirip tipe STRUCT), UDT MaxCompute bekerja seperti pernyataan CREATE TYPE — mencakup bidang dan metode, serta Anda mereferensikannya langsung dalam SQL tanpa menulis DDL.
Contoh berikut menggambarkan perbedaannya. Untuk mengakses Integer.MAX_VALUE dari paket java.lang Java:
Menggunakan UDT (referensi langsung):
-- Aktifkan tipe data baru (diperlukan untuk tipe seperti INTEGER).
set odps.sql.type.system.odps2=true;
SELECT java.lang.Integer.MAX_VALUE;
Karena java.lang diimpor otomatis (seperti di Java), ini setara dengan:
set odps.sql.type.system.odps2=true;
SELECT Integer.MAX_VALUE;
Hasil:
+-----------+
| max_value |
+-----------+
| 2147483647 |
+-----------+
Menggunakan UDF (untuk perbandingan):
-
Tulis kelas UDF:
package com.aliyun.odps.test; public class IntegerMaxValue extends com.aliyun.odps.udf.UDF { public Integer evaluate() { return Integer.MAX_VALUE; } } -
Kompilasi, unggah, dan daftarkan:
add jar odps-test.jar; create function integer_max_value as 'com.aliyun.odps.test.IntegerMaxValue' using 'odps-test.jar'; -
Panggil:
select integer_max_value();
UDT menyederhanakan proses ini menjadi satu pernyataan SQL.
Eksekusi multi-tahap
Objek UDT mengalir secara alami melalui tahap MapReduce. Contoh berikut melakukan join dua kolom BigInteger yang dihitung dari sumber data berbeda:
-- Data contoh.
@table1 := select * from values ('100000000000000000000') as t(x);
@table2 := select * from values (100L) as t(y);
-- Buat objek dengan metode baru.
@a := select new java.math.BigInteger(x) x from @table1;
-- Panggil metode statis.
@b := select java.math.BigInteger.valueOf(y) y from @table2;
-- Panggil metode instans melalui join.
select /*+mapjoin(b)*/ x.add(y).toString() from @a a join @b b;
-- Output:
100000000000000000100
Pekerjaan ini berjalan melalui tiga tahap (M1, R2, J3). new java.math.BigInteger(x) dijalankan di M1; java.math.BigInteger.valueOf(y) dan x.add(y).toString() dijalankan di J3 pada proses dan mesin fisik berbeda. UDT mengenkapsulasi hal ini sehingga semua tahap berperilaku seolah-olah berjalan pada Java Virtual Machine (JVM) yang sama.
DAG dari pekerjaan SQL contoh UDT ini terdiri dari tiga tahap: M1 → R2_1 → J3_2. Semua tahap selesai 100%, dengan 1 baris data ditransfer antar tahap.
Kolom x dari variabel a bertipe java.math.BigInteger dan bukan tipe bawaan. Nilai UDT ini dapat diteruskan ke operator lain dan digunakan dalam pengacakan data.
Mereferensikan paket JAR dan mengatur impor Java
Semua SDK untuk kelas Java tersedia untuk UDT secara default. Untuk mereferensikan paket JAR tambahan atau mengatur jalur impor default, gunakan flag sesi berikut.
Mereferensikan paket JAR:
set odps.sql.type.system.odps2=true;
set odps.sql.session.resources=odps-test.jar;
-- JAR harus diunggah ke proyek terlebih dahulu.
select new com.aliyun.odps.test.IntegerMaxValue().evaluate();
Anda dapat menentukan beberapa sumber daya yang dipisahkan koma: set odps.sql.session.resources=foo.sh,bar.txt;
odps.sql.session.resources mengontrol baik UDT maupun SELECT TRANSFORM. JAR yang ditetapkan di sini tersedia untuk kedua fitur tersebut.
Mengatur jalur impor Java default:
set odps.sql.type.system.odps2=true;
set odps.sql.session.resources=odps-test.jar;
set odps.sql.session.java.imports=com.aliyun.odps.test.*;
-- Dengan impor yang ditetapkan, Anda dapat menghilangkan awalan paket lengkap.
select new IntegerMaxValue().evaluate();
odps.sql.session.java.imports menerima classpath (misalnya, java.math.BigInteger) atau wildcard (*). Impor statis tidak didukung.
Operasi yang didukung
UDT mendukung operasi berikut dalam ekspresi SQL:
-
Membuat objek menggunakan
new— contoh:new java.math.BigInteger('123') -
Membuat array menggunakan
newdengan daftar inisialisasi — contoh:new Integer[] { 1, 2, 3 } -
Memanggil metode instans dan metode statis
-
Mengakses bidang instans publik dan bidang statis publik
Hanya metode publik dan bidang publik yang dapat diakses. Semua pengenal (nama paket, nama kelas, nama metode, nama bidang) bersifat case-sensitive. Kelas anonim dan ekspresi lambda tidak didukung. Fungsi yang tidak mengembalikan nilai tidak dapat dipanggil dalam ekspresi.
Tipe data
Pemetaan tipe
Tipe data Java dipetakan ke tipe bawaan MaxCompute. Pemetaan yang sama yang digunakan dalam UDF Java berlaku untuk UDT.
-
Panggil metode tipe bawaan secara langsung:
'123'.length(),1L.hashCode() -
Gunakan UDT dalam fungsi bawaan:
chr(Long.valueOf('100'))—Long.valueOfmengembalikanjava.lang.Long, yang dipetakan ke tipe bawaan BIGINT -
Tipe primitif Java dikonversi secara otomatis ke tipe boxing-nya
Untuk tipe data bawaan baru, tambahkan set odps.sql.type.system.odps2=true; sebelum menjalankan kueri.
Konversi tipe
-
Konversi tipe SQL didukung:
cast(1 as java.lang.Object) -
Konversi gaya Java tidak didukung:
(Object)1 -
Objek UDT dapat dikonversi secara implisit ke objek kelas dasar
-
Objek UDT dapat dikonversi secara eksplisit (cast) ke objek kelas dasar atau subkelas
-
Konversi antara dua tipe yang tidak terkait mengikuti aturan yang sama seperti konversi tipe bawaan. Misalnya, mengonversi
java.lang.Longkejava.lang.Integermenerapkan aturan yang sama seperti mengonversi BIGINT ke INT, yang dapat menyebabkan kehilangan data.
Objek UDT tidak dapat disimpan ke disk dan tidak dapat dimasukkan langsung ke tabel (DDL tidak mendukung UDT sebagai tipe kolom). Jika nilai UDT dapat dikonversi secara implisit ke tipe bawaan, nilai tersebut dapat ditulis ke tabel. BINARY mendukung serialisasi otomatis — arraybyte[]dapat disimpan dan dideserialisasi. Untuk menyimpan UDT secara persisten, konversikan ke BINARY menggunakan metode serialisasi dan deserialisasi. Nilai UDT tidak dapat muncul langsung di output akhir. PanggiltoString()untuk mengonversi UDT apa pun kejava.lang.Stringagar dapat ditampilkan. Untuk mengonversi semua output UDT ke string secara otomatis selama debugging, gunakan: Flag ini hanya berlaku untuk pernyataan PRINT, bukan pernyataan INSERT.
set odps.sql.udt.display.tostring=true;
Generik
UDT mendukung generik Java. Kompilator melakukan inferensi parameter tipe dari argumen:
-- Mengembalikan java.util.List<java.math.BigInteger>
java.util.Arrays.asList(new java.math.BigInteger('1'))
Tentukan parameter tipe secara eksplisit dalam pemanggilan konstruktor atau gunakan java.lang.Object:
-- ArrayList<Object>
new java.util.ArrayList(java.util.Arrays.asList('1', '2'))
-- ArrayList<String>
new java.util.ArrayList<String>(java.util.Arrays.asList('1', '2'))
Semantik operator
Semua operator mengikuti semantik MaxCompute SQL, bukan semantik Java:
-
Konkatenasi string:
String.valueOf(1) + String.valueOf(2)mengembalikan3(kedua string dikonversi implisit ke DOUBLE dan dijumlahkan). Untuk menggabungkan sebagai string, gunakan fungsi konkatenasi string. -
Kesetaraan: Operator
=adalah operator perbandingan SQL, bukan kesetaraan referensi Java. Gunakan metodeequalsuntuk memeriksa apakah dua objek setara.
Kesetaraan objek dan pengacakan data
UDT tidak memiliki definisi yang jelas mengenai kesetaraan objek. Selama pengacakan data, objek dapat ditransmisikan melalui proses dan mesin fisik, menyebabkan satu objek tampak sebagai dua referensi berbeda. Selalu gunakan metode equals — bukan = — untuk membandingkan objek UDT.
Objek dalam baris atau kolom yang sama saling berkorelasi, tetapi korelasi antar baris atau kolom tidak dijamin.
Batasan
UDT tidak dapat digunakan sebagai kunci pengacakan dalam klausa JOIN, GROUP BY, DISTRIBUTE BY, SORT BY, ORDER BY, atau CLUSTER BY. UDT valid dalam ekspresi pada tahap-tahap ini, tetapi tidak dapat menjadi output. Contohnya:
-
group by new java.math.BigInteger('123')— tidak didukung -
group by new java.math.BigInteger('123').hashCode()— didukung, karenahashCode()mengembalikanint.class, yang dipetakan ke tipe bawaan INT
UDF, fungsi agregat yang didefinisikan pengguna (UDAF), dan UDT tidak dapat membaca data dari jenis tabel berikut:
-
Tabel yang mengalami evolusi skema
-
Tabel yang berisi tipe data kompleks
-
Tabel yang berisi tipe data JSON
-
Tabel transaksional
Mengakses sumber daya
Dalam MaxCompute SQL, panggil metode statis com.aliyun.odps.udf.impl.UDTExecutionContext.get() untuk mendapatkan objek ExecutionContext. Gunakan objek ini untuk mengakses konteks eksekusi saat ini, termasuk file dan tabel yang didaftarkan sebagai sumber daya.
Pertimbangan kinerja
Kinerja UDT mirip dengan kinerja UDF. Mesin komputasi yang dioptimalkan memberikan peningkatan tambahan dalam skenario tertentu:
-
Tidak ada overhead serialisasi untuk operasi lokal. Ketika objek UDT digunakan dalam proses yang sama (tidak memerlukan pengacakan data, seperti pada tahap JOIN atau AGGREGATE), serialisasi dan deserialisasi dilewati.
-
Waktu proses berbasis Codegen. UDT dijalankan melalui Codegen dan bukan refleksi, sehingga tidak ada overhead refleksi. Beberapa panggilan UDT di-batch menjadi satu panggilan fungsi — misalnya,
values[x].add(values[y]).divide(java.math.BigInteger.valueOf(2))dipanggil sekali, menghindari overhead antarmuka per panggilan.
Keamanan
UDT tunduk pada model sandbox Java yang sama seperti UDF. Untuk melakukan operasi yang dibatasi oleh sandbox, batalkan isolasi sandbox untuk operasi tersebut atau ajukan permohonan untuk bergabung dalam daftar putih sandbox.
Fitur yang akan ditingkatkan
Fitur berikut direncanakan untuk versi mendatang:
-
Memanggil fungsi yang tidak mengembalikan nilai, dan fungsi yang langsung menggunakan data yang ditransfer (di mana nilai kembali diabaikan, seperti metode
adddari antarmuka List). -
Menggunakan kelas anonim dan ekspresi lambda.
-
Menggunakan UDT sebagai kunci pengacakan.
-
Mendukung lebih banyak bahasa pemrograman, seperti Python.
Apa selanjutnya
-
UDF Java — tabel pemetaan tipe data dan referensi implementasi UDF
-
Tipe data bawaan baru — tipe yang memerlukan
set odps.sql.type.system.odps2=true; -
SELECT TRANSFORM — menyematkan skrip dalam pernyataan SQL
-
COLLECT_SET dan fungsi agregat lainnya — gunakan dengan UDT untuk menerapkan perilaku fungsi agregat dan fungsi bernilai tabel
-
Sandbox Java — model sandbox dan aplikasi daftar putih