AWS Türkçe Blog

Amazon Athena için En İyi 10 Performans Ayarlama İpuçları

Orijinal makale: Link (Mert Hocanin ve Pathik Shah)

Bu blog yazısı, EXPLAIN ANALYZE kullanımı, güncellenmiş sıkıştırma, ORDER BY ve JOIN ipuçları, bölüm indekslemeyi kullanma, güncellenmiş istatistikler (performans iyileştirmeleri ile), ek ipuçları gibi daha fazla ayrıntıyla en son Mayıs 2022’de gözden geçirildi ve güncellendi.

Amazon Athena, standart SQL kullanarak Amazon Simple Storage Service‘te (Amazon S3) depolanan verileri analiz etmeyi kolaylaştıran etkileşimli bir sorgu hizmetidir. Athena sunucusuzdur, bu nedenle yönetilecek bir altyapı yoktur ve yalnızca çalıştırdığınız sorgular için ödeme yaparsınız. Athena’nın kullanımı kolaydır. Amazon S3’deki verilerinizi işaret edin, şemayı tanımlayın ve standart SQL kullanarak sorgulamaya başlayın.

Bu yazıda, sorgu performansını artırabilecek en iyi 10 ipucunu inceliyoruz. Amazon S3’de veri depolama ve sorgulara özel ayarlama ile ilgili konulara odaklanıyoruz.

Bu gönderi, Parquet, ORC, TEXTFILE, AVRO, CSV, TSV ve JSON gibi farklı dosya biçimleri hakkında bilgi sahibi olduğunuzu varsayar.

Depolama

Bu bölümde, Athena’dan en iyi şekilde yararlanabilmeniz için verilerinizi nasıl yapılandıracağınız anlatılmaktadır. Aynı uygulamaları, verileriniz Amazon S3’de depolandığında Spark, Presto ve Hive gibi Amazon EMR veri işleme uygulamalarına uygulayabilirsiniz. Aşağıdaki en iyi uygulamaları tartışıyoruz:

  1. Verilerinizi bölümlere ayırın
  2. Verilerinizi klasörleyin
  3. Sıkıştırmayı kullanın
  4. Dosya boyutunu optimize edin
  5. Sütunlu (columnar) veri deposu oluşturmayı optimize edin

1.   Verilerinizi bölümlere ayırın

Bölümleme (partitioning), tablonuzu bölümlere ayırır ve ilgili verileri tarih, ülke ve bölge gibi sütun değerlerine göre bir arada tutar. Bölümler sanal sütunlar gibi davranır. Bunları tablo oluştururken tanımlarsınız ve sorgu başına taranan veri miktarını azaltarak performansı artırmaya yardımcı olabilirler. Bölüme dayalı filtreler belirleyerek bir sorgu tarafından taranan veri miktarını kısıtlayabilirsiniz. Daha fazla ayrıntı için bkz. Athena’da verileri bölümleme.

Athena, iki adlandırma kuralından birini izleyen Hive bölümlemesini destekler. İlk yöntem, bir eşit sembolü (=) ve ardından değeri takip eden sütun adını bölümlemektir. Örneğin:

s3://yourBucket/pathToTable/<PARTITION_COLUMN_NAME>=<VALUE>/<PARTITION_COLUMN_NAME>=<VALUE>/
Code

Veri kümeniz bu biçimde bölümlenmişse, tablonuza otomatik olarak bölümler eklemek için MSCK REPAIR tablo komutunu çalıştırabilirsiniz.

Verilerinizin yolu önceki biçimi izlemiyorsa, her bölüm için ALTER TABLE ADD PARTITION komutunu kullanarak bölümleri manuel olarak ekleyebilirsiniz. Örneğin:

s3://yourBucket/pathToTable/YYYY/MM/DD/
ALTER TABLE <tablename> ADD PARTITION (PARTITION_COLUMN_NAME = <VALUE>, PARTITION_COLUMN2_NAME = <VALUE>) LOCATION ‘s3://yourBucket/pathToTable/YYYY/MM/DD/’;
Code

Bu metodoloji ile, herhangi bir konumu, onları hangi değerlere göre referans göstermek istediğinizle eşleyebilirsiniz.

Aşağıdaki örnek, verilerin bir S3 klasöründe depolanan uçuş tablosundaki yıl sütununda nasıl bölümlendiğini gösterir:

$ aws s3 ls s3://athena-examples/flight/parquet/
PRE year=1987/
PRE year=1988/
PRE year=1989/
PRE year=1990/
PRE year=1991/
PRE year=1992/
PRE year=1993/
Code

‘WHERE’ yantümcesindeki sütunu kullanarak bir sorguda taranan bölümleri kısıtlayabilirsiniz.

SELECT dest, origin FROM flights WHERE year = 1991
Code

Birden çok sütunu bölüm anahtarı (partition key) olarak da kullanabilirsiniz. Belirli değerler vb. için verileri tarayabilirsiniz.

