20 Nisan 2017 Perşembe

Duyuru: Microsoft İş Ortaklığı

Gün itibariyle, gerek Microsoft Azure bulut ortamında, gerekse geleneksel ortamlardaki Microsoft SQL Server konusunda kurulum, sağlık bakımı (healthcheck), performans iyileştirme çalışmaları, sürekli kullanılabilirlik ve felaket önleme planlama ve kurulumları konusunda uzmanlaşmış olan ve hizmet veren şirketimin Microsoft Silver Cloud Partner olduğunu iftihar ile ilan ederim efendim.

Bu iş ortaklığı sayesinde, yaptığımız projelerde Microsoft ile daha yakın çalışabileceğiz. Projelerimizde Microsoft ürünleriyle ilgili karşılaştığımız sorunlar için Microsoft'tan daha iyi destek alabileceğiz.

Bu süreçteki desteği için Hakan Türköner'e ve bu vesileyle benimle çalışmayı tercih eden değerli tüm müşterilerime teşekkür ederim.

Verdiğimiz hizmetler hakkında daha fazla bilgi için lütfen tıklayın.


4 Nisan 2017 Salı

Olası gizemli kesintilere hazırlıklı mısınız?

Yoğunluktan dolayı yine uzun bir süre yazamadım, projelerden kalan bu aralıkta hemen bu yazıyı yazmak istedim.

Bir müşterimde bir süredir büyük bir tabloda yaşanan dahili kayıt tekilleştirici mekanizmasının yarattığı kesinti sorununun giderilmesi konusunda çalışıyorduk. Nihayet sorunu giderdik. Bu yazımda sorunun tanımından ve çözmek için neler yapılabileceğinden özetle bahsedeceğim.

Yazının başında belirtmem gerekir ki bu yazı Microsoft SQL Server konusunda bazı ileri seviye terim ve kavramları bilmenizi gerektiriyor. Bu yazıda her kavramı tek tek açıklamayı hedeflemiyorum.

"Dahili kayıt tekilleştirici" nedir önce ondan bahsedeyim. Muhtemelen oldukça küçük bir azınlığın haberdar olduğu bir terim olduğu için ilk bakışta ilginç görünse de Türkçe olarak tanımlamak istedim ki biraz daha anlaşılabilir olsun. Efendim bahsettiğim kavram Microsoft SQL Server ürünündeki Clustered tablolardaki tekrar eden kayıtların tekilleştirilmesi için kullanılan Uniquifier kavramı. Öncelikle bu kavram tam olarak nerede, nasıl ve neden kullanılıyor ondan bahsedeyim.

Bir tabloyu Clustered yaptığınızda, yani bir tabloda bir Clustered indeks oluşturduğunuzda, o tablodaki tüm kayıtların eşsiz olması gerekiyor. Bu eşsizliği, oluşturacağınız Clustered indeksi Unique Clustered indeks olarak oluşturarak sağlayabilirsiniz; aksi takdirde, yani indeksinizi Unique Clustered indeks olarak değil de sadece Clustered olarak oluşturursanız SQL Server indeksinize Uniquifier adında dahili bir alan ekleyecektir. Normal şartlar altında, SELECT sorgularınızda veya [sp_helpindex] ile ve benzeri diğer yöntemlerle bu alanı görmezsiniz. Bu alanın varlığını ve etkilerini görebilmek için özel bazı yöntemler kullanmak gerekiyor. Heap tablolarda, yani bir Clustered indeks tanımlanmamış bir tablodaki kayıtların bir Unique indeks ile veya Uniquifier ile eşsizleştirilmesine ihtiyaç yoktur.

Uniquifier isimli alanın veritipi INT'tir, tutabileceği azami değer 2.147.483.647'dir ve diskte 4 baytlık yer kaplar. Cluster Key kendini tekrar etmedikçe Uniquifier'ın değeri artmaz, 0 olarak kalır ve diskte de yer kaplamaz. Cluster Key, Clustered indeksi oluşturan alandır. Clustered indeksi oluşturan alan sayısı birden fazla da olabilir, birden fazla alandan oluşan indekslere de Composite indeks denir.

Biraz da örnek ve görsellerle anlatayım, bu kavramlara çok yabancı olan arkadaşlar için daha anlaşılabilir olsun.

Not: Öncelikle şunu belirtmek gerekiyor ki DBCC PAGE komutu Microsoft tarafından resmen desteklenen ve dokümante edilmiş bir komut değildir. Bu ve diğer komutları canlı/üretim sunucularınızda çalıştırmamanızı öneririm.

Bu örnekte önce [uniq_test] adında bir tablo oluşturuyorum ve bu tablodaki [id] alanı için de bir Clustered indeks oluşturuyorum. Sonra aynı kayıttan 2 tane oluşturuyorum. DBCC IND komutuyla [test] veritabanındaki [uniq_test] tablosunun Page'lerini tespit ediyorum. DBCC PAGE komutuyla da yine [test] veritabanındaki birinci veri dosyasındaki 36296 numaralı Page'in içeriğine bakıyorum.

