Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

Andrey Salnikov'un 2016 yılı başında yazdığı "Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar" raporunun transkripsiyonunu okumanızı öneririm.

Bu raporda uygulama kodunun tasarlanması ve yazılması aşamasında ortaya çıkan uygulamalardaki ana hataları analiz edeceğim. Ve yalnızca Postgresql'de şişkinliğe yol açan hataları alacağım. Kural olarak, bu, bir bütün olarak sisteminizin performansının başlangıcı ve sonudur, ancak başlangıçta bunun için hiçbir önkoşul görülmemektedir.

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

Herkesi ağırlamaktan mutluyum! Bu rapor meslektaşımın önceki raporu kadar teknik değil. Oldukça fazla sayıda müşterimiz olduğundan, bu rapor temel olarak arka uç sistem geliştiricilerine yöneliktir. Ve hepsi aynı hataları yapıyor. Size onlardan bahsedeceğim. Bu hataların ne gibi ölümcül ve kötü sonuçlara yol açtığını anlatacağım.

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

Hatalar neden yapılır? Bunlar iki nedenden dolayı yapılır: rastgele, belki işe yarayacaktır ve veritabanı ile uygulama arasındaki düzeyde ve veritabanının kendisinde meydana gelen bazı mekanizmaların bilgisizliği nedeniyle.

İşlerin ne kadar kötüye gittiğine dair korkunç resimler içeren üç örnek vereceğim. Burada meydana gelen mekanizmayı kısaca anlatacağım. Ve bunlarla nasıl başa çıkılacağı, ne zaman meydana geldiği ve hataları önlemek için hangi önleyici yöntemlerin kullanılması gerektiği. Size yardımcı araçlardan bahsedeceğim ve faydalı bağlantılar vereceğim.

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

İki tablomun olduğu bir test veritabanı kullandım. Bir plakada müşteri hesapları, diğerinde ise bu hesaplardaki işlemler yer alıyor. Ve bu hesaplardaki bakiyeleri belirli aralıklarla güncelliyoruz.

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

Plakanın ilk verileri: oldukça küçük, 2 MB. Veritabanının ve özellikle de işaretin yanıt süresi de çok iyi. Ve oldukça iyi bir yük - plakaya göre saniyede 2 işlem.

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

Ve bu rapor aracılığıyla size grafikler göstereceğim, böylece neler olduğunu net bir şekilde anlayabilirsiniz. Her zaman grafikli 2 slayt olacaktır. İlk slayt sunucuda genel olarak olup bitenleri anlatıyor.

Ve bu durumda gerçekten küçük bir işaretimizin olduğunu görüyoruz. İndeks 2 MB'ta küçüktür. Bu soldaki ilk grafik.

Sunucudaki ortalama yanıt süresi de stabil ve kısadır. Bu sağ üstteki grafik.

Sol alttaki grafik en uzun işlemleri gösteriyor. İşlemlerin hızlı bir şekilde tamamlandığını görüyoruz. Otovakum henüz burada çalışmıyor çünkü bu bir başlangıç ​​testiydi. Çalışmaya devam edecek ve bize faydalı olacaktır.

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

İkinci slayt her zaman test edilen plakaya ayrılacaktır. Bu durumda müşterinin hesap bakiyelerini sürekli olarak güncelleriz. Ve bir güncelleme işlemi için ortalama yanıt süresinin bir milisaniyeden daha kısa bir sürede oldukça iyi olduğunu görüyoruz. İşlemci kaynaklarının da (sağ üstteki grafik bu) eşit miktarda ve oldukça az tüketildiğini görüyoruz.

Sağ alt grafik, güncellemeden önce istediğimiz satırı ararken ne kadar işletim ve disk belleğinden geçtiğimizi gösterir. Ve işarete göre işlem sayısı da başta söylediğim gibi saniyede 2'dir.

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

Ve şimdi bir trajediyle karşı karşıyayız. Bazı nedenlerden dolayı uzun zamandır unutulmuş bir işlem var. Sebeplerin tümü genellikle banaldir:

  • En yaygın olanlardan biri, uygulama kodunda harici bir hizmete erişmeye başlamamızdır. Ve bu hizmet bize cevap vermiyor. Yani bir işlem açtık, veritabanında değişiklik yaptık ve uygulamadan mail okumak için ya da altyapımız dahilindeki başka bir servise geçtik ve bazı nedenlerden dolayı bize yanıt vermiyor. Ve oturumumuz ne zaman çözüleceği bilinmeyen bir durumda kaldı.
  • İkinci durum ise kodumuzda herhangi bir nedenle istisna oluşmasıdır. Ve istisna dışında, işlemin kapatılmasını işleme koymadık. Ve açık bir işlemin olduğu bir askı seansıyla sonuçlandık.
  • Ve sonuncusu da oldukça yaygın bir durumdur. Bu düşük kaliteli koddur. Bazı çerçeveler bir işlem açar. Takılıyor ve uygulamada asılı olduğunu bilmiyor olabilirsiniz.

Bu tür şeyler nereye varır?

Öyle ki tablolarımız ve dizinlerimiz çarpıcı biçimde şişmeye başlıyor. Bu tamamen aynı şişkinlik etkisidir. Veritabanı için bu, veritabanı yanıt süresinin çok keskin bir şekilde artacağı ve veritabanı sunucusu üzerindeki yükün artacağı anlamına gelecektir. Sonuç olarak uygulamamız zarar görecektir. Çünkü kodunuzda veritabanına yapılan bir istek için 10 milisaniye, mantığınızda 10 milisaniye harcadıysanız, fonksiyonunuzun tamamlanması 20 milisaniye sürdü. Ve şimdi durumunuz çok üzücü olacak.

Ve bakalım ne olacak. Sol alt grafik uzun bir işlemimizin olduğunu gösteriyor. Ve sol üstteki grafiğe baktığımızda tablomuzun boyutunun bir anda 300 megabayttan XNUMX megabayta sıçradığını görüyoruz. Aynı zamanda tablodaki veri miktarı da değişmedi, yani. orada oldukça fazla miktarda çöp var.

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