s3://athena-examples/flight/parquet/year=1991/month=1/day=1/
s3://athena-examples/flight/parquet/year=1991/month=1/day=2/
Code

Bölünecek sütunlara karar verirken aşağıdakileri göz önünde bulundurun:

  • Filtre olarak kullanılan sütunlar, bölümleme için iyi adaylardır.
  • Bölmenin bir maliyeti vardır. Tablonuzdaki bölüm sayısı arttıkça, bölüm meta verilerini alma ve işleme ek yükü o kadar yüksek ve dosyalarınız o kadar küçük olur. Çok ince bir şekilde bölümleme, ilk faydayı ortadan kaldırabilir.
  • Verileriniz bir bölüm değerine aşırı derecede meyilliyse ve çoğu sorgu bu değeri kullanıyorsa, ek yük ilk avantajı ortadan kaldırabilir.

Örneğin, aşağıdaki tablo, bölümlenmiş ve bölümlenmemiş bir tablo arasındaki sorgu çalışma zamanlarını karşılaştırır. Her iki tablo da sıkıştırılmamış ve metin biçiminde depolanmış 74 GB veri içerir. Bölümlenmiş tablo, l_shipdate sütunu tarafından bölümlenmiştir ve 2.526 bölüme sahiptir.

Sorgu Bölümlenmemiş Tablo Fiyat Bölümlenmiş Tablo Fiyat Tasarruf
. Çalışma zamanı Taranan veriler . Çalışma zamanı Taranan veriler . ,
SELECT count(*) FROM lineitem WHERE l_shipdate = ‘1996-09-01’ 4,8 saniye 74,1 GB 0,36 USD 0,7 saniye 29,96 MB 0,0001 USD

%99 daha ucuz

%85 daha hızlı

SELECT count(*) FROM lineitem WHERE l_shipdate >= ‘1996-09-01’ AND l_shipdate < ‘1996-10-01’ 4,4 saniye 74,1 GB 0,36 USD 2,0 saniye 898,58 MB 0,004 USD

%98 daha ucuz

%54 daha hızlı

EXPLAIN ANALYZE‘ı bir sorgu üzerinde çalıştırmak, bir tablonun bölümlenmiş olup olmadığını ve bölümlenmiş bir sütunun filtre olarak kullanılıp kullanılmadığını belirlemeye yardımcı olur. Bölümlenmiş bir tabloda çalıştırılan aşağıdaki sorguyu göz önünde bulundurun:

EXPLAIN ANALYZE SELECT count(*) FROM lineitem WHERE l_shipdate = '1996-09-01'
SQL

Aşağıdaki çıktıda, TableScan operatörü için, taranan verilerde azalmaya neden olan bir bölüm anahtarı filtresinin kullanıldığını gösteriyor:

…
…
Fragment 2 
    CPU: 851.65ms, Input: 249684 rows (0B), Data Scanned: 29.96MB; per task: std.dev.: 284.00, Output: 30 rows (270B)
    Output layout: [count_3]
    - Aggregate(PARTIAL) => [[count_3]]
            CPU: 46.00ms (5.39%), Output: 30 rows (270B)
            Input avg.: 8322.80 rows, Input std.dev.: 1.17%
            count_3 := "count"(*)
        - TableScan[awsdatacatalog:HiveTableHandle{schemaName=tpch100, tableName=lineitem, analyzePartitionValues=Optional.empty}, grouped = false] => [[]]
                CPU: 805.00ms (94.37%), Output: 249684 rows (0B)
                Input avg.: 8322.80 rows, Input std.dev.: 1.17%
                LAYOUT: tpch100.lineitem
                l_shipdate:string:-1:PARTITION_KEY :: [[1996-09-01]]
Code

Aşağıdaki tabloda gösterildiği gibi, sorguda bir bölüm filtresi kullanılmazsa, bölümlemenin de bir cezası vardır. Verileri fazla bölmediğinizden emin olun. Aşırı bölümleme, bu yazıda daha sonra gösterildiği gibi, performansı düşüren daha fazla sayıda daha küçük dosyaya yol açar.

Sorgu Bölümlenmemiş Tablo Bölümlenmiş Tablo Tasarruf
. Çalışma zamanı Taranan veriler Çalışma zamanı Taranan veriler .
SELECT count(*) FROM lineitem; 3,4 saniye 74,1 GB 8,9 saniye 74,1 GB %62 daha yavaş

Bir AWS Glue Data Catalog’da depolanan tablonuz on binlerce, yüz binlerce ve milyonlarca bölüm içeriyorsa, tabloda bölüm dizinlerini (partition indexes) etkinleştirebilirsiniz. Bölüm dizinleriyle, tüm bölümlerin meta verilerini almak yerine, yalnızca sorgunun filtresindeki bölüm değeri için meta veriler katalogdan alınır. Sonuç, bu tür yüksek oranda bölümlenmiş tablolar için daha hızlı sorgulardır. Aşağıdaki tablo, bölüm dizinlemesi olmayan ve olan bölümlenmiş bir tablo arasındaki sorgu çalışma zamanlarını karşılaştırır. Tablo yaklaşık 100.000 bölüm ve sıkıştırılmamış metin verisi içerir. Siparişler tablosu o_custkey sütunu ile bölümlenmiştir.