CREATE TABLE [uniq_test](id INT, isim NVARCHAR(50), soyisim NVARCHAR(50));
GO
CREATE CLUSTERED INDEX [CIX] ON [uniq_test]([id]);
GO
INSERT INTO [uniq_test] VALUES(1, 'Ekrem', 'Önsoy');
GO 2
DBCC IND([test], [uniq_test], 1);
GO
DBCC PAGE(test, 1, 36296, 3) WITH TABLERESULTS;


DBCC PAGE'in sonucu

Not: Burada [isim] ve [soyisim] alanlarının aynı olması değil, [id] alanının aynı olması Uniquifier'ın kullanılmasını sağlıyor. [isim] ve [soyisim] alanları farklı da olsa yine de Uniquifier değeri artacaktı.

Not: DBCC IND komutu sizde muhtemelen farklı bir Page numarası döndürecektir, bunda hiçbir gariplik veya sakınca yok. Eğer örneği siz de kendi test ortamınızda uygulamak istiyorsanız DBCC PAGE komutuyla sizde dönen Page numarasını kullanın.

DBCC PAGE ile ilgili Page'in içerisine baktığımda yukarıdaki ekran görüntüsünde paylaştığım gibi bir sonuç görüyorum. Önceden de belirttiğim gibi eğer Clustered indeksimi oluştururken Unique Clustered indeks olarak oluştursaydım veya hiç Clustered indeks oluşturmasaydım ve yine bu Page'in içine baksaydım o zaman UNIQUIFIER diye bir alanı hiç görmeyecektim.

Yukarıdaki ekran görüntüsünde oluşturduğum 2 kayda ait değerleri her kayıt için ayrı ayrı renklerle ve dikdörtgen ile işaretledim. Önce lütfen daha uzun olan kırmızı ve mavi dikdörtgenlere bakın. Bu değerlere dikkatlice baktığınızda UNIQUIFIER'ın normalde 4 bayt uzunluğunda oduğunu, diskte ne kadar yer kapladığını ve o anki değerini göreceksiniz. (physical) 0 olduğunda diskte yer kaplamıyor demektir, yani aynı değer birden fazla tekrar etmemiş demektir. Bu nedenle uzun kırmızı dikdörtgende (physical)'ın yanında 0 varken, uzun mavi dikdörtgen ile işaretlediğim 2. kayda ait değer 4. Kırmızı ile işaretlediğim ilk kayda ait UNIQUIFIER'ın değil de mavi ile işaretlediğim ikinci kayda ait UNIQUIFIER'ın diskte 4 bayt yer kapladığını da küçük dikdörtgenlerle çevrelediğim Record Size değerlerinden anlayabilirsiniz. İlk kaydın boyutu 39 bayt iken, ikinci kaydın boyutu 4 baytlık UNIQUIFIER değer nedeniyle 43 bayt.

Eğer aynı kaydı 3. kere ekleseydim UNIQUIFIER'ın değeri 2 olacaktı ve INT veritipindeki bir alanın alabileceği azami değer olan 2.147.483.647'ye kadar gidecekti. Şayet aynı Cluster Key değeriyle bir kayıt bu kadar tekrar ederse, bir sonraki kaydı oluşturamazsınız ve aşağıdaki hatayı alırsınız:

"The maximum system-generated unique value for a duplicate group was exceeded for index with partition ID XXX. Dropping and re-creating the index may resolve this; otherwise, use another clustering key."

Emin olun, böyle bir durumla karşılaşmak istemezsiniz. Çünkü bu durumla karşılaştığınızda bu demektir ki tablonuzda en az 2 küsur milyar kayıt var ve muhtemelen önemli bir tablonuzdur ki bu kadar besleniyordur.

Bu sorun, hatalı tablo tasarımından kaynaklanır. Kayıtları blok blok silmeniz bir şey ifade etmez, çünkü UNIQUIFIER değeri böyle sıfırlanmaz. UNIQUIFIER değerinin sıfırlanması için tekrar eden kaydın tamamının silinmesi veya blokları sildikten sonra tabloyu hata mesajında da belirtildiği gibi komple yeniden oluşturmanız gerekir. Bu da size ancak bir sonraki hataya kadar zaman kazandırır.

Firmalar iş hayatlarına başlarken genelde düşük bütçeyle başlar, bu nedenle her konuda uzmanı bünyelerinde barındıramazlar ve böyle tasarımlar da genelde bu gibi nedenlerden kaynaklanır. Çünkü eğer firmanın bünyesinde donanımlı bir veritabanı yöneticisi varsa ve tasarım sırasında kendisine tablo tasarımı danışılsa, o böyle bir tasarımın yaratacağı olası sonuçları önceden öngörebilir.