Ortalama sunucu yanıt süresine ilişkin genel durum da birkaç kat değişti. Yani sunucudaki tüm istekler tamamen düşmeye başladı. Aynı zamanda, bir şeyler yapmaya çalışan ve kaynakları tüketen otovakum şeklinde dahili Postgres süreçleri başlatıldı.

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

Tabelamızda neler oluyor? Aynısı. İşarete göre ortalama yanıt süremiz birkaç kat arttı. Özellikle tüketilen kaynaklar açısından işlemcinin üzerindeki yükün oldukça arttığını görüyoruz. Bu sağ üstteki grafik. Ve arttı çünkü işlemci, ihtiyaç duyulan satırı bulmak için bir sürü işe yaramaz satırı sıralamak zorunda kalıyor. Bu sağ alttaki grafik. Sonuç olarak, saniye başına çağrı sayımız önemli ölçüde düşmeye başladı çünkü veritabanının aynı sayıda isteği işleyecek zamanı yoktu.

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

Hayata dönmemiz lazım. İnternete giriyoruz ve uzun işlemlerin sorunlara yol açtığını öğreniyoruz. Bu işlemi bulup öldürüyoruz. Ve bizim için her şey normalleşiyor. Her şey olması gerektiği gibi çalışıyor.

Sakinleştik ama bir süre sonra uygulamanın acil durum öncesindeki gibi çalışmadığını fark etmeye başladık. İstekler hâlâ daha yavaş ve önemli ölçüde daha yavaş işleniyor. Benim örneğimde özellikle bir buçuk ila iki kat daha yavaş. Sunucu üzerindeki yük de kaza öncesine göre daha yüksek.

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

Ve şu soru: "Şu anda üsse ne oluyor?" Ve tabanda aşağıdaki durum ortaya çıkıyor. İşlem grafiğinde bunun durduğunu ve gerçekte uzun vadeli işlem olmadığını görebilirsiniz. Ancak kaza sırasında tabelanın boyutu ölümcül derecede arttı. Ve o zamandan beri azalmadılar. Üssün ortalama süresi sabitlendi. Ve cevaplar bizim için kabul edilebilir bir hızda yeterince geliyor gibi görünüyor. Otomatik vakum daha aktif hale geldi ve işaretle ilgili bir şeyler yapmaya başladı çünkü daha fazla veriyi elemesi gerekiyor.

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

Özellikle, dengeleri değiştirdiğimiz hesapların bulunduğu test plakasına göre: bir isteğin yanıt süresi normale dönmüş gibi görünüyor. Ancak gerçekte bir buçuk kat daha yüksektir.

İşlemci üzerindeki yükten de, işlemci üzerindeki yükün çökmeden önce gerekli değere dönmediğini görüyoruz. Ve bunun nedenleri tam olarak sağ alt grafikte yatıyor. Orada belli miktarda hafızanın arandığı görülüyor. Yani gerekli satırı bulmak için gereksiz verileri ayıklayarak veritabanı sunucusunun kaynaklarını boşa harcıyoruz. Saniyedeki işlem sayısı sabitlendi.

Genel olarak iyi ama durum eskisinden daha kötü. Bu veritabanıyla çalışan uygulamamızın bir sonucu olarak veritabanı bozulmasını temizleyin.

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

Ve orada neler olduğunu anlamak için, eğer önceki raporda değilseniz, şimdi küçük bir teoriye bakalım. İç süreç teorisi. Neden araba süpürgesi ve ne işe yarar?

Anlamak için kısaca. Bir noktada bir masamız var. Tabloda satırlarımız var. Bu çizgiler aktif, canlı ve şu anda ihtiyacımız olan şey olabilir. Resimde yeşil renkle işaretlenmiştir. Ve halihazırda üzerinde çalışılmış, güncellenmiş ve üzerlerinde yeni girişler ortaya çıkan son tarihler var. Ve artık veritabanı için ilgi çekici olmadıkları işaretlendi. Ancak Postgres özelliği nedeniyle tabloda yer alıyorlar.

Neden bir araba süpürgesine ihtiyacınız var? Bir noktada autovacuum gelir, veritabanına erişir ve sorar: "Lütfen bana şu anda veritabanında açık olan en eski işlemin kimliğini ver." Veritabanı bu kimliği döndürür. Ve otovakum ona güvenerek tablodaki çizgileri sıralıyor. Ve eğer bazı satırların çok daha eski işlemler tarafından değiştirildiğini görürse, o zaman onları oraya yeni veriler yazarak gelecekte yeniden kullanabileceğimiz satırlar olarak işaretleme hakkına sahiptir. Bu bir arka plan sürecidir.

Şu anda veritabanıyla çalışmaya ve tabloda bazı değişiklikler yapmaya devam ediyoruz. Ve tekrar kullanabileceğimiz bu satırlara yeni veriler yazıyoruz. Ve böylece bir döngü elde ederiz, yani orada her zaman bazı ölü eski çizgiler belirir, onların yerine ihtiyacımız olan yeni satırları yazıyoruz. Ve bu PostgreSQL'in çalışması için normal bir durumdur.

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

Kaza sırasında ne oldu? Orada bu süreç nasıl gerçekleşti?

Bazı durumlarında bir tabelamız vardı, bazısı canlı, bazısı ölü hatlar. Araba süpürgesi geldi. Veritabanına en eski işlemimizin ne olduğunu ve kimliğinin ne olduğunu sordu. Bu kimliği saatler önce, belki on dakika önce aldım. Veritabanınızdaki yükün ne kadar ağır olduğuna bağlıdır. Ve yeniden kullanılmış olarak işaretleyebileceği çizgiler aramaya başladı. Ve masamızda böyle çizgiler bulamadım.