Sorgu Bölüm dizinlemesi = devre dışı Bölüm dizinlemesi = etkin Hız artışı
. Çalışma zamanı Çalışma zamanı .
SELECT count(*) FROM orders where o_custkey BETWEEN 1 AND 100 19,5 saniye 1,2 saniye 16x

AWS Glue Data Catalog’un Athena’daki bölüm dizinlemesinin avantajları hakkında daha fazla bilgi edinmek için AWS Glue Data Catalog bölüm dizinlerini kullanarak Amazon Athena sorgu performansını iyileştirme bölümüne bakın.

2. Verilerinizi klasörleyin

Verilerinizi bölümlere ayırmanın başka bir yolu, verileri tek bir bölüm içinde klasörlemektir (bucketing). Klasörleme ile birlikte gruplamak istediğiniz satırları içeren bir veya daha fazla sütun belirleyebilir ve bu satırları birden çok klasöre koyabilirsiniz. Bu, yalnızca klasörlenen sütunlar değeri belirtildiğinde okumanız gereken klasörü sorgulamanıza olanak tanır; bu da okunacak veri satırlarının sayısını önemli ölçüde azaltabilir ve bu da sorguyu çalıştırma maliyetini düşürür.

Klasörleme için kullanılacak bir sütun seçerken, kardinalitesi yüksek (yani çok sayıda benzersiz değeri olan) ve sorgu süresi boyunca okunan verileri filtrelemek için sıklıkla kullanılan bir sütun seçmenizi öneririz. Klasörleme için kullanılacak iyi bir sütun örneği, sistemler için kullanıcı kimliği gibi bir birincil anahtar olabilir.

Athena’da, CREATE TABLE deyiminizin içindeki klasörlenmiş sütunu, CLUSTERED BY (<bucketed column>) INTO <number of buckets> BUCKETS ile belirtebilirsiniz. Klasör sayısı, dosyaların en uygun boyutta olması için olmalıdır. Daha fazla ayrıntı için Dosya boyutunu optimize edin bölümüne bakın.

Athena içinde klasörlenmiş tabloları kullanmak için, Athena Apache Spark klasörleme biçimini desteklemediğinden veri dosyalarını oluşturmak üzere Apache Hive kullanmanız gerekir. Klasörlenmiş tabloların nasıl oluşturulacağı hakkında bilgi için Apache Hive belgelerinde LanguageManual DDL BucketedTables‘a bakın.

Ayrıca Athena’nın, örneğin birden çok INSERT INTO ifadesinin çalıştırıldığı durumlarda olduğu gibi, dosya sayısının klasör sayısıyla eşleşmediği tabloları ve bölümleri desteklemediğini unutmayın.

Aşağıdaki tablo, 32 klasör oluşturmak için c_custkey sütununun kullanıldığı bir müşteri tablosundaki farkı gösterir. Müşteri tablosu 2,29 GB boyutundadır.

Sorgu Klasörlenmemiş Tablo Fiyat Kümelenmiş Sütun Olarak c_custkey Kullanan Klasörlenmiş Tablo Fiyat Tasarruf
. Çalışma zamanı Taranan veriler . Çalışma zamanı Taranan veriler . .
SELECT count(*) FROM customer where c_custkey = 12677856; 1,3 saniye 2,29 GB 0,01145 USD 0,82 saniye 72,94 MB 0,0003645 USD %97 daha ucuz
%37 daha hızlı

EXPLAIN ANALYZE’ı önceki sorguda çalıştırmak, klasörlemenin customer tablosu için Amazon S3’den daha az veri okumaya nasıl yardımcı olduğunu gösterir. Klasörlenmemiş ve klasörlenmiş tabloların sorgusundaki EXPLAIN ANALYZE çıktısının aşağıdaki parçacıkları, farkı anlamak için giriş satırlarını ve veri boyutunu vurgular.

Klasörlenmemiş tablonun çıktısı aşağıdadır:

…
…
- ScanFilterProject[table = awsdatacatalog:HiveTableHandle{schemaName=tpch100, tableName=customer, analyzePartitionValues=Optional.empty}, grouped = false, filterPredicate = ("c_custkey" = 12677856)] => [[]]
                CPU: 30.35s (99.95%), Output: 1 row (0B)
                Input avg.: 202702.70 rows, Input std.dev.: 4.83%
                LAYOUT: tpch100.customer
                c_custkey := c_custkey:int:0:REGULAR
                Input: 15000000 rows (2.29GB), Filtered: 100.00%
Code

Klasörlenmiş tablonun çıktısı aşağıdadır:

…
…
- ScanFilterProject[table = awsdatacatalog:HiveTableHandle{schemaName=tpch100, tableName=customer, analyzePartitionValues=Optional.empty}, grouped = false, filterPredicate = ("c_custkey" = 12677856)] => [[]]
                CPU: 858.00ms (100.00%), Output: 1 row (0B)
                Input avg.: 156250.00 rows, Input std.dev.: 22.44%
                LAYOUT: tpch100.customer bucket=32
                c_custkey := c_custkey:int:0:REGULAR
                Input: 468750 rows (72.94MB), Filtered: 100.00%
Code

3. Sıkıştırmayı kullanın

Dosyalar en uygun boyutta olduğu (sonraki bölüme bakın) veya dosyalar bölünebilir olduğu sürece, verilerinizi sıkıştırmak sorgularınızı önemli ölçüde hızlandırabilir. Daha küçük veri boyutları, Amazon S3’den taranan verileri azaltarak sorgu çalıştırma maliyetlerini düşürür. Ayrıca Amazon S3’den Athena’ya ağ trafiğini azaltır.

Aşağıdaki tablo, her bir depolama dosyası formatı için Athena’daki sıkıştırma formatı desteğini özetlemektedir. TEXTFILE formatı TSV, CSV, JSON ve metin için özel SerDes içerir.

. AVRO ORC Parquet TEXTFILE
BZIP2 Yalnızca okuma desteği. Yazma desteklenmiyor. Hayır Hayır Evet
DEFLATE Evet Hayır Hayır Hayır
GZIP Hayır Hayır Evet Evet
LZ4 Hayır Evet (ham/çerçevesiz) Hayır Hadoop uyumlu okuma desteği. Yazma desteklenmiyor.
LZO Hayır Hayır Evet Hadoop uyumlu okuma desteği. Yazma desteklenmiyor.
SNAPPY Ham/çerçevesiz okuma desteği. Yazma desteklenmiyor. Evet (ham/çerçevesiz) Evet (ham/çerçevesiz) Evet (Hadoop uyumlu çerçeveleme)
ZLIB Hayır Evet Hayır Hayır
ZSTD Hayır Evet Evet Evet

Bölünebilir bir dosya Athena’daki yürütme motoru tarafından paralel olarak okunabilirken, bölünemeyen bir dosya paralel olarak okunamaz. Bu, bölünemez bir dosyaya kıyasla bölünebilir bir dosyayı okumak için daha az zaman harcanması anlamına gelir. AVRO, Parquet ve Orc, kullanılan sıkıştırma codec’inden bağımsız olarak bölünebilir. Metin dosyaları için yalnızca BZIP2 ve LZO codec bileşeniyle sıkıştırılmış dosyalar bölünebilir. Metin dosyalarında başka codec bileşenleri kullanılıyorsa, tek bir büyük sıkıştırılmış dosyaya sahip olmaktan kaçının. Bunun yerine, aşağıdaki bölümde tartışıldığı gibi, en uygun boyutta birden çok sıkıştırılmış dosyaya bölün.

Athena için, verileri varsayılan olarak sıkıştıran ve bölünebilir olan Apache Parquet veya Apache ORC’u kullanmanızı öneririz.

Mevcut veri kümenizi sıkıştırmak için AWS Glue ETL işlerini, Amazon EMR’da Spark veya Hive veya Athena’da CTAS veya INSERT INTO ve UNLOAD deyimlerini kullanabilirsiniz. AWS Glue kullanarak sıkıştırmaya yönelik örnek bir komut dosyası aşağıdadır:

from awsglue.job import Job
from awsglue.transforms import *
from awsglue.context import GlueContext
from awsglue.utils import getResolvedOptions

from pyspark.context import SparkContext

args = getResolvedOptions(sys.argv, ['JOB_NAME'])

sc = SparkContext()
glueContext = GlueContext(sc)
spark = glueContext.spark_session
job = Job(glueContext)
job.init(args['JOB_NAME'], args)

## Read TABLE_NAME from DB_NAME out of the AWS Glue Data Catalog
dataset = glueContext.create_dynamic_frame.from_catalog(database = DB_NAME, table_name = TABLE_NAME, transformation_ctx = "dataset")

## Write data in JSON format to S3, compressed with GZip
outputdf = glueContext.write_dynamic_frame.from_options( \
	frame = dataset,
	connection_type = "s3",
	connection_options = {"path":"s3://bucket/prefix/"},
	format = "json",
	compression = "gzip",
	transformation_ctx = "outputdf")

job.commit()
Python

4. Dosya boyutlarını optimize edin

Veri tarama paralelleştirilebildiğinde ve veri blokları sırayla okunabildiğinde sorgular daha verimli çalışır. Dosya formatlarınızın bölünebilir olmasını sağlamak, dosyalarınızın büyüklüğü ne olursa olsun paralelliğe yardımcı olur.

