paint-brush
Bir Veritabanı OOM Çökmelerinden Nasıl Kurtulur?ile@shirleyfromapachedoris
763 okumalar
763 okumalar

Bir Veritabanı OOM Çökmelerinden Nasıl Kurtulur?

ile Shirley H.11m2023/06/12
Read on Terminal Reader
Read this story w/o Javascript

Çok uzun; Okumak

Bellek ayırma, bellek izleme ve bellek sınırında optimizasyonlar sağlayan daha sağlam ve esnek bir bellek yönetimi çözümü.

People Mentioned

Mention Thumbnail
featured image - Bir Veritabanı OOM Çökmelerinden Nasıl Kurtulur?
Shirley H. HackerNoon profile picture

Büyük veri sorgulama görevlerinde sistem kararlılığını ne garanti eder? Etkili bir hafıza ayırma ve izleme mekanizmasıdır. Hesaplamayı bu şekilde hızlandırırsınız, bellek etkin noktalarından kaçınırsınız, yetersiz belleğe anında yanıt verirsiniz ve OOM hatalarını en aza indirirsiniz.




Bir veritabanı kullanıcısının bakış açısına göre, kötü bellek yönetiminden nasıl etkilenirler? Kullanıcılarımızı rahatsız eden şeylerin listesi aşağıdadır:


  • OOM hataları arka uç işlemlerinin çökmesine neden olur. Topluluk üyelerimizden birinden alıntı yapacak olursak: Merhaba Apache Doris, hafızanız yetersiz olduğunda işleri yavaşlatmanız veya birkaç görevde başarısız olmanız sorun değil, ancak kesintiye uğramak hiç de hoş değil.


  • Arka uç işlemleri çok fazla bellek alanı tüketir, ancak tek bir sorgu için suçlanacak veya bellek kullanımını sınırlandıracak tam görevi bulmanın bir yolu yoktur.


  • Her sorgu için uygun bir bellek boyutu ayarlamak zordur; bu nedenle, bol miktarda bellek alanı olsa bile sorgunun iptal edilme ihtimali vardır.


  • Yüksek eşzamanlılık sorguları orantısız derecede yavaştır ve bellek etkin noktalarının bulunması zordur.


  • HashTable oluşturma sırasında ara veriler disklere aktarılamaz, bu nedenle iki büyük tablo arasındaki birleştirme sorguları genellikle OOM nedeniyle başarısız olur.


Neyse ki o karanlık günler geride kaldı çünkü hafıza yönetimi mekanizmamızı baştan sona geliştirdik. Şimdi hazırlanın; işler yoğunlaşacak.

Bellek Tahsisi

Apache Doris'te bellek tahsisi için tek ve tek bir arayüze sahibiz: Allocator . Bellek kullanımını verimli ve kontrol altında tutmak için uygun gördüğü ayarlamaları yapacaktır.


Ayrıca, MemTracker'lar tahsis edilen veya serbest bırakılan hafıza boyutunu takip etmek için mevcuttur ve operatörün yürütülmesi sırasında büyük hafıza tahsisinden üç farklı veri yapısı sorumludur (bunlara hemen ulaşacağız).




Bellekteki Veri Yapıları

Farklı sorgular yürütülürken farklı bellek erişim noktası modellerine sahip olduğundan, Apache Doris üç farklı bellek içi veri yapısı sağlar: Arena , HashTable ve PODArray . Hepsi Tahsis Edici'nin egemenliği altındadır.



  1. Arena

Arena, Tahsis Edicinin talebi üzerine tahsis edilecek parçaların listesini tutan bir hafıza havuzudur. Parçalar hafıza hizalamasını destekler. Bunlar Arena'nın ömrü boyunca mevcuttur ve yok edildiklerinde (genellikle sorgu tamamlandığında) serbest kalacaklardır.


Parçalar esas olarak Shuffle sırasında serileştirilmiş veya seri durumdan çıkarılmış verileri veya HashTable'larda serileştirilmiş Anahtarları depolamak için kullanılır.


Bir yığının başlangıç boyutu 4096 bayttır. Geçerli yığın istenen bellekten küçükse listeye yeni bir yığın eklenecektir.


Mevcut parça 128M'den küçükse yeni parçanın boyutu iki katına çıkar; 128M'den büyükse, yeni yığın gerekenden en fazla 128M daha büyük olacaktır.