Ancak şu anda masayla çalışmaya devam ediyoruz. İçinde bir şeyler yapıyoruz, güncelliyoruz, verileri değiştiriyoruz. Veritabanı şu anda ne yapmalıdır? Mevcut tablonun sonuna yeni satırlar eklemekten başka seçeneği yoktur. Ve böylece masa boyutumuz şişmeye başlıyor.

Gerçekte çalışmak için yeşil hatlara ihtiyacımız var. Ancak böyle bir sorun sırasında tüm tablodaki yeşil çizgilerin yüzdesinin son derece düşük olduğu ortaya çıkıyor.

Ve bir sorgu yürüttüğümüzde, veritabanının istenen satırı bulmak için tüm satırlardan geçmesi gerekir: hem kırmızı hem de yeşil. Ve bir tablonun gereksiz verilerle şişirilmesi etkisine "şişme" denir ve bu da disk alanımızı da tüketir. 2 MB'tı, 300 MB oldu hatırlıyor musunuz? Şimdi megabaytı gigabayta değiştirin; tüm disk kaynaklarınızı hızla kaybedersiniz.

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

Bizim için ne gibi sonuçlar olabilir?

  • Örneğimde tablo ve dizin 150 kat büyüdü. Müşterilerimizden bazılarının disk alanı tükenmeye başladığında daha ölümcül vakalar yaşandı.
  • Tabloların boyutu hiçbir zaman azalmayacaktır. Bazı durumlarda Autovacuum, yalnızca ölü çizgiler varsa tablanın kuyruğunu kesebilir. Ancak sürekli bir dönüş olduğu için, bir yeşil çizgi en sonunda donabilir ve güncellenmeyebilir, diğerleri ise plakanın başında bir yere yazılacaktır. Ancak bu o kadar beklenmedik bir olay ki, masanızın kendisi de küçülecek, bu yüzden bunu umut etmemelisiniz.
  • Veritabanının bir sürü işe yaramaz satırı sıralaması gerekiyor. Ve disk kaynaklarını israf ediyoruz, işlemci kaynaklarını ve elektriği israf ediyoruz.
  • Ve bu uygulamamızı doğrudan etkiliyor, çünkü başlangıçta istek için 10 milisaniye, kodumuza 10 milisaniye harcadık, sonra çökme sırasında istek için bir saniye ve kod için 10 milisaniye harcamaya başladık, yani bir emir. uygulama performansındaki büyüklük azaldı. Ve kaza çözülünce 20 milisaniyeyi istek üzerine, 10 milisaniyeyi kod için harcamaya başladık. Bu, üretkenliğimizin hala bir buçuk kat düştüğü anlamına geliyor. Ve bunların hepsi belki de bizim hatamız yüzünden donan bir işlem yüzünden oldu.
  • Ve şu soru: "Her şeyi nasıl geri alabiliriz?" Böylece her şey yolunda gider ve talepler kazadan önceki kadar çabuk gelir.

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

Bu amaçla gerçekleştirilen belirli bir çalışma döngüsü vardır.

Öncelikle şişirilmiş sorunlu tabloları bulmamız gerekiyor. Bazı tablolarda kaydın daha aktif, bazılarında ise daha az aktif olduğunu anlıyoruz. Ve bunun için uzantıyı kullanıyoruz pgstattuple. Bu uzantıyı yükleyerek oldukça şişirilmiş tabloları bulmanıza yardımcı olacak sorgular yazabilirsiniz.

Bu tabloları bulduktan sonra sıkıştırmanız gerekir. Bunun için zaten araçlar var. Şirketimizde üç araç kullanıyoruz. Birincisi yerleşik VAKUM DOLU'dur. Zalim, sert ve acımasızdır ama bazen çok faydalıdır. Pg_repack и kompakt - Bunlar tabloları sıkıştırmak için kullanılan üçüncü taraf yardımcı programlardır. Ve veritabanına daha dikkatli davranıyorlar.

Sizin için neyin daha uygun olduğuna bağlı olarak kullanılırlar. Ama bunu size en sonunda anlatacağım. Önemli olan üç aracın olmasıdır. Aralarından seçim yapabileceğiniz çok şey var.

Her şeyi düzelttikten ve her şeyin yolunda olduğundan emin olduktan sonra, gelecekte bu durumu nasıl önleyeceğimizi bilmeliyiz:

  • Oldukça kolay bir şekilde önlenebilir. Ana sunucudaki oturumların süresini izlemeniz gerekir. İşlem durumunda boştayken özellikle tehlikeli oturumlar. Bunlar, yeni bir işlem açan, bir şey yapan ve ayrılan veya sadece asılı kalan, kodun içinde kaybolan kişilerdir.
  • Geliştiriciler olarak sizin için bu durumlar ortaya çıktığında kodunuzu test etmeniz önemlidir. Bunu yapmak zor değil. Bu yararlı bir kontrol olacaktır. Uzun işlemlerle ilgili çok sayıda "çocukça" sorundan kaçınacaksınız.

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

Bu grafiklerde bu örnekte VACUUM FULL işaretinden geçtikten sonra işaretin ve veritabanının davranışının nasıl değiştiğini göstermek istedim. Bu benim için üretim değil.

Tablo boyutu hemen birkaç megabaytlık normal çalışma durumuna geri döndü. Bu, sunucunun ortalama yanıt süresini büyük ölçüde etkilemedi.

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

Ancak özellikle hesap bakiyelerini güncellediğimiz test tabelamız için, tabeladaki verileri güncelleme talebine verilen ortalama yanıt süresinin acil durum öncesi seviyelere düştüğünü görüyoruz. İşlemcinin bu isteği tamamlamak için tükettiği kaynaklar da çökme öncesi seviyelere düştü. Ve sağ alttaki grafik, artık tam olarak ihtiyacımız olan çizgiyi, tablo sıkıştırılmadan önce orada bulunan ölü çizgi yığınlarının üzerinden geçmeden, hemen bulduğumuzu gösteriyor. Ortalama talep süresi ise yaklaşık olarak aynı seviyede kaldı. Ancak burada donanımımda bir hata var.

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