Ancak, dosyalarınız çok küçükse (genellikle 128 MB’den az), yürütme motoru S3 dosyalarını açma, dizinleri listeleme, nesne meta verilerini alma, veri aktarımını ayarlama, dosya başlıklarını okuma, sözlüklerin sıkıştırma okuma vb. ek yüküyle ek zaman harcıyor olabilir. Öte yandan, dosyanız bölünebilir değilse ve dosyalar çok büyükse, sorgu işleme, tek bir okuyucunun tüm dosyayı okumasını tamamlamasını bekler. Bu paralelliği azaltabilir.

Küçük dosya sorununuzu çözmenin bir yolu, Amazon EMR’da S3DistCP yardımcı programını kullanmaktır. Daha küçük dosyaları daha büyük nesnelerle birleştirmek için kullanabilirsiniz. Büyük miktarda veriyi optimize edilmiş bir şekilde HDFS’ten Amazon S3’ye, Amazon S3’den Amazon S3’ye ve Amazon S3’den HDFS’e taşımak için de S3DistCP’yi kullanabilirsiniz.

Daha büyük dosyalara sahip olmanın bazı avantajları arasında daha hızlı listeleme, daha az Amazon S3 isteği ve yönetilecek daha az meta veri bulunur.

Örneğin, aşağıdaki tablo, biri tek bir büyük dosya ve diğeri 100.000 küçük dosya tarafından desteklenen iki tablo arasındaki sorgu çalışma zamanlarını karşılaştırır. Her iki tablo da metin biçiminde depolanan yaklaşık 8 GB veri içerir.

Sorgu Dosya sayısı Çalışma zamanı
SELECT count(*) FROM lineitem 100,000 dosya 13 saniye
SELECT count(*) FROM lineitem 1 dosya 1,3 saniye
Hız artışı ~%90

Aşağıdaki örnek komut dosyasında gösterildiği gibi, verilerinizi bölmek için AWS Glue’yu da kullanabilirsiniz.

from awsglue.job import Job
from awsglue.transforms import *
from awsglue.context import GlueContext
from awsglue.utils import getResolvedOptions

from pyspark.context import SparkContext

args = getResolvedOptions(sys.argv, ['JOB_NAME'])

sc = SparkContext()
glueContext = GlueContext(sc)
spark = glueContext.spark_session
job = Job(glueContext)
job.init(args['JOB_NAME'], args)

## Read TABLE_NAME from DB_NAME out of the AWS Glue Data Catalog
dataset = glueContext.create_dynamic_frame.from_catalog(database = DB_NAME, table_name = TABLE_NAME, transformation_ctx = "dataset")

## Write data in JSON format to S3, compressed with GZip
outputdf = glueContext.write_dynamic_frame.from_options( \
	frame = dataset,
	connection_type = "s3",
	connection_options = {"path":"s3://bucket/prefix/", compression = "gzip"},
	format = "json",
	transformation_ctx = "outputdf")

job.commit()
Python

5. Sütunlu (columnar) veri deposu oluşturmayı optimize edin

Apache Parquet ve Apache ORC, popüler sütunlu veri depolarıdır. Sütun bazında sıkıştırma, farklı kodlama, veri türüne dayalı sıkıştırma ve predicate pushdown kullanarak verileri verimli bir şekilde depolayan özellikler sağlarlar. Onlar da bölünebilir. Genel olarak, daha iyi sıkıştırma oranları veya veri bloklarını atlama, Amazon S3’den daha az bayt okumak anlamına gelir ve bu da daha iyi sorgu performansı ve daha düşük sorgu çalıştırma maliyetleri sağlar. Parquet ve ORC, optimum performans için ayarlayabileceğiniz birkaç parametreye sahiptir.

Ayarlayabileceğiniz bir parametre blok (block) boyutudur (veya şerit – stripe – boyutu). Parquet’deki blok boyutu (veya ORC’daki şerit boyutu), bayt cinsinden boyut açısından bir bloğa sığabilecek maksimum satır sayısını temsil eder. Blok veya şerit boyutu ne kadar büyük olursa, her blokta o kadar fazla satır saklayabilirsiniz. Varsayılan olarak, Parquet blok boyutu 128 MB ve ORC şerit boyutu 64 MB’dir. Çok sütunlu tablolarınız varsa, her sütun bloğunun verimli sıralı I/O’ya izin veren bir boyutta kalmasını sağlamak için daha büyük bir blok boyutu öneririz.

Ayarlanabilecek başka bir parametre de veri bloklarındaki sıkıştırma algoritmasıdır. Parquet GZIP, Snappy (varsayılan), ZSTD ve LZO tabanlı sıkıştırma tekniklerini destekler. ORC ise ZLIB (varsayılan), LZ4, ZSTD ve Snappy sıkıştırma tekniklerini destekler. 10 GB’den fazla veriye sahipseniz, varsayılan sıkıştırma algoritmasıyla başlamanızı ve diğer sıkıştırma algoritmalarıyla test etmenizi öneririz.

