16 Ağustos 2016 Salı

Bir Azure SQL Database'i yerel bir Instance'a taşımak

Selam millet,

Bugün bir yazılımcı arkadaş, Azure SQL Database'de bulunan bir veritabanını verileriyle birlikte yerel bir sunucumuza taşıma talebinde bulundu.

Yaptığım işlem çok özetle ilgili SQL Database'i Azure'daki müsait bir Storage'a Export etmek ve sonrasında üretilen *.bacpac dosyasını yerel ortamdaki uygun bir sunucuya indirmek ve daha sonra da Restore/Import edilmesi istenen yerel SQL Server Instance'ına Restore etmek oldu.

Bu yazıda daha ziyade değinmek istediğim şey ise *.bacpac dosyasının marifeti. Azure SQL Database'deki veritabanının versiyonu 12.0.2000.8, yani V12 idi, *.bacpac dosyasını Restore ettiğim Instance'ın versiyonu ise 11.0.5582.0 idi. Deneyimli her SQL Server DBA'in bildiği gibi, üst bir versiyonda oluşturulmuş olan *.bak dosyaları alt bir versiyona Restore edilemez. Daha net olmak gerekirse ve bu örnek üstünden gidersek 12.0.2000.8 versiyonunda olan bir Instance'ta oluşturduğunuz bir *.bak dosyası 11.0.5582.0 versiyonlu bir Instance'a Restore edilemez. Fakat 12.0.2000.8 versiyonunda oluşturulmuş bir *.bacpac dosyası pekala ve başarıyla 11.0.5582.0 versiyonlu bir Instance'a Restore edilebiliyor.

Bununla birlikte, bu Restore (aslında Import) işlemini doğrudan 11.0.5582.0 üstündeki SQL Server Management Studio'dan yapmaya çalıştığımda şöyle bir hata aldım:

TITLE: Microsoft SQL Server Management Studio
------------------------------
Count not load schema model from package. (Microsoft.SqlServer.Dac)
------------------------------
ADDITIONAL INFORMATION:
Internal Error. The database platform service with type Microsoft.Data.Tools.Schema.Sql.SqlAzureV12DatabaseSchemaProvider is not valid. You must make sure the service is loaded, or you must provide the full type name of a valid database platform service. (Microsoft.Data.Tools.Schema.Sql)

Bu sorun *.bacpac dosyasından ziyade SQL Server Management Studio'nun versiyonuyla ilgiliydi. Aynı *.bacpac dosyasını SQL Server Management Studio'nun daha yeni bir versiyonunda Import etmeyi denediğimde hiçbir sorunla karşılaşmadan işlem başarıyla tamamlandı. Eğer olur da biri böyle bir hata ile karşılaşırsa diye burada not etmek istedim.

Sevgiler,
Ekrem Önsoy

12 Ağustos 2016 Cuma

Sorunları donanım ekleyerek sadece bir yere kadar ötelersiniz

Merhabalar,

Çalıştığımız ortamlardan birinde 3 senedir arşivleme yapılmıyordu ve canlı ortamda devreye girmiş  ve henüz kontrolden geçmemiş bazı kodlar vardı.

En son gösterilecek şeyi en önce göstereyim:

CPU kullanım grafiği

Yukarıda grafiğini gördüğünüz şey, bahsini ettiğim bu şirket için en kritik olan veritabanı sunucusuna ait CPU kullanım grafiği.

Tabii bu grafiği CPU kullanım ortalaması zaten düşükmüş olarak yorumlamayın hemen. CPU kullanım oranının ortalama %20-25'lerde görünmesinin nedeni, yoğun ortalamanın bu seviyede oluşu. Yani kısa süreli de olsa çok daha yoğun kullanımlar oluyordu, fakat yukarıdaki grafiği oluşturmak için kullanılan verinin çokluğu ve ortalamanın %20-25'lerde olması nedeniyle grafikte ortalama bu seviyelerde görünüyor. Yani eğer birkaç ay geriye dönük değil de daha kısa zaman dilimlerinde bakılırsa ortalama daha yüksek gözükür.