İlk hikayenin bittiği yer burası. En yaygın olanıdır. Ve bu, müşterinin deneyimine ve programcıların ne kadar nitelikli olduğuna bakılmaksızın herkesin başına gelir. Er ya da geç bu olur.

Yükü dağıttığımız ve sunucu kaynaklarını optimize ettiğimiz ikinci hikaye

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

  • Biz zaten büyüdük ve ciddi adamlar olduk. Ve bir kopyaya sahip olduğumuzu ve yükü dengelemenin bizim için iyi olacağını anlıyoruz: Master'a yazın ve kopyadan okuyun. Ve genellikle bu durum bazı raporlar veya ETL hazırlamak istediğimizde ortaya çıkar. İş dünyası da bundan çok memnun. Gerçekten çok sayıda karmaşık analitik içeren çeşitli raporlar istiyor.
  • Karmaşık analizler milisaniyelerle hesaplanamadığından raporlar saatler alır. Biz cesur adamlar gibi kod yazarız. Ekleme uygulamasında Master'a kayıt yapıyoruz ve raporları replika üzerinde çalıştırıyoruz.
  • Yükün dağıtılması.
  • Her şey mükemmel çalışıyor. Biz harikayız.

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

Peki bu durum neye benziyor? Bu grafiklere özellikle işlem süresi için replikadan işlem süresini de ekledim. Diğer tüm grafikler yalnızca Ana sunucuya ilişkindir.

Bu zamana kadar rapor panom büyümüştü. Daha fazlası da var. Ortalama sunucu yanıt süresinin stabil olduğunu görüyoruz. Replika üzerinde 2 saat süren uzun süreli bir işlemimizin olduğunu görüyoruz. Ölü çizgileri işleyen otovakumun sessiz çalışmasını görüyoruz. Ve bizim için her şey yolunda.

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

Özellikle test edilen plakaya göre oradaki hesap bakiyelerini güncellemeye devam ediyoruz. Ayrıca istekler için istikrarlı bir yanıt süremiz ve istikrarlı kaynak tüketimimiz var. Bizim için her şey yolunda.

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

Çoğaltmayla ilgili bir çelişki nedeniyle bu raporların geri tepmeye başladığı ana kadar her şey yolunda. Ve düzenli aralıklarla karşılık veriyorlar.

İnternete girip bunun neden olduğunu okumaya başlıyoruz. Ve bir çözüm buluyoruz.

İlk çözüm çoğaltma gecikmesini artırmaktır. Raporumuzun 3 saat çalıştığını biliyoruz. Çoğaltma gecikmesini 3 saate ayarladık. Her şeyi başlatıyoruz ancak raporların bazen iptal edilmesiyle ilgili sorun yaşamaya devam ediyoruz.

Her şeyin mükemmel olmasını istiyoruz. Daha da tırmanıyoruz. İnternette harika bir ayar bulduk - hot_standby_feedback. Hadi açalım. Hot_standby_feedback, Master üzerindeki otomatik vakumu durdurmamızı sağlar. Böylece replikasyon çakışmalarından tamamen kurtulmuş oluyoruz. Ve raporlarla bizim için her şey yolunda gidiyor.

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

Peki şu anda Ana sunucuda neler oluyor? Ve Master sunucuyla başımız tamamen belada. Şimdi bu ayarların her ikisini de etkinleştirdiğimde grafikleri görüyoruz. Ve kopyamızdaki oturumun bir şekilde Ana sunucudaki durumu etkilemeye başladığını görüyoruz. Bir etkisi var çünkü otomatik vakumu duraklattı, bu da son çizgileri ortadan kaldırıyor. Masa büyüklüğümüz yine arttı. Veritabanının tamamındaki ortalama sorgu yürütme süresi de hızla arttı. Otomatik vakumlar biraz sıkılaştı.

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

Spesifik olarak bizim tabağımızdan, üzerindeki veri güncellemesinin de göklere sıçradığını görüyoruz. CPU tüketimi de benzer şekilde büyük ölçüde arttı. Yine çok sayıda ölü, işe yaramaz çizgiden geçiyoruz. Ve bu işarete yanıt verme süresi ve işlem sayısı düştü.

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

Daha önce neden bahsettiğimi bilmiyorsak nasıl görünecek?

  • Sorun aramaya başlıyoruz. İlk bölümde sorunla karşılaştıysak bunun uzun bir işlemden kaynaklanabileceğini biliyor ve ustaya gidiyoruz. Usta'yla ilgili bir sorunumuz var. Onu sosisler. Isınıyor, Yük Ortalaması yüz civarında.
  • Oradaki talepler yavaş ama uzun süredir devam eden herhangi bir işlem göremiyoruz. Ve sorunun ne olduğunu anlamıyoruz. Nereye bakacağımızı anlamıyoruz.
  • Sunucu ekipmanlarını kontrol ediyoruz. Belki baskınımız başarısız oldu. Belki hafızamız yanmıştır. Evet, her şey olabilir. Ama hayır, sunucular yeni, her şey yolunda çalışıyor.
  • Herkes koşuyor: yöneticiler, geliştiriciler ve yönetmen. Hiç bir şey yardımcı olmaz.
  • Ve bir noktada her şey birdenbire kendi kendine düzelmeye başlar.

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

Şu anda kopyamızdaki istek işlendi ve bırakıldı. Raporu aldık. İş dünyası hala mutlu. Gördüğünüz gibi tabelamız yeniden büyüdü ve küçülmeyecek. Seansların olduğu grafikte, bu uzun işlemin bir kopyasını bir kopyadan bıraktım, böylece durum istikrara kavuşana kadar ne kadar süreceğini tahmin edebilirsiniz.