Eski küçük yığın yeni isteklere tahsis edilmeyecektir. Tahsis edilen ve ayrılmamış parçalar arasındaki bölme çizgisini işaretlemek için bir imleç vardır.


  1. Karma Tablo

HashTable'lar Hash Birleştirmeleri, toplamalar, ayar işlemleri ve pencere işlevleri için geçerlidir. PartitionedHashTable yapısı en fazla 16 alt HashTable'ı desteklemez. Ayrıca HashTable'ların paralel olarak birleştirilmesini de destekler ve her bir alt Hash Birleştirmesi bağımsız olarak ölçeklendirilebilir.


Bunlar genel bellek kullanımını ve ölçeklendirmenin neden olduğu gecikmeyi azaltabilir.


Geçerli HashTable 8M'den küçükse 4 faktörüyle ölçeklendirilir;

8M'den büyükse 2 faktörüyle ölçeklendirilecektir;

2G'den küçükse %50 dolu olduğunda ölçeklendirilecektir;

2G'den büyük ise %75 dolu olduğunda ölçeklendirilecektir.


Yeni oluşturulan HashTable'lar, sahip olacağı veri miktarına göre önceden ölçeklendirilecektir. Ayrıca farklı senaryolar için farklı türde HashTable'lar da sağlıyoruz. Örneğin toplamalar için PHmap'i uygulayabilirsiniz.


  1. PODA Dizisi

PODArray, adından da anlaşılacağı gibi dinamik bir POD dizisidir. Bununla std::vector arasındaki fark, PODArray'in öğeleri başlatmamasıdır. Bellek hizalamasını ve std::vector bazı arayüzlerini destekler.


2 faktörüyle ölçeklendirilir. Yok etme sırasında, her öğe için yıkıcı işlevi çağırmak yerine tüm PODArray'in belleğini serbest bırakır. PODArray temel olarak dizeleri sütunlara kaydetmek için kullanılır ve birçok işlev hesaplamasında ve ifade filtrelemede uygulanabilir.

Bellek Arayüzü

Arena, PODArray ve HashTable'ı koordine eden tek arayüz olan Allocator, 64M'den büyük istekler için bellek eşleme (MMAP) tahsisini yürütür.


4K'dan küçük olanlar malloc/free üzerinden doğrudan sistemden tahsis edilecek; ve arada kalanlar, kıyaslama sonuçlarımıza göre %10'luk bir performans artışı sağlayan genel amaçlı bir önbellekleme ChunkAllocator ile hızlandırılacak.


ChunkAllocator, belirtilen boyuttaki bir yığını geçerli çekirdeğin FreeList'inden kilitsiz bir şekilde almaya çalışacaktır; eğer böyle bir yığın mevcut değilse, diğer çekirdeklerden kilit tabanlı bir şekilde deneme yapacaktır; eğer bu hala başarısız olursa, sistemden belirtilen bellek boyutunu isteyecek ve bunu bir yığın halinde kapsülleyecektir.


Her ikisini de deneyimledikten sonra TCMalloc yerine Jemalloc'u seçtik. Yüksek eşzamanlılık testlerimizde TCMalloc'u denedik ve CentralFreeList'teki Döndürme Kilidinin toplam sorgu süresinin %40'ını aldığını fark ettik.


"Agresif bellek ayırmayı" devre dışı bırakmak işleri daha iyi hale getirdi, ancak bu çok daha fazla bellek kullanımına neden oldu, bu nedenle önbelleği düzenli olarak geri dönüştürmek için ayrı bir iş parçacığı kullanmak zorunda kaldık. Öte yandan Jemalloc, yüksek eşzamanlılık sorgularında daha performanslı ve istikrarlıydı.


Diğer senaryolar için ince ayarlar yapıldıktan sonra TCMalloc ile aynı performansı sağladı ancak daha az bellek tüketti.

Belleğin Yeniden Kullanımı

Belleğin yeniden kullanımı, Apache Doris'in yürütme katmanında yaygın olarak yürütülür. Örneğin veri blokları bir sorgunun yürütülmesi boyunca yeniden kullanılacaktır. Karıştırma sırasında Gönderici tarafında iki blok olacak ve bunlar dönüşümlü olarak çalışacak, biri veri alırken diğeri RPC aktarımında olacak.


Doris bir tableti okurken yüklem sütununu yeniden kullanacak, döngüsel okumayı uygulayacak, filtreleyecek, filtrelenmiş verileri üst bloğa kopyalayacak ve ardından temizleyecektir.