Parquet ve ORC dosya biçimlerinin her ikisi de predicate pushdown destekler (predicate filtering olarak da adlandırılır). Her iki formatta da sütun değerlerini temsil eden veri blokları bulunur. Her blok, blok için maks/min değerleri gibi istatistikleri tutar. Bir sorgu çalıştırılırken, bu istatistikler, sorguda kullanılan filtre değerine bağlı olarak bloğun okunması mı yoksa atlanması mı gerektiğini belirler. Bu, taranan verilerin azaltılmasına yardımcı olur ve sorgu çalışma zamanını iyileştirir. Bu özelliği kullanmak için sorguya daha fazla filtre ekleyin (örneğin, bir WHERE yan tümcesi kullanarak).

Atlanacak blok sayısını optimize etmenin bir yolu, ORC veya Parquet dosyalarınızı yazmadan önce yaygın olarak filtrelenen bir sütunu belirlemek ve sıralamaktır. Bu, blok içindeki minimum ve maksimum değerler arasındaki aralığın her bir blok içinde mümkün olduğunca küçük olmasını sağlar. Bu, budanma için daha iyi bir şans verir ve ayrıca taranan verileri daha da azaltır.

Aşağıdaki tablo, metin GZIP, sıralama olmadan Parquet GZIP ve l_partkey üzerinde sıralama ile Parquet GZIP’de aynı veri kümesi için taranan çalışma zamanlarını ve verileri karşılaştırır.

Sorgu select l_orderkey from lineitem where l_partkey = 17766770 Metin Formatına Göre Tasarruf
Text GZIP data Çalışma zamanı 33,06 saniye .
Taranan veriler 22 GB .
Fiyat 0,1 USD .
Sıralama olmadan Parquet GZIP Çalışma zamanı 1,72 saniye ~%94 daha hızlı
Taranan veriler 2 GB .
Fiyat 0,009 USD ~%91 daha ucuz
l_partkey üzerinde sıralama ile Parquet GZIP Çalışma zamanı 1,0 saniye ~%96 daha hızlı
Taranan veriler 34,34 MB .
Fiyat 0,0001 USD ~%99,9 daha ucuz

You can convert your existing data to Parquet or ORC using Spark or Hive on Amazon EMR, AWS Glue ETL jobs, or CTAS or INSERT INTO and UNLOAD in Athena. See also the following resources:

Amazon EMR’da Spark veya Hive, AWS Glue ETL işleri veya Athena’da CTAS veya INSERT INTO ve UNLOAD kullanarak mevcut verilerinizi Parquet veya ORC’a dönüştürebilirsiniz. Ayrıca aşağıdaki kaynaklara bakın:

Sorgu ayarlama

Athena motoru Presto üzerine inşa edilmiştir. Nasıl çalıştığını anlamak, sorguları çalıştırırken nasıl optimize edebileceğiniz konusunda fikir verir. Bu bölüm aşağıdaki en iyi uygulamaları detaylandırmaktadır:

  1. ORDER BY deyimini optimize edin
  2. Birleşimleri (joins) optimize edin
  3. GROUP BY deyimini optimize edin
  4. Yaklaşık işlevleri kullanın
  5. Yalnızca ihtiyacınız olan sütunları ekleyin

6. ORDER BY deyimini optimize edin

ORDER BY deyimi, bir sorgunun sonuçlarını sıralama düzeninde döndürür. Athena, sıralama işlemini birden çok düğümde paralel olarak çalıştırmak için dağıtılmış sıralama kullanır. Üst veya alt N değerlerine bakmak için ORDER BY deyimini kullanıyorsanız, sıralamanın maliyetini azaltmak için bir LIMIT deyimi kullanın, bu da daha hızlı bir sorgu çalışma zamanı sağlar.

Örneğin, aşağıdaki tablo, metin biçiminde sıkıştırılmamış, yaklaşık 60 milyon satırlık 7,25 GB’lık bir tabloya sahip bir veri kümesinin çalışma zamanını özetlemektedir.

Sorgu Çalışma zamanı
SELECT * FROM lineitem ORDER BY l_shipdate 274 saniye
SELECT * FROM lineitem ORDER BY l_shipdate LIMIT 10000 4,6 saniye
Hız artışı %98 daha hızlı

7. Birleşimleri (joins) optimize edin

Doğru bir birleşim sırası seçmek, daha iyi sorgu performansı için çok önemlidir. İki tabloyu birleştirdiğinizde, büyük tabloyu birleşimin sol tarafında ve daha küçük tabloyu birleşimin sağ tarafında belirtin. Athena, sağdaki tabloyu çalışan düğümlere dağıtır ve ardından birleştirmeyi yapmak için soldaki tabloyu akışa alır. Sağdaki tablo daha küçükse, daha az bellek kullanılır ve sorgu daha hızlı çalışır.

Aşağıdaki tablo, metin biçiminde sıkıştırılmamış, yaklaşık 602 milyon satırlık toplam 74 GB veri içeren bir veri kümesindeki çalışma zamanlarını gösterir.