Oturum bitti. Ve ancak bir süre sonra sunucu aşağı yukarı sırayla gelir. Ve Ana sunucudaki isteklerin ortalama yanıt süresi normale döner. Çünkü sonunda otovakum bu son çizgileri temizleme ve işaretleme olanağına sahip. Ve işini yapmaya başladı. Ve bunu ne kadar çabuk yapıyor, o kadar çabuk düzene gireceğiz.

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

Hesap bakiyelerini güncellediğimiz test edilen tablete göre tamamen aynı tabloyu görüyoruz. Ortalama hesap güncelleme süresi de giderek normalleşiyor. İşlemcinin tükettiği kaynaklar da azalır. Saniyedeki işlem sayısı da normale dönüyor. Ama yine normale döndük, kazadan öncekiyle aynı değil.

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

Her durumda, ilk durumda olduğu gibi bir buçuk ila iki kat ve bazen daha fazla performans düşüşü yaşıyoruz.

Her şeyi doğru yapmış gibiyiz. Yükü dağıtın. Ekipman boşta değil. İstekleri kafamıza göre böldük ama yine de her şey kötü sonuçlandı.

  • hot_standby_feedback'i etkinleştirmiyor musunuz? Evet, özellikle güçlü nedenler olmadan açılması önerilmez. Çünkü bu bükülme doğrudan Master sunucuyu etkiliyor ve oradaki otovakumun çalışmasını askıya alıyor. Bunu bazı kopyalarda etkinleştirip unutarak, Master'ı öldürebilir ve uygulamada büyük sorunlar yaşayabilirsiniz.
  • max_standby_streaming_delay artırılsın mı? Evet, raporlara göre bu doğrudur. Üç saatlik bir raporunuz varsa ve çoğaltma çakışmaları nedeniyle çökmesini istemiyorsanız, gecikmeyi artırmanız yeterlidir. Uzun vadeli bir rapor hiçbir zaman veritabanına şu anda ulaşan verilere ihtiyaç duymaz. Üç saat boyunca elinizde varsa, onu eski bir veri dönemi boyunca çalıştırıyorsunuz demektir. Ve sizin için üç saatlik bir gecikme ya da altı saatlik bir gecikme fark etmeyecek, ancak raporları tutarlı bir şekilde alacak ve düşme konusunda herhangi bir sorun yaşamayacaksınız.
  • Doğal olarak, özellikle bir kopyada hot_standby_feedback'i etkinleştirmeye karar verirseniz, kopyalar üzerindeki uzun oturumları kontrol etmeniz gerekir. Çünkü her şey olabilir. İstekleri test edebilmesi için bu kopyayı geliştiriciye verdik. Çılgın bir istek yazdı. Onu çalıştırdı ve çay içmeye gitti ve biz de yerleşik Üstad'ı bulduk. Ya da belki oraya yanlış uygulamayı koymuşuzdur. Durumlar çeşitlidir. Replikalardaki oturumlar Ana Oturumdaki kadar dikkatli izlenmelidir.
  • Replikalar üzerinde hızlı ve uzun sorgularınız varsa, bu durumda yükü dağıtmak için bunları bölmek daha iyidir. Bu,stream_delay'e bir bağlantıdır. Hızlı olanlar için, küçük bir çoğaltma gecikmesine sahip bir kopya bulundurun. Uzun süren raporlama istekleri için 6 saat veya bir gün gecikmeli olabilen bir replika bulundurun. Bu tamamen normal bir durumdur.

Sonuçları aynı şekilde ortadan kaldırıyoruz:

  • Şişirilmiş masalar buluyoruz.
  • Ve bunu bize uygun olan en uygun araçla sıkıştırıyoruz.

İkinci hikaye burada bitiyor. Gelelim üçüncü hikayeye.

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

Ayrıca göç yaptığımız yerler bizim için oldukça yaygın.

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

  • Herhangi bir yazılım ürünü büyüyor. Bunun gereksinimleri değişiyor. Her durumda gelişmek istiyoruz. Ve öyle oluyor ki, tablodaki verileri güncellememiz gerekiyor, yani geliştirmemizin bir parçası olarak sunduğumuz yeni işlevsellik için geçişimiz açısından bir güncelleme çalıştırmamız gerekiyor.
  • Eski veri formatı tatmin edici değil. Şimdi bu hesaplarda işlemlerimin olduğu ikinci tabloya geçelim diyelim. Diyelim ki ruble cinsindendiler ve doğruluğu artırmaya ve kopek cinsinden yapmaya karar verdik. Bunun için de bir güncelleme yapmamız gerekiyor: İşlem tutarını içeren alanı yüz ile çarpın.
  • Günümüz dünyasında otomatikleştirilmiş veritabanı sürüm kontrol araçlarını kullanıyoruz. Diyelimki likibaz. Göçümüzü oraya kaydediyoruz. Bunu test üssümüzde test ediyoruz. Herşey yolunda. Güncelleme devam ediyor. Bir süre çalışmayı engelliyor ama güncel veriler alıyoruz. Ve bu konuda yeni işlevler başlatabiliriz. Her şey test edildi ve kontrol edildi. Her şey onaylandı.
  • Planlı çalışmalar yaptık ve göçü gerçekleştirdik.

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

İşte karşınıza sunulan güncelleme ile geçiş. Bunlar benim hesap hareketlerim olduğu için plaka 15 GB idi. Ve her satırı güncellediğimiz için güncellemeyle tablonun boyutunu iki katına çıkardık, çünkü her satırı yeniden yazdık.

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

Geçiş sırasında bu plakayla hiçbir şey yapamadık çünkü ona yönelik tüm istekler sıraya alındı ​​ve bu güncelleme tamamlanana kadar beklendi. Ancak burada dikey eksende yer alan sayılara dikkatinizi çekmek istiyorum. Yani, geçiş öncesi ortalama istek süremiz yaklaşık 5 milisaniyedir ve işlemci yükü, disk belleğini okumaya yönelik blok işlem sayısı 7,5'ten azdır.

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