Bununla birlikte dikkat edilirse ya işlenmesi gereken verinin gitgide artmış olmasından, ya bazı yeni kodların devreye girmesinden vb. CPU kullanımının zamanla yükseldiği de görülüyor. Yani müdahale edilmediğinde ortalama değer daha da artacaktı. Arşivleme ve performansiyileştirme işleri de 28 Temmuz tarihinde başlatıldı.

Bu ortamda arşivlenebilecek 3 senelik veriyi arşivledim ve bazı SP'leri elden geçirdik. Sadece bu çalışmalardan sonra CPU kullanımının yoğunluklu ortalaması %30'lardan %5'lere düştü.

Bunları anlatarak vermek istediğim mesajı daha da netleştirmek gerekirse, canlı sisteminizde olması şart olmayan veya canlı ortamda sadece zaman zaman ulaşılması gereken verilerinizi arşivleyin. Gereksiz veriler için indeks ve istatistik bakımları yapmayın, tablo/index tarama işlemleri yapmayın, sorgulanan veriyi işlemek için hafıza, disk, ağ ve işlemci gibi kaynaklarınızı boşa harcamayın.

Verilerinizi ihtiyacınıza göre ılık şekilde veya soğuk şekilde arşivleyin. Misal sürekli ana veritabanınızda olması gerekmeyen veriyi ayrı bir veritabanına aktarın, gerekirse disk altyapısını da disk/sistem yönetimi takımlarıyla planlayıp ayırın ve ihtiyaca göre tasarlayın. Bu ılık veriyi sadece gereken sorgularda/SP'lerde sorgulatın. Eğer verinin soğuk olarak arşivlenmesi gerekiyorsa planlamanızı yapın ve veriyi örneğin senelere, aylara bölün, sıkıştırarak yedekleyip canlı veritabanından silin. Tabii tüm bu çalışmaların iş bölümüyle, ilgili yazılım bölümleriyle ve yöneticilerle planlanıp uygulanması gerekiyor.

Sevgiler,
Ekrem Önsoy

5 Ağustos 2016 Cuma

Bir Fulltext Index tecrübesi

Selam arkadaşlar,

Son birkaç gündür bayağı yoğunluk oldu. Özellikle sorumlu olduğum ortamlardan birinde yaptığımız bir arşivleme çalışması neticesinde Fulltext Index Population işlemiyle bayağı cebelleştim.

Öncelikle senaryodan bahsedeyim. Çalıştığım sunucu canlı, üretim sunucusu. Burada SQL Server'ın Standard Edition'ı kullanılıyor, yani Table Partitioning, arşivleme için geçerli bir seçenek değil.

Arşivlenen üretim veritabanının adına Galata diyelim, arşiv veritabanının adına da Galata_arsiv. Galata'daki bazı tablolardaki kayıtlar, tarih değerlerine göre Galata_arsiv'e taşınıyor. Tabii ki taşıma, önce ilgili kayıtların Galata_arsiv'deki hedef tabloya kopyalanması, sonra da kaynaktan silinmesi şeklinde yapılıyor. Efendim Galata veritabanındaki kaynak tabloda 2 alan için bir de Fulltext Index tanımlı. Silinecek kayıt sayısı 30 milyon civarında.

Silme işlemini yaptım ve bir sorun yok. Bir süre sonra Ghost Cleanup arkaplanda çalışmaya başladı, gene bir sıkıntı yok. O günlük arşivleme çalışması tamamlandı ve bilgisayarımı kapattım. Fakat birkaç saat sonra CPU uyarı mesajları ve akabinde operasyonda çalışan arkadaşlardan yavaşlık şikayetleri gelmeye başladı. Hemen bağlandım baktım, CPU kullanımı sürekli %95'in üstünde, IO çok yüksek. Felaket!