Verileri bir Toplama Anahtarı tablosuna alırken, verileri önbelleğe alan MemTable belirli bir boyuta ulaştığında, önceden toplanacak ve ardından daha fazla veri yazılacaktır.


Belleğin yeniden kullanımı da veri taramada gerçekleştirilir. Tarama başlamadan önce, tarama görevine bir dizi boş blok (tarayıcı ve iş parçacığı sayısına bağlı olarak) tahsis edilecektir.


Her tarayıcı zamanlaması sırasında, boş bloklardan biri veri okunması için depolama katmanına aktarılacaktır.


Veri okunduktan sonra blok, sonraki hesaplamalarda üst operatörlerin tüketimi için üretici kuyruğuna konulacaktır. Bir üst operatör hesaplama verilerini bloktan kopyaladığında, blok bir sonraki tarayıcı planlaması için boş bloklara geri dönecektir.


Boş blokları önceden tahsis eden iş parçacığı aynı zamanda veri taramasından sonra bunları serbest bırakmaktan da sorumlu olacaktır, dolayısıyla ekstra masraf olmayacaktır. Boş blokların sayısı bir şekilde veri taramasının eşzamanlılığını belirler.

Bellek Takibi

Apache Doris, bellek etkin noktalarını analiz ederken belleğin tahsisini ve serbest bırakılmasını takip etmek için MemTrackers'ı kullanıyor. MemTracker'lar her veri sorgusunun, veri alımının, veri sıkıştırma görevinin ve Cache ve TabletMeta gibi her global nesnenin bellek boyutunun kayıtlarını tutar.


Hem manuel sayımı hem de MemHook otomatik takibini destekler. Kullanıcılar, bir Web sayfasındaki Doris arka ucunda gerçek zamanlı bellek kullanımını görüntüleyebilir.

MemTrackers'ın Yapısı

Apache Doris 1.2.0'dan önceki MemTracker sistemi, proses_mem_tracker, query_pool_mem_tracker, query_mem_tracker, example_mem_tracker, ExecNode_mem_tracker ve benzerlerinden oluşan hiyerarşik bir ağaç yapısındaydı.


İki komşu katmanın MemTracker'ları ebeveyn-çocuk ilişkisine sahiptir. Bu nedenle, çocuk MemTracker'daki herhangi bir hesaplama hatası baştan sona toplanacak ve daha büyük bir güvenilmezliğe yol açacaktır.



Apache Doris 1.2.0 ve daha yeni sürümlerde MemTrackers'ın yapısını çok daha basit hale getirdik. MemTracker'lar rollerine göre yalnızca iki türe ayrılır: MemTracker Limiter ve diğerleri.


Bellek kullanımını izleyen MemTracker Sınırlayıcı, her sorgu/besleme/sıkıştırma görevinde ve genel nesnede benzersizdir; diğer MemTracker'lar, belleğin farklı operatörlerde nasıl kullanıldığına dair bir resim vermek veya bellek kontrolü için bir referans sağlamak için, sorgu yürütmedeki Birleştirme/Toplama/Sıralama/Pencere işlevlerindeki HashTable'lar ve serileştirmedeki ara veriler gibi bellek sıcak noktalarını izler. veri temizleme.


MemTracker Limiter ve diğer MemTracker'lar arasındaki ebeveyn-çocuk ilişkisi yalnızca anlık görüntü yazdırmada ortaya çıkar. Böyle bir ilişkiyi sembolik bir bağ gibi düşünebilirsiniz. Aynı anda tüketilmezler ve birinin yaşam döngüsü diğerinin yaşam döngüsünü etkilemez.


Bu, geliştiricilerin bunları anlamasını ve kullanmasını çok daha kolaylaştırır.


MemTrackers (MemTracker Limiter ve diğerleri dahil) bir Harita grubuna yerleştirilir. Kullanıcıların genel MemTracker türü anlık görüntüleri, Sorgu/Yükleme/Sıkıştırma görevi anlık görüntülerini yazdırmasına ve en fazla bellek kullanımı veya en fazla bellek aşırı kullanımına sahip Sorgu/Yüklemeyi bulmasına olanak tanır.



MemTracker Nasıl Çalışır?