Taşıma işlemini gerçekleştirdik ve yine sorun yaşadık.

Taşıma başarılı oldu ancak:

  • Eski işlevselliğin tamamlanması artık daha uzun sürüyor.
  • Masa yeniden büyüdü.
  • Sunucudaki yük yine eskisinden daha fazla oldu.
  • Ve tabii ki hala iyi çalışan işlevsellik üzerinde çalışıyoruz, onu biraz geliştirdik.

Ve bu yine hayatımızı mahveden şişkinliktir.

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

Burada tablonun önceki iki durumda olduğu gibi önceki boyutlarına dönmeyeceğini gösteriyorum. Ortalama sunucu yükü yeterli görünüyor.

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

Ve hesapların olduğu tabloya dönersek bu tablo için ortalama istek süresinin iki katına çıktığını göreceğiz. İşlemci üzerindeki yük ve bellekte sıralanan satır sayısı 7,5'un üzerine çıktı ancak daha düşüktü. İşlemcilerde ise 2 kat, blok işlemlerde ise 1,5 kat sıçradı yani sunucu performansında düşüş yaşadık. Ve sonuç olarak uygulamamızın performansında bozulma. Aynı zamanda çağrı sayıları da yaklaşık olarak aynı seviyede kaldı.

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

Ve burada asıl önemli olan bu tür geçişlerin nasıl doğru şekilde yapılacağını anlamaktır. Ve bunların yapılması gerekiyor. Bu geçişleri oldukça tutarlı bir şekilde yapıyoruz.

  • Bu kadar büyük göçler otomatik olarak gerçekleşmez. Her zaman kontrol altında olmaları gerekir.
  • Bilgili bir kişinin denetimi gereklidir. Ekibinizde bir DBA varsa bırakın bunu DBA yapsın. Bu onun işi. Değilse, veritabanlarıyla nasıl çalışılacağını bilen en deneyimli kişinin yapmasına izin verin.
  • Yeni bir veritabanı şeması, bir sütunu güncellesek bile her zaman aşamalı olarak, yani uygulamanın yeni sürümü yayınlanmadan önce hazırlarız:
  • Güncellenen verileri kaydedeceğimiz yeni alanlar eklendi.
  • Eski alandaki verileri küçük parçalar halinde yeni alana aktarıyoruz. Bunu neden yapıyoruz? Öncelikle bu sürecin sürecini her zaman kontrol ediyoruz. Zaten çok sayıda partiyi aktardığımızı ve elimizde çok sayıda partinin kaldığını biliyoruz.
  • Ve ikinci olumlu etki, bu tür her parti arasında işlemi kapattığımız, yeni bir tane açtığımız ve bu, otovakumun plakaya göre çalışmasına, yeniden kullanım için son satırları işaretlemesine olanak sağlamasıdır.
  • Uygulama çalışırken çıkacak satırlar için (eski uygulamamız hala çalışıyor) yeni alanlara yeni değerler yazan bir tetikleyici ekliyoruz. Bizim durumumuzda bu, eski değerin yüz katıyla çarpılmasıdır.
  • Tamamen inatçıysak ve aynı alanı istiyorsak, tüm geçişleri tamamladıktan sonra ve uygulamanın yeni bir sürümünü kullanıma sunmadan önce alanları yeniden adlandırmanız yeterlidir. Eski olanlara icat edilmiş bir isim verilir ve yeni alanlar eski olanlarla yeniden adlandırılır.
  • Ve ancak bundan sonra uygulamanın yeni bir sürümünü başlatıyoruz.

Aynı zamanda şişkinlik yaşamayacağız ve performans açısından sıkıntı yaşamayacağız.

Üçüncü hikaye de burada bitiyor.

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

https://github.com/dataegret/pg-utils/blob/master/sql/table_bloat.sql

https://github.com/dataegret/pg-utils/blob/master/sql/table_bloat_approx.sql

Ve şimdi ilk hikayede bahsettiğim araçlar hakkında biraz daha detay.

Bloat'ı aramadan önce uzantıyı yüklemelisiniz pgstattuple.

Soru sormanıza gerek kalmaması için bu sorguları çalışmamızda zaten yazdık. Bunları kullanabilirsiniz. Burada iki istek var.

  • İlkinin çalışması oldukça uzun sürüyor ancak tablodaki şişkinlik değerlerini tam olarak gösterecek.
  • İkincisi daha hızlı çalışır ve tabloya göre şişkinlik olup olmadığını hızlı bir şekilde değerlendirmeniz gerektiğinde çok etkilidir. Ayrıca Postgres tablosunda şişkinliğin her zaman mevcut olduğunu da anlamalısınız. Bu MVCC modelinin bir özelliğidir.
  • Çoğu durumda masalarda %20 şişkinlik normaldir. Yani endişelenmemeli ve bu tabloyu sıkıştırmamalısınız.

İşe yaramaz verilerle dolup taşan tabloları nasıl tespit edeceğimizi bulduk.