Bu sorunu düzeltmek için tablonuzu Heap'e çevirebilir veya Clustered indeksinizi değiştirebilirsiniz. Tabii ki bunlar çok ciddi süreçler, çok iyi hazırlık ve doğru aksiyon gerekiyor. Aksi takdirde, performans sorunu gibi yeni sorunlarla karşılaşabilirsiniz. Belki fırsat olursa ileride de bu konulardan bahsederim.

Ekrem Önsoy




10 Mart 2017 Cuma

Veritabanı sunucunuzun "rutin" durumu gerçekten normal mi?

Son zamanlarda bir firma ile kısa bir çalışma yaptık. Microsoft SQL Server veritabanı sunucusu sağlık kontrolü çalışması yaparken, sunucunun işlemci kaynaklarının neredeyse sürekli %100 kullanıldığını gözlemledim. Açıkçası ben her ne kadar o anda veritabanı sunucusu sağlık kontrolü çalışmasını erteleyip sorunlara göz atmak için sabırsızlansam da, müşteri bu durum ile yaşamaya alışıktı. Bu nedenle o gün sağlık kontrolü çalışmamıza devam ettik.


Kontrollerde sunucunun CPU durumu (temsilidir).

Aslında benimle çalışmak istemelerinin nedenlerinden biri de işlemcinin bu durumuydu, ama bu sorun hakkında çok da kaygılı değillerdi. Sonuç itibariyle günlük operasyonlarını durduran, kesintiye neden olan bir durum yoktu ortada. Sadece zaman zaman ağırlık hissediliyordu ve Deadlock'lar oluşuyordu. Muhtemelen kullanıcılar da zaten sistemin böyle bir performansla çalışmasına alışmışlardı, o yüzden kimse yadırgamıyordu, sonuç itibariyle sistemin "rutin" hali bu idi.

Şahsen sağlık kontrolü çalışmasınının tamamlanmasını ve ertesi gün yapacağımız performans iyileştirme çalışmasını iple çekiyordum. Ertesi gün performans iyileştirme çalışması için yalnızca birkaç saat ayırabildik. Bu kadarlık bir sürede bile, yaptığımız müdahalelerle işlemci kullanımının (sıçramaları hariç tutarsak) %1'lere indiğini gözlemledim. Zaman zaman işlemci kullanımını %100'e çıkartan bir (ve muhtemel başka zaman aralıklarında çalışan birkaç tane daha) sorgu hala vardı ve en sorunlu görünen sorguyu da tespit edip ilgili yöneticiye bildirmiştim; fakat sorgular uygulamada gömülü olduğundan hemen o gün müdahale edip sorun düzeltilemedi. Birkaç saatte bu kadar iyileştirme sağlanabildiyse çalışmak için 1 ekstra günümüz daha olsa, stabil olarak işlemci kaynaklarını %5'in altına indirebileceğimize eminimdim. Tabii tüm ortamlar için mümkün olan bir sonuç değil bu, bazı çalışmalar daha uzun sürüyor; ama bu müşterim için durum böyleydi.


Önceden de benzer senaryolara ve konulara değinmiştim. Sorunları CPU ve RAM ekleyerek ancak bir yere kadar öteleyebilirsiniz. Tabii CPU ve RAM eklemek de yetmiyor, bu donanımlar için Microsoft SQL Server lisansı da satın almanız gerekiyor. Örneğin Microsoft SQL Server 2016 Enterprise Edition veritabanı sunucunuzu Per Core modeliyle lisanslamak istediğinizde tek bir CPU Core'u için lisans bedeli 14,256$ ve sunucunuzdaki tüm fiziksel Core'lar için lisans satın almanız gerekiyor.


Güncelleme (2017-03-13): Per Core lisanslama modelinde çekirdek başına lisanslama yapılıyor ve 2 çekirdek paketiyle satılıyor. Yani 14,256$, 2 Core'luk paket fiyatı. Eğer sunucunuz 8 çekirdekliyse, o zaman 4 tane 2 Core'luk paket almanız gerekiyor. Tabii her lisanslama senaryosu bu kadar basit hesaplanmıyor. Senaryoya göre lisans hesabı çok daha ayrıntılı olabiliyor. Her halükarda bu konuyu yetkili bir satış kanalıyla görüşmenizde fayda var.