Belirli bir yürütmenin bellek kullanımını hesaplamak için, geçerli iş parçacığının İş Parçacığı Yerel'deki bir yığına bir MemTracker eklenir. Jemalloc veya TCMalloc'ta malloc/free/realloc'u yeniden yükleyerek MemHook, tahsis edilen veya serbest bırakılan belleğin gerçek boyutunu elde eder ve bunu mevcut iş parçacığının Yerel İş Parçacığına kaydeder.


Bir yürütme tamamlandığında ilgili MemTracker yığından kaldırılacaktır. Yığının en altında, tüm sorgu/yük yürütme süreci boyunca bellek kullanımını kaydeden MemTracker bulunur.


Şimdi basitleştirilmiş bir sorgu yürütme süreciyle anlatayım.


  • Bir Doris arka uç düğümü başlatıldıktan sonra, tüm iş parçacıklarının bellek kullanımı Process MemTracker'a kaydedilecektir.


  • Bir sorgu gönderildiğinde, parça yürütme iş parçacığında İş Parçacığı Yerel Depolama (TLS) Yığınına bir Sorgu MemTracker eklenecektir.


  • Bir ScanNode planlandıktan sonra, parça yürütme iş parçacığında İş Parçacığı Yerel Depolama (TLS) Yığınına bir ScanNode MemTracker eklenecektir. Daha sonra, bu iş parçacığında ayrılan veya serbest bırakılan herhangi bir bellek hem Query MemTracker'a hem de ScanNode MemTracker'a kaydedilecektir.


  • Bir Tarayıcı planlandıktan sonra, Tarayıcı iş parçacığının TLS Yığınına bir Sorgu MemTracker'ı ve bir Tarayıcı MemTracker'ı eklenecektir.


  • Tarama tamamlandığında Tarayıcı İş Parçacığı TLS Yığınındaki tüm MemTracker'lar kaldırılacaktır. ScanNode zamanlaması tamamlandığında, ScanNode MemTracker parça yürütme iş parçacığından kaldırılacaktır. Daha sonra, benzer şekilde, bir toplama düğümü planlandığında, parça yürütme iş parçacığı TLS Yığınına bir AggregationNode MemTracker eklenecek ve planlama tamamlandıktan sonra kaldırılacaktır.


  • Sorgu tamamlanırsa Query MemTracker, parça yürütme iş parçacığı TLS Yığınından kaldırılacaktır. Bu noktada bu yığının boş olması gerekir. Daha sonra, QueryProfile'dan sorgu yürütmenin tamamı ve her aşama (tarama, toplama vb.) sırasındaki en yüksek bellek kullanımını görüntüleyebilirsiniz.



MemTracker Nasıl Kullanılır?

Doris arka uç Web sayfası, türlere ayrılmış gerçek zamanlı bellek kullanımını gösterir: Sorgu/Yükleme/Sıkıştırma/Global. Mevcut bellek tüketimi ve en yüksek tüketim gösterilir.



Global türler arasında MemTrackers of Cache ve TabletMeta bulunur.



Sorgu türlerinden, mevcut sorgunun mevcut bellek tüketimini ve en yüksek tüketimini ve içerdiği operatörleri görebilirsiniz (bunların nasıl ilişkili olduğunu etiketlerden anlayabilirsiniz). Geçmiş sorguların hafıza istatistikleri için Doris FE denetim günlüklerini veya BE INFO günlüklerini kontrol edebilirsiniz.



Bellek Sınırı

Doris arka uçlarında yaygın olarak uygulanan bellek izleme sayesinde, arka uç kesintilerinin ve büyük ölçekli sorgu hatalarının nedeni olan OOM'u ortadan kaldırmaya bir adım daha yaklaştık. Bir sonraki adım, bellek kullanımını kontrol altında tutmak için sorgular ve işlemlerdeki bellek sınırını optimize etmektir.

Sorguda Bellek Sınırı

Kullanıcılar her sorguya bir hafıza sınırı koyabilirler. Yürütme sırasında bu sınır aşılırsa sorgu iptal edilir. Ancak sürüm 1.2'den bu yana, daha esnek bir bellek sınırı kontrolü olan Bellek Aşırı Taahhüdü'ne izin verdik.


Yeterli bellek kaynağı varsa, bir sorgu iptal edilmeden sınırdan daha fazla bellek tüketebilir; böylece kullanıcıların bellek kullanımına ekstra dikkat etmesi gerekmez; yoksa sorgu yeni bellek alanı tahsis edilene kadar bekler, ancak yeni boşalan bellek sorgu için yeterli olmadığında sorgu iptal edilir.