Önce hemen sorun Windows'taki başka bir uygulamada mı yoksa SQL Server'da mı onu kontrol ettim, sorun SQL Server Instance'ındaydı. Daha sonra çok silme işlemi yaptığım için Ghost Cleanup işleminden şüphelendim. Nedir bu Ghost Cleanup ondan da çok özetle bahsedeyim. SQL Server'daki bir tablodan kayıt sildiğinizde bu kayıtlar birkaç nedenden dolayı hemen silinmezler, silinecek olarak işaretlenirler. Öncelikle bu hem silme işleminin hızlı olmasını sağlar, hem de olur da işlemi iptal etmek ve veriyi geri getirmek isterseniz bu yolla çok daha hızlı olmuş olur. Daha sonra belli bazı şartlar oluştuğunda Ghost Cleanup devreye girer ve bu kayıtları yavaş yavaş siler. Efendim inceledim, baktım ve sorunun Ghost Cleanup olmadığına karar verdim.

Kullanıcı işlemlerinde bir gariplik var mı diye inceledim, özellikle göze çarpan bir işlem görmedim. Peki ne oluyordu bu SQL Server Instance'ına da birden bu kadar çok kaynak kullanmaya başlamıştı?

Biraz daha inceleyince gözüme özellikle bir sistem işlemi çarptı. Çok fazla CPU tüketiyordu. Lastwaittype'ına bakınca "FW CRAWL" ibaresini gördüm. O anda aklıma başımın Fulltext Index ile dertte olabileceği geldi. Hemen silme işlemi yaptığım tabloları kontrol ettim, evet, birinde Fulltext Index vardı ve Population işlemine başlamıştı bile. Yani yapılan milyonlarca kayıt silme işlemini kendi Index'ine yansıtıyordu. Fulltext Index uzmanı değilim, tam olarak ne yapıyor da ne oluyor henüz inceleme şansım olmadı ve şunu da belirtmeliyim ki Fulltext Index konusu başlı başına bir kitap konusudur ki sadece bu konuda yazılmış kitaplar vardır. Fakat başka bazı belirtilere göre de başımın Fulltext Index ile dertte olduğu artık kesindi.

Bu arada, sorunun Fulltext Index olduğundan emin olmadan önce, herhangi bir Bug veya gariplikle karşı karşıya olma ihtimaline karşın SQL Server Instance'ını birkaç kere yeniden başlatmıştım. Servis açılırken Galata_arsiv veritabanının uzun süre In-Recovery durumda kaldığını gördüm. Benim silme işlemlerim biteli çok olmuştu ve Transaction Log dosyasını boşaltmıştım, yani geri alınacak veya sürdürülecek herhangi bir log yoktu içinde. Bu Recovery de neyin nesi diye düşünüyordum. Fulltext Index durumu kafamda netleşince, Recovery'nin de bununla ilgili olduğunu anladım. Veritabanı Recovery durumuna düştüğüne göre ve Recovery'nin 3. fazında 15-20 dakika beklediğine göre bir şeyler (Fulltext Index ile ilgili) loglanıyordu.

Efendim ya henüz ben bulamadım ve bilemedim, ama maalesef bir Fulltext Index Population işleminin tam olarak hangi safhada olduğu, yani mesela yüzde kaçının tamamlandığı ve benzeri bir bilgiyi edinebileceğim herhangi bir sistem kataloğu veya DMV'si yok. Bilen varsa da lütfen paylaşsın, ben de buradan duyurayım. Sadece Fulltext Index'leri ve kataloglarını görüntüleyebiliyoruz, bir de Index'in güncel olmayan durumunu. "Index'in güncel olmayan durumu" derken neyi kastettiğimi de hemen belirteyim, [sys].[dm_fts_index_population] isimli DMV ile bir Fulltext Index'in o anki durumunu ([status_description]) görebiliyorsunuz, fakat benim senaryomda bu alanda "Starting" yazıyordu, halbuki bu işlem başlayalı 2 gün olmuştu zaten! Tabii insan kuşkulanıyor ve akla sorular düşüyor, işlem gerçekten devam ediyor mu, yoksa bir yerde bloke mi oldu, bloke olsa neden bu kadar kaynak tüketiyor, yoksa kaynak tüketen ve henüz tespit edemediğim başka bir işlem mi var? Bu noktada Fulltext Index'in kara kutu olduğunu düşünmeye başlamıştım, ki hala öyle görünüyor.