Bu konuda size bazı önerilerim var:


  • Yazılımcıların veritabanı kodlaması konusunda en iyi pratikleri bildiğinden ve uyguladığından emin olun, 
  • Veritabanı sunucunuza kod taşımalarını yapmadan önce taşınacak değişiklikleri iyi ve sistemli bir şekilde test edin, 
  • Testleri küçük boyutlarda, gerçekçi olmayan verilerle yapmayın, 
  • Test ortamınızın hem donanım olarak, hem yapılandırma olarak, hem de veri olarak üretim ortamınıza olabildiğince benzer ve güncel olmasını sağlayın, 
  • Kod taşımalarından önce ve sonra, üretim ortamınızdaki performans değişikliklerini takip edin, kodu taşıyıp hiçbir şey olmamış gibi üretim ortamını kendi haline bırakmayın,
  • Performans sorunlarınızı donanım kaynaklarını arttırarak değil, öncelikle veritabanı yapılandırmanızı ve kodlarınızı iyileştirerek gidermeyi deneyin,
  • Bu değişikliklerin yaratacağı olası performans sorunları takip edilmediğinde ve gerekli müdahaleler zamanında yapılmadığında bir noktada artık sistemin sağlıklı çalışamaz hale geleceğini unutmayın.

Microsoft SQL Server ile ilgili profesyonel bir desteğe ihtiyacınız varsa beklerim efendim.

Ekrem Önsoy

27 Şubat 2017 Pazartesi

MVP Reconnect Programı Anısı

1 ay önce LinkedIn'de Microsoft'un MVP Reconnect programını aşağıdaki gibi duyurmuştum.



Bu program çerçevesinde Microsoft bu ay bu programa dünya çapında kayıt olan ilk 200 kişiye bir hatıra gönderdi. Efendim benim hatıram da bugün geldi, resmini çekip sizlerle de paylaşmak istedim.



Sevgiler,
Ekrem Önsoy

15 Şubat 2017 Çarşamba

SQL Server 2016'da Database Mail ve .Net 3.5

Firma ziyaretlerimde sık karşılaştığım sorulardan biri şu:

"Ekrem, sen birçok firmaya girip çıkıyorsun, birçok ortam görüyorsun, şu anda sektörde en çok kullanılan SQL Server versiyonu nedir?"

Bu sorunun cevabı an itibariyle şu: "SQL Server 2012".

Sektör Microsoft'un hızına yetişemiyor. Bu sadece Türkiye'de değil, tüm dünyada böyle.

Efendim bu yazımın konusuna dönelim. Böyle bir giriş yapmamın nedeni, SQL Server 2016 ile fazla karşılaşmadığımızdan, SQL Server 2016'da Database Mail ile ilginç bir sorun yaşama olasılığınıza dikkat çekmekti. 

Belki bazılarınızın bildiği gibi SQL Server 2016 kurarken .Net 3.5 kurulumu zorunlu değil. SQL Server 2016 medyanızla doğrudan SQL Server 2016 Instance'ınızı kurabiliyorsunuz. Böyle kurulmuş bir ortamda Database Mail'i yapılandırdım ve test amaçlı bir eposta gönderdim ve beklemeye başladım. İlk dikkatimi çeken şey, Database Mail işleminin bir türlü başlamayışıydı. Task Manager'a baktım, DatabaseMail.exe yok. "sysmail_start_sp" isimli, Database Mail'i başlatmak için kullanılan sistem Stored Procedure'ünü çalıştırdım, ama gene tık yok.

DatabaseMail.exe'nin bulunduğu yola "C:\Program Files\Microsoft SQL Server\MSSQL13.MSSQLSERVER\MSSQL\Binn" gittim ve elle çalıştırmayı denedim ve bir sürpriz ile karşılaştım:

Genişletmek için resmin üstüne tıklayın
Bu ekranı görünce, önceden bu konuda bir yazı okuduğum geldi aklıma. SQL Server 2016 kurulumu için .Net 3.5 gerekmese de, SQL Server 2016'da Database Mail kullanmak için .Net 3.5 kurulumu gerekiyordu.

Genişletmek için resmin üstüne tıklayın
Ben de kurulumu gerçekleştirdim ve Database Mail'i tetiklemek için aşağıdaki Stored Procedure'ü çalıştırdım:

EXEC sysmail_start_sp;

Akabinde Task Manager'ı kontrol ettim ve aşağıdaki gibi DatabaseMail.exe'nin çalıştığını gördüm.


Genişletmek için resmin üstüne tıklayın

SQL Server 2016 kurulumlarınızda Database Mail'in bu durumuna dikkat etmeyi unutmayın. İşin kötüsü, herhangi bir yerde bu konuda bir hata veya kayıt da yok. Eğer ben de sorunu çözmek için izlediğim bu yöntemi izlemesem veya çook önceden okuduğum o yazı aklıma gelmese, bu sorun kim bilir ne saç baş yoldururdu.

Güncelleme: Bu sorun SQL Server 2016 Cumulative Update 2 ile çözülmüş ve Service Pack 1'e de yansıtılmış. Henüz kendim test etmedim, ama bu Connect'e göre Microsoft böyle diyor.

Ekrem Önsoy