Şimdi şişkinliği nasıl düzelteceğiniz hakkında:

  • Küçük bir tabletimiz ve iyi disklerimiz varsa, yani gigabayta kadar bir tablette VACUUM FULL'u kullanmak oldukça mümkündür. Birkaç saniyeliğine sizden masaya özel bir kilit alacak ve tamam, ama her şeyi hızlı ve sert bir şekilde yapacak. VAKUM DOLU ne işe yarar? Tabloya özel bir kilit alır ve eski tablolardaki canlı satırları yeni tabloya yeniden yazar. Ve sonunda onların yerini alır. Eski dosyaları siler ve eskileri yenileriyle değiştirir. Ancak çalışması süresince masaya özel bir kilit gerekir. Bu, bu tabloyla hiçbir şey yapamayacağınız anlamına gelir: ne ona yazın, ne okuyun, ne de değiştirin. Ve VACUUM FULL, veri yazmak için ek disk alanı gerektirir.
  • Sonraki araç pg_repack. Prensip olarak VACUUM FULL'a çok benzer çünkü aynı zamanda verileri eski dosyalardan yenilerine yeniden yazar ve bunları tabloda değiştirir. Ancak aynı zamanda, işinin en başında masaya özel bir kilit almaz, ancak dosyaları değiştirmek için yalnızca hazır verilere sahip olduğu anda alır. Disk kaynağı gereksinimleri VACUUM FULL'un gereksinimlerine benzer. Ek disk alanına ihtiyacınız vardır ve terabaytlık tablolarınız varsa bu bazen kritik öneme sahiptir. Ve I/O ile aktif olarak çalıştığı için işlemciye oldukça aç.
  • Üçüncü yardımcı program ise kompakt. Biraz farklı prensiplere göre çalıştığı için kaynaklar konusunda daha dikkatlidir. Pgcompacttable'ın ana fikri, tablodaki güncellemeleri kullanarak tüm canlı satırları tablonun başına taşımasıdır. Ve sonra bu masa üzerinde bir boşluk oluşuyor çünkü biliyoruz ki başlangıçta canlı satırlarımız ve sonunda ölü satırlarımız var. Ve vakumun kendisi bu kuyruğu keser, yani. çok fazla ek disk alanı gerektirmez. Ve aynı zamanda kaynaklar açısından hâlâ sıkıştırılabilir durumda.

Aletlerle her şey.

Postgresql'de şişkinliğe yol açan uygulamalardaki tipik hatalar. Andrey Salnikov

Eğer şişkinlik konusunu daha derinlere inmek açısından ilginç buluyorsanız, işte bazı yararlı bağlantılar:

Geliştiriciler için daha çok bir korku hikayesi göstermeye çalıştım, çünkü onlar bizim doğrudan veritabanlarımızdır ve eylemlerin neye ve hangi eylemlere yol açtığını anlamaları gerekir. Umarım başardım. İlginiz için teşekkür ederiz!

sorular

Rapor için teşekkürler! Sorunları nasıl tanımlayabileceğinizi anlattınız. Nasıl uyarılabilirler? Yani isteklerin yalnızca bazı harici hizmetlere eriştikleri için askıda kaldığı bir durumla karşılaştım. Bunlar sadece bazı çılgın birleşmelerdi. Bir gün boyunca ortalıkta dolaşan bazı küçük, zararsız istekler vardı ve sonra saçma sapan şeyler yapmaya başladılar. Yani sizin tarif ettiğinize çok benzer. Bu nasıl takip edilir? Oturup sürekli hangi isteğin sıkışıp kaldığını mı izliyorsunuz? Bu nasıl önlenebilir?

Bu durumda bu, DBA'nın değil, şirketinizin yöneticilerinin görevidir.

Ben bir yöneticiyim.

PostgreSQL, sarkan sorguları gösteren pg_stat_activity adlı bir görünüme sahiptir. Ve orada ne kadar süre asılı kaldığını görebilirsiniz.

Her 5 dakikada bir gelip bakmak zorunda mıyım?

Cron'u kurun ve kontrol edin. Uzun vadeli bir isteğiniz varsa mektup yazın, bu kadar. Yani gözlerinizle bakmanıza gerek yok, otomatikleştirilebilir. Bir mektup alacaksınız, ona tepki vereceksiniz. Veya otomatik olarak çekim yapabilirsiniz.

Bunun olmasının bariz nedenleri var mı?

Bazılarını listeledim. Diğer daha karmaşık örnekler. Ve uzun süre sohbet edilebilir.

Rapor için teşekkürler! pg_repack yardımcı programı hakkında açıklama yapmak istedim. Eğer özel bir kilitleme yapmazsa, o zaman...

Özel bir kilit yapıyor.

... o zaman potansiyel olarak verileri kaybedebilirim. Başvurumun bu süre zarfında hiçbir şey kaydetmemesi mi gerekiyor?

Hayır, tabloyla sorunsuz çalışır, yani pg_repack ilk olarak mevcut tüm canlı hatları aktarır. Doğal olarak orada da masaya bir çeşit giriş oluyor. Sadece bu at kuyruğunu atıyor.

Yani sonunda bunu gerçekten yapıyor mu?

Sonunda bu dosyaları değiştirmek için özel bir kilit alır.

VACUUM FULL'dan daha mı hızlı olacak?

VACUUM FULL, başlar başlamaz hemen özel bir kilit aldı. Ve her şeyi yapana kadar gitmesine izin vermeyecek. Ve pg_repack yalnızca dosya değiştirme sırasında özel bir kilit alır. Şu anda oraya yazmayacaksınız ama veriler kaybolmayacak, her şey yoluna girecek.

Merhaba! Araba süpürgesinin çalışmasından bahsettiniz. Kırmızı, sarı ve yeşil kayıt hücrelerinin olduğu bir grafik vardı. Yani sarı olanları silinmiş olarak işaretledi. Ve sonuç olarak bunlara yeni bir şeyler yazılabilir mi?

Evet. Postgres satırları silmez. Böyle bir özelliği var. Bir satırı güncellediysek eskisini silindi olarak işaretledik. Bu satırı değiştiren işlemin id'si orada beliriyor ve yeni bir satır yazıyoruz. Ve potansiyel olarak bunları okuyabilecek oturumlarımız var. Bir noktada oldukça yaşlanırlar. Ve otovakumun çalışma şeklinin özü, bu çizgilerin üzerinden geçerek onları gereksiz olarak işaretlemesidir. Ve oradaki verilerin üzerine yazabilirsiniz.

Anladım. Ancak sorunun konusu bu değil. Bitirmedim. Diyelim ki bir masamız var. Değişken büyüklükte alanlara sahiptir. Ve yeni bir şey eklemeye çalışırsam, eski hücreye sığmayabilir.