Bununla birlikte, şöyle sorular soruyor olabilirsiniz:
- Neden Population'ı durdurmadın?
- Neden Fulltext Index'i silmedin veya Disable duruma getirmedin?
- Neden Auto-tracking'i Manuel yapmadın ve daha uygun zamanda devam etmedin Population'a?

Çok güzel sorular ve tabii ki benim de ilk aklıma gelenler oldu. Fakat FW CRAWL işlemini yapan sistem işlemi buna izin vermedi. Yukarıdaki sorular için aksiyon almaya kalktığımda FW CRAWL sistem işleminin o aksiyonu bloke ettiğini gözlemledim. Ayrıca bloke olan sadece benim aksiyon almak için kullandığım işlem değil, arşiv veritabanına gelen diğer tüm kullanıcı işlemleri de o anda bloke oluyordu. Ben aksiyon almak için kullandığım ALTER FULLTEXT INDEX vb komutları çalıştırdığım işlemi sonlandırdığım anda blokasyon çözülüyor ve kullanıcılar işlemine devam edebiliyordu.

Yani öyle bir durum ki, Population'ı durduramıyorum, CPU ve IO kaynaklarım sonuna kadar kullanılıyor, kullanıcılardan sürekli yavaşlık şikayetleri geliyor.

Durum konusundaki bazı noktalar netleşince hemen alternatif bir aksiyon planı hazırladım. O aksiyon planını devreye almadan önce, sorunun şiddetini dindirebilecek önlemleri almak için ilgili birimler arasında küçük toplantılar tertip ettim, alınabilecek küçük, hızlı, anlık sorunları hafife indirgeyecek aksiyonlar aldık ve tüm bunlar konusunda ilgili yöneticileri zaman zaman bilgilendirdim. Eğer Population işlemi T zamanına kadar bitmeseydi, ilgili yöneticilerin onayıyla da alternatif planı devreye alacaktım. Fakat işlem dün akşam nihayet tamamlandı.

Bu yazıdan özellikle almanızı beklediğim şey, bir kriz anında panik yapmadan yapılacak şeyleri düşünmeniz, ilgili yöneticileri her daim bilgilendirmeniz, özellikle de kritik bir aksiyon almadan önce. Yeri geldiğinde tabii ki inisiyatif alın, fakat unutmayın ki siz resmin sadece bir bölümünü görebilirsiniz, ilgili yöneticileriniz resmin daha büyük kısmına hakim, o nedenle kritik bir aksiyon öncesinde muhakkak ilgili birimlerle ve yöneticilerle eşgüdümlü çalışın. Biliyorum kolay değil, ama panik yapmayın. Veritabanı yönetimi, tüm şirketi etkileyen, hatta bir şirketin en hayati kademelerinden biridir. Çok ciddi bir iştir ve büyük sorumluluk ister. Her zaman bunun bilincinde olmalısınız.

Sevgiler,
Ekrem Önsoy

28 Temmuz 2016 Perşembe

Tablo tasarım aşamasında kötü uygulamalardan biri...

Selam millet,

Bugün çalıştığım ortamlardan birindeki geliştirme ortamından canlı ortama taşıma yapıyorduk ve tablolardan birinde çok kötü bir uygulama yapıldığını gördüm. Tablo Schema'sı şöyle bir şeydi:

CREATE TABLE xxx.tbl_xxx(
...
Log_Id INT,
Log_Explanation NVARCHAR(500)
...)