Sorgu Çalışma zamanı
SELECT count(*) FROM lineitem, part WHERE lineitem.l_partkey = part.p_partkey 6,4 saniye
SELECT count(*) FROM part, lineitem WHERE part.p_partkey = lineitem.l_partkey 8,1 saniye
Tasarruf / Hız artışı ~%20 hız artışı

Üç ve daha fazla tabloyu birleştirdiğinizde, ara sonucu azaltmak ve diğer tablolarla birleştirmek için önce büyük tabloyu en küçük tabloyla birleştirmeyi düşünebilirsiniz.

Athena ayrıca, aşağıdaki örnekte gösterildiği gibi, sorgu çalışma zamanını iyileştiren ve birleşimli sorgular için taranan verileri ve birleştirmenin sağ tarafındaki tablo için çok seçici bir filtre yan tümcesini azaltan dinamik filtrelemeyi ve dinamik bölüm budamasını destekler. Aşağıdaki sorguda Table_B, çok seçici bir filtreye sahip küçük bir tablodur (column_A = “value”). Seçici filtre Table_B‘ye uygulandıktan sonra, önce birleştirilmiş Table_B.date sütunu için bir değer listesi çıkarılır ve filtre olarak birleştirilmiş Table_A tablosuna itilir. Table_A‘nın gereksiz satırlarını ve bölümlerini filtrelemek için kullanılır. Bu, Table_A için kaynaktan daha az satır ve bölümün okunmasıyla sonuçlanır ve sorgu çalışma süresinin ve veri tarama boyutunun azaltılmasına yardımcı olur, bu da sorguyu Athena’da çalıştırma maliyetlerinin azaltılmasına yardımcı olur.

SELECT count(*)
FROM Table_A
    JOIN Table_B ON Table_A.date = Table_B.date
WHERE Table_B.column_A = "value"
SQL

8. GROUP BY deyimini optimize edin

GROUP BY operatörü, GROUP BY sütunlarına dayalı olarak satırları, GROUP BY değerlerini bellekte tutan çalışan düğümlere dağıtır. Satırlar alınırken, GROUP BY sütunları bellekte aranır ve değerler karşılaştırılır. GROUP BY sütunları eşleşirse değerler birlikte toplanır.

Sorgunuzda GROUP BY kullanırken, sütunları en yüksek kardinaliteye (yani, eşit olarak dağıtılmış en benzersiz değerlere) göre en düşüğe doğru sıralayın.

SELECT state, gender, count(*) 
           FROM census 
GROUP BY state, gender;
SQL

Diğer bir iyileştirme, satırlar bellekte tutulduğundan ve GROUP BY deyimi için toplandığından, depolamak için gereken bellek miktarını azaltmak için SELECT ve GROUP BY deyimindeki sütun sayısını sınırlamaktır. Aşağıdaki örnek, SELECT deyimindeki sütunların azaltılmasıyla ilgili sorgulardaki hızlanmayı gösterir.

Sorgu Çalışma zamanı
select “l_shipdate”,”l_receiptdate”,”l_commitdate”, “l_linestatus”, count(*) from “lineitem” group by 1,2,3,4 33,6 saniye
select “l_shipdate”, “l_linestatus”, count(*) from “lineitem” group by 1,2 6,2 saniye
Hız artışı ~%81 hız artışı

9. Yaklaşık işlevleri kullanın

Büyük veri kümelerini araştırmak için yaygın bir kullanım durumu, COUNT(DISTINCT sütunu) kullanarak belirli bir sütun için farklı değerlerin sayısını bulmaktır. Bir örnek, bir web sayfasına giren benzersiz kullanıcıların sayısına bakmaktır.

Kesin bir sayı gerekli olmadığında (örneğin, hangi web sayfalarını derinlemesine inceleyeceğinizi arıyorsanız), approx_distinct() kullanmayı düşünün. Bu işlev, tüm dizeler yerine benzersiz değer karmalarını sayarak bellek kullanımını en aza indirmeye çalışır. Dezavantajı ise %2,3’lük bir standart hata olmasıdır.

Aşağıdaki tablo, metin biçiminde sıkıştırılmamış, yaklaşık 600 milyon satırlık 74 GB’lık bir tabloya sahip bir veri kümesindeki hızlanmayı özetlemektedir.

Sorgu Çalışma zamanı
SELECT count(distinct l_comment) FROM lineitem; 7,7 saniye
SELECT approx_distinct(l_comment) FROM lineitem; 4,6 saniye
Hız artışı ~%40 daha hızlı

Daha fazla bilgi için Presto belgelerindeki Toplama İşlevleri‘ne bakın.

10. Yalnızca ihtiyacınız olan sütunları ekleyin

Sorgularınızı çalıştırırken, son SELECT ifadesini tüm sütunları seçmek yerine yalnızca ihtiyacınız olan sütunlarla sınırlayın. Sütun sayısını kırpmak, tüm sorgu çalıştırma işlem hattı boyunca işlenmesi gereken veri miktarını azaltır. Bu, özellikle dize tabanlı çok sayıda sütun içeren tabloları sorgularken ve birden çok birleştirme veya toplama gerçekleştirdiğinizde yardımcı olur. Sütunlu biçimler için, yalnızca belirli sütunların verileri okunduğundan Amazon S3’den taranan verileri azaltır.