Hayır, her durumda hattın tamamı orada güncellenir. Postgres'in iki veri depolama modeli vardır. Veri türünden seçim yapar. Doğrudan tabloda saklanan veriler var ve ayrıca tos verileri de var. Bunlar büyük miktarda veridir: metin, json. Ayrı plakalarda saklanırlar. Ve bu tabletlere göre şişkinlik ile aynı hikaye yaşanıyor, yani. her şey aynı. Sadece ayrı ayrı listelenirler.

Rapor için teşekkürler! Süreyi sınırlamak için ifade zaman aşımı sorgularının kullanılması kabul edilebilir mi?

Çok kabul edilebilir. Bunu her yerde kullanıyoruz. Ve kendi hizmetlerimiz olmadığı için uzaktan destek sağlıyoruz, oldukça çeşitli müşterilerimiz var. Ve herkes bundan tamamen memnun. Yani kontrol eden cron işlerimiz var. Seansların süresi yalnızca danışanla kararlaştırılır, bundan önce anlaşamayız. Bir dakika da olabilir, 10 dakika da olabilir. Tabandaki yüke ve amacına bağlıdır. Ama hepimiz pg_stat_activity kullanıyoruz.

Rapor için teşekkürler! Raporunuzu uygulamalarıma uygulamaya çalışıyorum. Ve öyle görünüyor ki her yerde bir işlem başlatıyoruz ve bunu her yerde açıkça tamamlıyoruz. Bir istisna varsa, geri alma yine de gerçekleşir. Ve sonra düşünmeye başladım. Sonuçta işlem açıkça başlamayabilir. Bu muhtemelen kıza bir ipucudur. Bir kaydı güncellersem işlem PostgreSQL'de başlayacak ve yalnızca bağlantı kesildiğinde tamamlanacak mı?

Şimdi uygulama düzeyinden bahsediyorsanız, bu, kullandığınız sürücüye ve kullanılan ORM'ye bağlıdır. Orada bir sürü ayar var. Otomatik taahhüdü etkinleştirdiyseniz, orada bir işlem başlar ve hemen kapanır.

Yani güncellemeden hemen sonra mı kapanıyor?

Ayarlara bağlıdır. Bir ayarın adını verdim. Bu otomatik taahhüttür. Oldukça yaygındır. Etkinleştirilmişse, işlem açılmış ve kapatılmıştır. Açıkça "işlemi başlat" ve "işlemi sonlandır" deyip oturumda bir istek başlatmadığınız sürece.

Merhaba! Rapor için teşekkürler! Diyelim ki şişen ve şişen bir veritabanımız var ve daha sonra sunucudaki alan doluyor. Bu durumu düzeltecek herhangi bir araç var mı?

Sunucudaki alanın uygun şekilde izlenmesi gerekir.

Örneğin, DBA çay içmeye gitti, tatil köyüne gitti vs.

Bir dosya sistemi oluşturulduğunda en azından verilerin yazılmadığı bir tür yedekleme alanı oluşturulur.

Ya tamamen sıfırın altındaysa?

Orada buna ayrılmış alan denir, yani. serbest bırakılabilir ve ne kadar büyük oluşturulduğuna bağlı olarak boş alan elde edersiniz. Varsayılan olarak kaç tane olduğunu bilmiyorum. Başka bir durumda, diskleri teslim edin, böylece rekonstrüktif bir operasyon gerçekleştirmek için yeriniz olur. İhtiyaç duymayacağınız garanti edilen bazı tabloları silebilirsiniz.

Başka araçlar var mı?

Her zaman el yapımıdır. Ve yerel olarak orada yapılacak en iyi şeyin ne olduğu netleşiyor çünkü bazı veriler kritik, bazıları ise kritik değil. Ve her veritabanı ve onunla çalışan uygulama, işe bağlıdır. Her zaman yerel olarak karar verilir.

Rapor için teşekkürler! İki sorum var. İlk olarak, işlemler sıkıştığında hem tablo alanı boyutunun hem de dizin boyutunun büyüdüğünü gösteren slaytlar gösterdiniz. Ve raporun ilerleyen kısımlarında tableti paketleyen bir dizi yardımcı program vardı. Peki ya endeks?

Onları da paketliyorlar.

Ancak boşluk endeksi etkilemiyor mu?

Bazıları bir indeksle çalışır. Örneğin, pg_rapack, pgcompacttable. Vakum indeksleri yeniden yaratır ve onları etkiler. VACUUM FULL'un amacı her şeyin üzerine yazmaktır, yani herkesle çalışır.

Ve ikinci soru. Kopyalarla ilgili raporların neden kopyanın kendisine bu kadar bağlı olduğunu anlamıyorum. Bana öyle geldi ki raporlar okunuyor ve çoğaltma yazılıyor.

Çoğaltma çakışmasına ne sebep olur? Hangi süreçlerin gerçekleştiğine dair bir Ustamız var. Araba vakumlama işlemimiz devam ediyor. Bir otovakum aslında ne yapar? Bazı eski satırları kesiyor. Şu anda bu eski satırları okuyan kopya hakkında bir talebimiz varsa ve Master'da, otovakumun bu satırları üzerine yazmak için mümkün olduğunca işaretlediği bir durum meydana gelirse, o zaman bunların üzerine yazdık. Ve bir veri paketi aldık, isteğin kopyada ihtiyaç duyduğu satırları yeniden yazmamız gerektiğinde, çoğaltma işlemi yapılandırdığınız zaman aşımını bekleyecektir. Ve sonra PostgreSQL kendisi için neyin daha önemli olduğuna karar verecek. Ve çoğaltma onun için istekten daha önemlidir ve kopya üzerinde bu değişiklikleri yapmak için isteği vuracaktır.

Andrey, bir sorum var. Sunum sırasında gösterdiğiniz bu harika grafikler, bir tür faydalı çalışmanızın sonucu mu? Grafikler nasıl yapıldı?

Bu bir hizmettir Okmetre.

Bu ticari bir ürün mü?

Evet. Bu ticari bir üründür.

Kaynak: habr.com

Yorum ekle