Bu konuda, Java'da kullanıcı tanımlı bir toplama işlevi (UDAF) nasıl yazılacağı açıklanmaktadır.
UDAF Kod Yapısı
Java UDAF'i IntelliJ IDEA ile Maven kullanarak veya MaxCompute Studio'ya Giriş yoluyla yazabilirsiniz. Kod aşağıdaki bileşenleri içermelidir:
-
Java paketi: İsteğe bağlı.
Tanımladığınız Java sınıflarını paketleyerek bunların daha kolay bulunmasını ve yeniden kullanılmasını sağlayabilirsiniz.
-
Gerekli sınıflar ve ek açıklamalar: Zorunlu.
com.aliyun.odps.udf.Aggregatorsınıfını içeri aktarmanız ve@Resolveek açıklamasını (com.aliyun.odps.udf.annotation.Resolve) kullanmanız gerekir.com.aliyun.odps.udf.UDFExceptionsınıfı isteğe bağlıdır ve hata yönetimi için kullanılabilir. Diğer UDAF ile ilgili sınıfları veya karmaşık veri türlerini kullanmanız gerekiyorsa, gerekli sınıfları MaxCompute UDF'lerine Genel Bakış bölümünde açıklandığı şekilde içeri aktarın. -
@Resolveek açıklaması: Zorunlu.Biçimi
@Resolve(<imza>)şeklindedir; buradasignature(imza), giriş parametrelerinin ve dönüş değerinin veri türlerini tanımlayan işlev imzasıdır. Bir UDAF'in işlev imzası yansıma (reflection) yoluyla belirlenemez ve yalnızca@Resolveek açıklaması kullanılarak alınabilir; örneğin@Resolve("smallint->varchar(10)").@Resolveek açıklaması hakkında daha fazla bilgi için bkz. @Resolve Ek Açıklaması. -
Özel Java sınıfı: Zorunlu.
Bu sınıf, UDAF kodunun yapısal birimidir. İş mantığınızı uygulayan değişkenleri ve yöntemleri tanımlar.
-
Java sınıfı yöntemleri: Zorunlu.
Java sınıfınız
com.aliyun.odps.udf.Aggregatorsınıfından türemeli ve aşağıdaki yöntemleri uygulamalıdır.import com.aliyun.odps.udf.ContextFunction; import com.aliyun.odps.udf.ExecutionContext; import com.aliyun.odps.udf.UDFException; public abstract class Aggregator implements ContextFunction { // The initialization method. @Override public void setup(ExecutionContext ctx) throws UDFException { } // The termination method. @Override public void close() throws UDFException { } // Creates an aggregation buffer. abstract public Writable newBuffer(); // The iterate method. // buffer is an aggregation buffer that holds intermediate, summarized data. In map tasks, it aggregates data for a group, and this method is executed once for each row. // Writable[] represents a row of data, which refers to the input columns in the code. For example, writable[0] refers to the first column, and writable[1] refers to the second column. // args are the parameters specified when calling the UDAF in SQL. The args array itself cannot be null, but its elements can be null, which indicates that the corresponding input data is null. abstract public void iterate(Writable buffer, Writable[] args) throws UDFException; // The terminate method. abstract public Writable terminate(Writable buffer) throws UDFException; // The merge method. abstract public void merge(Writable buffer, Writable partial) throws UDFException; }iterate,mergeveterminateyöntemleri, bir UDAF'in ana mantığını uygulayan üç temel yöntemdir. Ayrıca özel bir yazılabilir arabellek (writable buffer) uygulamanız gerekir.Yazılabilir bir arabellek, bellek içi nesneleri bir bayt dizisine (veya başka bir veri aktarım protokolüne) dönüştürerek diskte kalıcılığı ve ağ üzerinden iletimi kolaylaştırır. MaxCompute, toplama işlevlerini işlemek için dağıtık hesaplamayı kullandığından, verileri çalışanlar arasında aktarmak için verileri seri hale getirmesi (serialize) ve seri hale getirilmiş veriyi çözümlemesi (deserialize) gerekir.
Bir Java UDAF yazarken Java türlerini veya Java Writable türlerini kullanabilirsiniz. MaxCompute tarafından desteklenen veri türleri ile Java veri türleri arasındaki eşlemeler hakkında daha fazla bilgi için bkz. Veri Türleri.
Aşağıdaki kodda bir UDAF örneği verilmiştir.
// Package the defined Java class in org.alidata.odps.udaf.examples.
package org.alidata.odps.udaf.examples;
// Import the required base classes.
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import com.aliyun.odps.io.DoubleWritable;
import com.aliyun.odps.io.Writable;
import com.aliyun.odps.udf.Aggregator;
import com.aliyun.odps.udf.UDFException;
import com.aliyun.odps.udf.annotation.Resolve;
// Define the custom Java class.
// Specify the @Resolve annotation.
@Resolve("double->double")
public class AggrAvg extends Aggregator {
// Implement the methods for the Java class.
private static class AvgBuffer implements Writable {
private double sum = 0;
private long count = 0;
@Override
public void write(DataOutput out) throws IOException {
out.writeDouble(sum);
out.writeLong(count);
}
@Override
public void readFields(DataInput in) throws IOException {
sum = in.readDouble();
count = in.readLong();
}
}
private DoubleWritable ret = new DoubleWritable();
@Override
public Writable newBuffer() {
return new AvgBuffer();
}
@Override
public void iterate(Writable buffer, Writable[] args) throws UDFException {
DoubleWritable arg = (DoubleWritable) args[0];
AvgBuffer buf = (AvgBuffer) buffer;
if (arg != null) {
buf.count += 1;
buf.sum += arg.get();
}
}
@Override
public Writable terminate(Writable buffer) throws UDFException {
AvgBuffer buf = (AvgBuffer) buffer;
if (buf.count == 0) {
ret.set(0);
} else {
ret.set(buf.sum / buf.count);
}
return ret;
}
@Override
public void merge(Writable buffer, Writable partial) throws UDFException {
AvgBuffer buf = (AvgBuffer) buffer;
AvgBuffer p = (AvgBuffer) partial;
buf.sum += p.sum;
buf.count += p.count;
}
}
Yukarıdaki UDAF kodunda, iterate ve merge yöntemlerindeki arabellek yeniden kullanılabilir. Bu arabellek, uygulamanıza göre giriş satırlarını arabellekte toplar.
Sınırlamalar
-
UDF'leri Kullanarak İnternet'e Erişim
Varsayılan olarak MaxCompute, UDF'ler kullanarak İnternet'e erişmenize izin vermez. UDF'ler aracılığıyla İnternet'e erişmek istiyorsanız, iş gereksinimlerinize göre ağ bağlantısı başvuru formunu doldurun ve başvuruyu gönderin. MaxCompute teknik destek ekibi, ağ bağlantısını etkinleştirmek için sizinle hızla iletişime geçecektir. Ağ bağlantısı başvuru formunun nasıl doldurulacağı hakkında daha fazla bilgi için bkz. Ağ Bağlantı Süreci.
-
UDF'leri Kullanarak VPC'ye Erişim
Varsayılan olarak MaxCompute, UDF'ler kullanarak VPC'lerdeki kaynaklara erişmenize izin vermez. UDF'leri kullanarak bir VPC'deki kaynaklara erişmek için MaxCompute ile VPC arasında bir ağ bağlantısı kurmanız gerekir. İlgili işlemler hakkında daha fazla bilgi için bkz. UDF'den VPC Kaynaklarına Erişim.
-
UDF'ler, UDAF'ler veya UDTF'ler Kullanarak Tablo Verilerini Okuma
Aşağıdaki tablo türlerinden veri okumak için UDF'leri, UDAF'leri veya UDTF'leri kullanamazsınız:
Şema evriminin uygulandığı tablo
Karmaşık veri türleri içeren tablo
JSON veri türleri içeren tablo
İşlemsel tablo
Kullanım Notları
Bir Java UDAF yazarken aşağıdaki noktalara dikkat edin:
Farklı UDAF'lerin JAR dosyalarında aynı ada sahip ancak farklı mantığa sahip sınıfların bulunması, beklenmeyen sonuçlara veya derleme hatalarına yol açabilir. Örneğin, UDAF1 ve UDAF2'nin sırasıyla udaf1.jar ve udaf2.jar kaynak JAR dosyalarına karşılık geldiğini varsayalım. Her iki JAR dosyası da
com.aliyun.UserFunction.classadında bir sınıf içeriyor ancak uygulamaları farklıysa, aynı SQL ifadesinde hem UDAF1 hem de UDAF2 çağrıldığında MaxCompute sınıflardan birini öngörülemez bir şekilde yükler.Bir Java UDAF'te giriş parametreleri ve dönüş değeri, ilkel türler (primitive types) yerine String ve Long gibi nesne türleri olmalıdır.
SQL'deki NULL değerleri Java NULL ile temsil edilir. Java ilkel türleri SQL'deki NULL değerlerini temsil edemez ve kullanılamaz.
@Resolve Ek Açıklaması
@Resolve ek açıklamasının biçimi aşağıdaki gibidir.
@Resolve(<signature>)
signature (imza), giriş parametrelerinin ve dönüş değerinin veri türlerini tanımlayan bir dizedir. Bir UDAF yürütüldüğünde, giriş parametrelerinin ve dönüş değerinin türleri işlev imzasında belirtilen türlerle eşleşmelidir. Anlamsal ayrıştırma sırasında sistem, işlev imzasına uymayan kullanımları kontrol eder ve bir tür uyumsuzluğu oluşursa hata bildirir. Belirli biçim aşağıdaki gibidir.
'arg_type_list -> type'
Açıklama:
-
arg_type_list: Giriş parametrelerinin veri türlerini temsil eder. Birden fazla giriş parametresi virgül (,) ile ayrılarak belirtilebilir. Desteklenen veri türleri BIGINT, STRING, DOUBLE, BOOLEAN, DATETIME, DECIMAL, FLOAT, BINARY, DATE, DECIMAL(hassasiyet,ölçek), CHAR, VARCHAR, karmaşık veri türleri (ARRAY, MAP, STRUCT) ve iç içe geçmiş karmaşık veri türleridir.arg_type_listayrıca yıldız işareti (*) veya boş bir dize ('') destekler.arg_type_listbir yıldız işareti (*) ise, işlevin herhangi bir sayıda giriş parametresini kabul ettiğini gösterir.arg_type_listboş bir dize ('') ise, işlevin hiçbir giriş parametresi olmadığını gösterir.
Resolve ek açıklamasının genişletilmiş sözdizimi hakkında daha fazla bilgi için bkz. UDAF'ler ve UDTF'ler İçin Dinamik Parametreler.
type: Dönüş değerinin veri türünü temsil eder. Bir UDAF yalnızca bir sütun döndürür. Desteklenen veri türleri BIGINT, STRING, DOUBLE, BOOLEAN, DATETIME, DECIMAL, FLOAT, BINARY, DATE, DECIMAL(hassasiyet,ölçek), karmaşık veri türleri (ARRAY, MAP, STRUCT) ve iç içe geçmiş karmaşık veri türlerini içerir.
UDAF kodu yazarken MaxCompute projenizin veri türü sürümüne göre uygun veri türlerini seçebilirsiniz. Veri türü sürümleri ve her sürüm tarafından desteklenen veri türleri hakkında daha fazla bilgi için bkz. Veri Türü Sürümleri.
Aşağıda geçerli @Resolve ek açıklamalarına örnekler verilmiştir.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Veri Türleri
MaxCompute tarafından desteklenen veri türleri veri türü sürümüne göre değişir. MaxCompute 2.0'dan başlayarak, ARRAY, MAP ve STRUCT gibi karmaşık türler dahil olmak üzere ek veri türleri kullanılabilir. Daha fazla bilgi için bkz. Veri Türü Sürümleri.
Java UDAF'iniz, MaxCompute'daki türlerle eşleşen veri türlerini kullanmalıdır. Aşağıdaki tabloda bu eşlemeler açıklanmaktadır.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Bir UDAF'in giriş parametreleri veya dönüş değeri, MaxCompute projeniz MaxCompute V2.0 veri türü sürümünü kullanıyorsa yalnızca Java Writable Türünü kullanabilir.
Kullanım
Java UDAF'i geliştirme sürecini izleyerek geliştirdikten sonra, bunu MaxCompute SQL'de aşağıdaki gibi çağırabilirsiniz:
Bir MaxCompute projesinde UDF kullanma: Yöntemi, yerleşik işlevleri kullanmaya benzer. Kullanıcı tanımlı bir işlevi, yerleşik bir işlevi kullandığınız şekilde kullanabilirsiniz.
Projeler arası UDF kullanma: B Projesindeki bir UDF'yi A Projesinde kullanma. Aşağıdaki ifade bir örnek gösterir:
select B:udf_in_other_project(arg0, arg1) as res from table_t;. Projeler arası paylaşım hakkında daha fazla bilgi için bkz. Paketlere Dayalı Projeler Arası Kaynak Erişimi.
MaxCompute Studio kullanarak bir Java UDAF'in nasıl geliştirileceği ve çağrılacağına ilişkin tam bir örnek için bkz. Örnek.
Örnek
Bu örnek, ortalama bir değeri hesaplayan AggrAvg adlı bir UDAF geliştirmek için MaxCompute Studio'nun nasıl kullanılacağını gösterir. Aşağıdaki şekil mantığı görselleştirir.

-
Giriş verisi dilimleme: MaxCompute, bir çalışanın verimli bir şekilde işleyebileceği dilimlere giriş verilerini bölmek için MapReduce işleme iş akışını takip eder.
Dilim boyutunu
odps.stage.mapper.split.sizeparametresini kullanarak yapılandırmanız gerekir. Ortalama hesaplamanın ilk aşaması: Her çalışan, kendi dilimindeki veri kayıtlarının sayısını sayar ve toplamlarını hesaplar. Her dilimden elde edilen sayım ve toplam, bir ara sonuç olarak kabul edilir.
Ortalama hesaplamanın ikinci aşaması: İlk aşamadaki her dilimden gelen ara sonuçlar toplanır.
Son çıktı:
r.sum/r.counttüm giriş verilerinin ortalamasıdır.
Aşağıdaki adımlarda Java UDAF'in nasıl geliştirileceği ve çağrılacağı açıklanmaktadır:
-
Ortamı hazırlayın.
MaxCompute Studio'da bir UDF geliştirmeden ve hata ayıklamadan önce MaxCompute Studio'yu yüklemeniz ve bir MaxCompute projesine bağlamanız gerekir. Daha fazla bilgi için aşağıdaki konulara bakın:
-
UDAF kodunu yazın
Project gezgininde, modülün kaynak kodu dizinine () sağ tıklayın ve öğelerini seçin.
-
Create new MaxCompute java class iletişim kutusunda UDAF seçeneğine tıklayın, Name alanına bir ad girin ve Enter tuşuna basın. Bu örnek için Java sınıfının adını AggrAvg olarak belirleyin.
Name, MaxCompute Java sınıfının adıdır. Bir paket oluşturmadıysanız, adı packagename.classname formatında girin. Bir paket otomatik olarak oluşturulur.
-
Kod düzenleyicide aşağıdaki UDAF kodunu yapıştırın.
import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import com.aliyun.odps.io.DoubleWritable; import com.aliyun.odps.io.Writable; import com.aliyun.odps.udf.Aggregator; import com.aliyun.odps.udf.UDFException; import com.aliyun.odps.udf.annotation.Resolve; @Resolve("double->double") public class AggrAvg extends Aggregator { private static class AvgBuffer implements Writable { private double sum = 0; private long count = 0; @Override public void write(DataOutput out) throws IOException { out.writeDouble(sum); out.writeLong(count); } @Override public void readFields(DataInput in) throws IOException { sum = in.readDouble(); count = in.readLong(); } } private DoubleWritable ret = new DoubleWritable(); @Override public Writable newBuffer() { return new AvgBuffer(); } @Override public void iterate(Writable buffer, Writable[] args) throws UDFException { DoubleWritable arg = (DoubleWritable) args[0]; AvgBuffer buf = (AvgBuffer) buffer; if (arg != null) { buf.count += 1; buf.sum += arg.get(); } } @Override public Writable terminate(Writable buffer) throws UDFException { AvgBuffer buf = (AvgBuffer) buffer; if (buf.count == 0) { ret.set(0); } else { ret.set(buf.sum / buf.count); } return ret; } @Override public void merge(Writable buffer, Writable partial) throws UDFException { AvgBuffer buf = (AvgBuffer) buffer; AvgBuffer p = (AvgBuffer) partial; buf.sum += p.sum; buf.count += p.count; } }
-
UDAF'i yerel olarak hata ayıklayın
Daha fazla hata ayıklama işlemi için bkz. UDF'leri Yerel Olarak Çalıştırarak Hata Ayıklama.
Projenizdeki AggrAvg.java dosyasına sağ tıklayın ve Run 'AggrAvg.main()' seçeneğini belirleyin. Görünen Run/Debug Configurations iletişim kutusunda aşağıdaki parametreleri yapılandırın: MaxCompute project için
local, MaxCompute table içinkmeans_in, Table columns içindim1,dim2, Download Record limit için100ve Data Column Separator için virgül. Ardından OK düğmesine tıklayın.NotÇalıştırma parametreleri için referans olarak şekilde gösterilen verileri kullanabilirsiniz.
-
Oluşturduğunuz UDAF'i bir JAR dosyasında paketleyin, JAR dosyasını bir MaxCompute projesine yükleyin ve işlevi kaydedin. Örneğin, işlevin adı
user_udafolsun.Paketleme işlemleri hakkında daha fazla bilgi için bkz. Adımlar.
IntelliJ IDEA'da, UDAF'i içeren Java dosyasına sağ tıklayın ve Deploy to server... seçeneğini belirleyin. Package a jar, submit resource and register function iletişim kutusunda MaxCompute project, Resource name, Main class (UDAF sınıf adını girin) ve Function name seçeneklerini yapılandırın. Force update if already exists onay kutusunu seçin ve dağıtımı tamamlamak için OK düğmesine tıklayın.
-
MaxCompute Studio'nun sol gezinme bölmesinde Project Explorer seçeneğine tıklayın, hedef MaxCompute projesine sağ tıklayın, MaxCompute istemcisini başlatın ve yeni oluşturulan UDAF'i çağırmak için bir SQL komutu yürütün.
Hedef tablo
my_tableiçin şema ve verilerin aşağıdaki gibi olduğunu varsayalım.+------------+------------+ | col0 | col1 | +------------+------------+ | 1.2 | 2.0 | | 1.6 | 2.1 | +------------+------------+UDAF'i çağırmak için aşağıdaki SQL ifadesini çalıştırın.
select user_udaf(col0) as c0 from my_table;Aşağıdaki sonuç döndürülür.
+----+ | c0 | +----+ | 1.4| +----+