Aşağıdaki tablo, metin biçiminde sıkıştırılmamış, yaklaşık 60 milyon satırlık 7,25 GB’lık bir tabloya sahip bir veri kümesindeki hızlanmayı özetlemektedir.

Sorgu Çalışma zamanı
SELECT * FROM lineitem, orders, customer WHERE lineitem.l_orderkey = orders.o_orderkey AND customer.c_custkey = orders.o_custkey; 19,7 saniye
SELECT customer.c_name, lineitem.l_quantity, orders.o_totalprice FROM lineitem, orders, customer WHERE lineitem.l_orderkey = orders.o_orderkey AND customer.c_custkey = orders.o_custkey; 5,2 saniye
Tasarruf / Hız artışı %73 daha hızlı

Bonus İpuçları

Bu bölümde, ek performans ayarlama ipuçları sağlıyoruz.

Bölme projeksiyonunu kullanarak Bölme İşlemeyi Optimize Etme

Çok sayıda bölümünüz olduğunda ve AWS Glue bölüm indekslemeyi kullanmadığınızda, bölüm bilgilerinin işlenmesi Athena sorguları için bir darboğaz olabilir. Athena’da yüksek oranda bölümlenmiş tabloların sorgu işlemesini hızlandırmak ve bölüm yönetimini otomatikleştirmek için bölüm projeksiyonunu kullanabilirsiniz. Bölüm projeksiyonu, bir metastore’dan almak yerine bölüm bilgilerini hesaplayarak bölümleri sorgulamanıza izin vererek bu ek yükü en aza indirmeye yardımcı olur. AWS Glue tablosuna bölümlerin meta verilerini ekleme ihtiyacını ortadan kaldırır.

Bölüm projeksiyonunda, bölüm değerleri ve konumları, AWS Glue Data Catalog gibi bir havuzdan okumak yerine yapılandırmadan hesaplanır. Bellek içi işlemler genellikle uzak işlemlerden daha hızlı olduğundan, bölüm projeksiyonu, yüksek oranda bölümlenmiş tablolara karşı sorguların çalışma süresini azaltabilir. Sorgunun ve temel alınan verilerin belirli özelliklerine bağlı olarak, bölüm projeksiyonu, bölüm meta veri alımıyla sınırlı sorgular için sorgu çalışma süresini önemli ölçüde azaltabilir.

Bölümlerin şemaları aynı olduğunda veya tabloların şeması bölüm şemalarını her zaman doğru bir şekilde açıklıyorsa, bölüm projeksiyonunu kullanmak idealdir. Kimlikler gibi çok yüksek önem derecesine sahip sütunları veya çok ince ayrıntı düzeyinde tarih aralıklarını bölmek için kullanılabilir.

Daha fazla ayrıntı için Amazon Athena ile bölüm projeksiyonuna bakın.

UNLOAD kullanarak büyük sonuç kümeleri üreten sorguları hızlandırın

Athena’da bir SELECT sorgusu çalıştırmak, Amazon S3’de sıkıştırılmamış CSV biçiminde tek bir sonuç dosyası üretir. Sorgunuzun büyük bir sonuç kümesi (aşağıdaki örnekteki 13 GB gibi) vermesi bekleniyorsa, sonuçları tek bir dosya olarak Amazon S3’ye yazmak için önemli ölçüde zaman harcanır. UNLOAD ile, Amazon S3’de sonuçları birden çok dosyaya bölerek yazma aşamasında harcanan süreyi kısaltabilirsiniz. Ayrıca sonuç kümesi için sonuç biçimini (ORC, Parquet, AVRO, JSON veya TEXTFILE) ve sıkıştırma türünü (varsayılan olarak Parquet, JSON ve TEXTFILE için GZIP ve ORC için ZLIB’dir) belirtebilirsiniz.

Aşağıdaki tablo, SELECT ve UNLOAD deyimleri arasındaki bir karşılaştırmayı göstermektedir. Sorgunun yaklaşık 13 GB sıkıştırılmamış veri vermesi bekleniyor.

Sorgu SELECT * FROM lineitem LIMIT 85700000 UNLOAD (SELECT * FROM lineitem LIMIT 85700000) to <s3-output-location> with (format=’TEXTFILE’) Tasarruf
Çalışma zamanı 362 saniye 33,2 saniye ~%90 daha hızlı
Sonuç kümesi 13 GB (CSV, sıkıştırılmamış) 3,2 GB (CSV, gzip sıkıştırılmış) ~%75 azaltılmış depolama

Sonuç

Bu gönderi, Presto motoruyla Athena’daki etkileşimli analizinizi optimize etmek için en iyi 10 ipucumuzu ele aldı. Aynı uygulamaları Amazon EMR’da Presto’yu kullanırken de uygulayabilirsiniz.