Bu bir başka tablodaki kayıtlarla ilgili yapılan işlemler için tutulan kayıt (log) tablosu.

Log_Id alanı, yapılan işlemin ne olduğunu belirten eşsiz bir işlem kayıt numarası, misal 10, 15, 17...

Log_Explanation alanı ise yapılan işlemin ne olduğunu açıklayan alan, misal "Giriş yapıldı", "Çıkış yapıldı", "Yeni kayıt"...

Yani tablodaki örnek bir kayıt şöyle görünüyor:

alan1 | alan2 | Log_ID | Log_Explanation
... | ... | 10 | "Giriş yapıldı"
... | ... | 10 | "Giriş yapıldı"
... | ... | 10 | "Giriş yapıldı"
... | ... | 15 | "Çıkış yapıldı"
... | ... | 15 | "Çıkış yapıldı"
... | ... | 17 | "Yeni kayıt"
... | ... | 17 | "Yeni kayıt"
... | ... | 17 | "Yeni kayıt"

Hiç uzatmadan doğrudan söylenecek şeyi söyleyeyim, böyle bir tablo oluşturan arkadaşımın veritabanı tablo tasarımından bihaber olduğu, hatta genel mantıktan bile uzak olduğu aşikar. Evet, biraz sert, ama üzgünüm gerçek bu.

Arkadaşlar böyle tasarım yapılmaz. Böyle bir şeyi ancak veritabanı sistmeleriyle tanışalı 1 gün olmuş biri, hiçbir şey okumadan, oynaya oynaya öğrenmeye çalışırken yapar.

Böyle senaryolar için tablo tasarımı yapılırken açıklamalar her zaman başka bir tabloda tutulur, kayıtlar bu şekilde çoklanmaz. Çoklanacak, yani kendini tekrar edecek tek bilgi açıklamanın ID'si olabilir, daha sonra da gerektiğinde, bir arayüzde gösterim yapılırken mesajların saklandığı diğer tablodaki kayıt açıklamasının ID'si ile kayıt tablosundaki ID birleştirilir ve öyle gösterilir.

Bunlar çok çok temel şeylerdir, lütfen ama lütfen bir tablo tasarlama işine girişmeden önce ya bu konuda bir eğitim alın ya da bütçeniz yoksa açın bir kitap okuyun.

Ekrem Önsoy


30 Haziran 2016 Perşembe

SQL Server 2014 SP1 CU7

Selam millet,

Müşterilerimin birinde SQL Server 2014 kullanılıyor ve çok kritik bir sunucu. Bu kritik sunucuda saliselerin hesabı yapılıyor, tam bir klasik OLTP ortamı.

Bu canlı ortamda, test ortamına kıyasla daha fazla CPU kullanıldığını gözlemliyorduk ve nedenini de henüz bulamamıştık. Derken geçenlerde SQL Server 2014 SP1 CU7 yayınlandı ve bu CU'daki bir KB (3162589) "FIX: High CPU usage on SQL queries after install SQL Server 2014 Service Pack 1" çok dikkat çekiciydi. CU'yu inceledikten sonra ve tabii ki önce test ortamına da uyguladıktan sonra ilgili yöneticilerle konuşup, planlamamızı yapıp canlı ortama da uyguladık. Sonrasında en kritik Stored Procedure'lerimizin işlem sürelerinin 200-400 milisaniyelerden 40-60 milisaniyelere düştüğünü gözlemledik. Yani bu Bug bu ortamda bizi vurmuştu.

Yayınlanan Service Pack ve Cumulative Update'leri yakınen izlemenizi, KB'leri incelemenizi ve ortamlarınızdaki ihtiyaçlarınızla karşılaştırmanızı öneririm. Bu örnekte de görüldüğü gibi, güncellemeler bilinçli ve planlı bir şekilde uygulandıklarında çok şeyi değiştirebiliyorlar.

Sevgiler,
Ekrem Önsoy