Apache Doris 2.0'da sorgular için istisna güvenliğini gerçekleştirdik. Bu, yetersiz bellek tahsisinin sorgunun anında iptal edilmesine neden olacağı anlamına gelir; bu da sonraki adımlarda "İptal" durumunu kontrol etme zahmetinden kurtarır.

İşlemde Bellek Sınırı

Doris arka ucu düzenli olarak işlemlerin fiziksel belleğini ve mevcut bellek boyutunu sistemden alır. Bu arada tüm Sorgu/Yükleme/Sıkıştırma görevlerinin MemTracker anlık görüntülerini toplar.


Bir arka uç işlemi bellek sınırını aşarsa veya bellek yetersizse Doris, Önbelleği temizleyerek ve bir dizi sorguyu veya veri alma görevini iptal ederek bellek alanında bir miktar yer açar. Bunlar düzenli olarak ayrı bir GC iş parçacığı tarafından yürütülecektir.



Tüketilen işlem belleği SoftMemLimit'in (varsayılan olarak toplam sistem belleğinin %81'i) üzerindeyse veya kullanılabilir sistem belleği Uyarı Su İşaretinin altına düşerse (3,2 GB'tan az), Küçük GC tetiklenir.


Şu anda, sorgu yürütme, bellek ayırma adımında duraklatılacak, veri alma görevlerinde önbelleğe alınan veriler zorla temizlenecek ve Veri Sayfası Önbelleğinin ve güncel olmayan Segment Önbelleğinin bir kısmı serbest bırakılacak.


Yeni serbest bırakılan bellek, işlem belleğinin %10'unu kaplamıyorsa, Bellek Aşırı Taahhüdü etkinken Doris, %10 hedefi karşılanana veya tüm sorgular iptal edilene kadar en büyük "aşırı taahhütler" olan sorguları iptal etmeye başlayacaktır.


Daha sonra Doris, sistem belleği kontrol aralığını ve GC aralığını kısaltacaktır. Daha fazla bellek mevcut olduğunda sorgulara devam edilecektir.


Tüketilen işlem belleği MemLimit'in (varsayılan olarak toplam sistem belleğinin %90'ı) üzerindeyse veya kullanılabilir sistem belleği Düşük Su İşaretinin (1,6 GB'tan az) altına düşerse, Tam GC tetiklenecektir.


Şu anda, veri alma görevleri durdurulacak ve Veri Sayfası Önbelleğinin tamamı ve diğer Önbelleklerin çoğu serbest bırakılacak.


Tüm bu adımlardan sonra, yeni çıkan bellek işlem belleğinin %20'sini kaplamıyorsa, Doris tüm MemTracker'ları inceleyerek en fazla bellek harcayan sorguları ve alım görevlerini bulacak ve bunları tek tek iptal edecektir.


Yalnızca %20 hedefine ulaşıldıktan sonra sistem belleği kontrol aralığı ve GC aralığı uzatılacak ve sorgular ve alım görevlerine devam edilecektir. (Bir çöp toplama işlemi genellikle yüzlerce μs ila düzinelerce ms sürer.)

Etkiler ve Sonuçlar

Bellek ayırma, bellek izleme ve bellek sınırındaki optimizasyonların ardından, gerçek zamanlı bir analitik veri ambarı platformu olarak Apache Doris'in kararlılığını ve yüksek eşzamanlılık performansını önemli ölçüde artırdık. Arka uçtaki OOM çökmesi artık nadir görülen bir sahne.


Bir OOM olsa bile, kullanıcılar günlüklere dayanarak sorunun kökünü bulabilir ve düzeltebilir. Ayrıca, sorgularda ve veri alımında daha esnek bellek sınırları sayesinde, kullanıcıların bellek alanı yeterli olduğunda bellekle ilgilenmek için ekstra çaba harcamasına gerek kalmaz.


Bir sonraki aşamada, bellek aşırı taahhüdünde sorguların tamamlanmasını sağlamayı planlıyoruz, bu da bellek yetersizliği nedeniyle daha az sorgunun iptal edilmesi gerektiği anlamına geliyor.


Bu hedefi belirli çalışma yönlerine ayırdık: istisna güvenliği, kaynak grupları arasında bellek izolasyonu ve ara verilerin temizleme mekanizması.


Geliştiricilerimizle tanışmak istiyorsanız bizi burada bulabilirsiniz .