27 Ekim 2015 Salı

SSMS'in azizliklerine dikkat!

Selam millet,

Bir ortamımda SQL Server 2008 R2 Instance'ı ve uyumluluk açısından da SQL Server Management Studio (SSMS)'nun ilgili versiyonu kullanılıyor.

Bu ortamdaki bir Index'i Covered Index yaparken hatırıma geldi, sizlerle de paylaşayım dedim. Seneler önce bir gün, çok yoğun Transaction olan bir ortamda çalışırken, yine bir Index'i değiştirirken büyük bir hata yapmıştım. SSMS bazen bu tür hataları yapmanıza çok yardımcı olabiliyor, konunun başlığı da buradan geliyor zaten.

Index'i Covered Index yapmak için SSMS'ten Index'in özelliklerine gittim, Included Columns bölümünden eklemek istediğim alanı ekledim ve sonra Script düğmesine tıklayıp Script'ini aldım ve sonuç aşağıdaki gibi oldu (tabii ki özel bilgileri gizledim): 

*********************************
USE []
GO
DROP INDEX [] WITH ( ONLINE = OFF )
GO
USE []
GO
CREATE NONCLUSTERED INDEX [] ON [dbo].[
(
[alan_adı1] ASC,
[alan_adı2] ASC,
)
INCLUDE ( [alan_adı3],
[alan_adı4]) WITH (STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

GO
*********************************

Özellikle yoğun ortamlar için bu çok tehlikeli bir kod örneği. Ben vaktinde bu hatayı yaptım, yine yoğun ortamlarda çalışan arkadaşlarım yapmasın. Eğer bu kodu çok yoğun Transaction'ların olduğu bir ortamda çalıştırırsanız, önce tablonuzu Index'siz bırakmış oluyorsunuz, ardından çok yoğun Blocking'ler oluşabiliyor, Index, IO ve tabii Cache masraflarınız dehşet şekilde artabiliyor ve bu yoğunlukta yeni Index'i öyle çabucak oluşturamıyorsunuz. Yığılan işlemlerin tamamlanmasını beklemek zorunda kalıyorsunuz. O gün nasıl kızardığımı ve zor durumda kaldığımı unutamam.

Tabii ki SSMS'i geliştirenler, kendilerini de zamanla geliştiriyorlar belli ki. Örneğin SQL Server 2014'ün SSMS'inde aynı tablo için aynı Included Column'ı eklediğinized ve Script'ini oluşturduğunuzda aşağıdaki gibi, yani olması gerektiği gibi bir kod oluşuyor:

*********************************
USE []
GO
CREATE NONCLUSTERED INDEX [] ON [dbo].[
(
[alan_adı1] ASC,
[alan_adı2] ASC,
)
INCLUDE ( [alan_adı3],
[alan_adı4]) WITH (STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = ON, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

GO
*********************************

Eğer Enterprise Edition kullanıyorsanız ONLINE parametresini de ON yapmalısınız. Eğer TempDB veritabanınız performanslı bir diskte ise, SORT_IN_TEMPDB parametresini de ON yapmalısınız. Böylece kritik Production veritabanınızda bu Index işlemiyle oluşacak olan yükü en aza indirgemiş olursunuz. Bununla birlikte, daha daha performans için Index'leri farklı bir diskteki farklı bir FileGroup'ta da tutabilirsiniz.

Peki DROP_EXISTING'in nasıl bu kadar büyük bir fark yaratabiliyor? Arkadaşım Turgay Sahtiyan zaten bu konuda bir makale yazmış, ben de aynı şeyleri tekrar yazmayayım.

SSMS'in azizlikleri sadece bununla kalmıyor. Hangi versiyondu çok çok emin değilim, ama muhtemelen SQL Server 2005'in SSMS'iydi, bir veritabanının özelliklerinde, bir dosyanın özelliklerini değiştirdiğinizde Shrink işlemi de yapıyordu. Bunu, o işlemi arayüzde yapmayıp Script'ini çıkarttığınızda görebiliyordunuz.

Yine başka bir Bug, ki bu son versiyonlarda da vardı, örneğin Log Shipping kurarken siz zaman olarak örneğin Log dosyalarımı sadece 3 gün tut olarak seçiyorsunuz arayüzde, fakat sonra aynı ekranı açtığınızda bir bakıyorsunuz 3 saat olarak kaydetmiş… Bu konuda bir makalem de vardı da, bulamadım.

Örneğin hala piyasadaki birçok yazılımcı, çok deneyimliler bile, bir tabloya bir alan ekleyecekleri zaman SSMS'teki Designer'ı kullanıyorlar. Bazı işlemlerde SSMS ilgili tabloyu tamamen siliyor ve tekrar oluşturuyor. Böyle bir şeyin Production ortamınızda olmasını gerçekten ister misiniz?

Bu yazımdan anlamanızı istediğim şey, işlemleri SSMS arayüzünden yaptıktan sonra hemen OK düğmesine basıp çalıştırmayın. Özellikle de kritik ortamlarda bunu sakın yapmayın. SSMS'in her versiyonda davranış değişiklikleri olabileceğinin bilincinde olun. Yapacağınız işlemin önce Script'ini çıkartın, mümkünse doğrudan ve sadece Script'i çalıştırın. T-SQL öğrenin, mümkün olan her yerde T-SQL ile çalışmayı alışkanlık haline getirin. Elbette Extended Event oluştururken arayüz kullanın veya benzer pratik durumlarda, fakat kritik işlemler için tetikte olmakta fayda var.

Kolay gelsin,
Ekrem Önsoy

22 Ekim 2015 Perşembe

HATA: The target principal name is incorrect. Cannot generate SSPI context. (Microsoft SQL Server)

HATA:
The target principal name is incorrect. Cannot generate SSPI context. (Microsoft SQL Server)

Error Log dosyasında da şu hata görünüyordu:
The SQL Server Network Interface library could not register the Service Principal Name (SPN) [ MSSQLSvc/..local ] for the SQL Server service. Windows return code: 0x21c7, state: 15. Failure to register a SPN might cause integrated authentication to use NTLM instead of Kerberos. This is an informational message. Further action is only required if Kerberos authentication is required by authentication policies and if the SPN has not been manually registered.

AÇIKLAMA:
Ortamlarımızdan birinde, 3 sunucudan oluşan AlwaysOn Availability Group kurulumu yapıyoruz bu günlerde. Kerberos yöntemini kullanmak için SPN kayıtlarımızı oluşturmuş, 3 sunucuda da SQL Server servisi olarak aynı hesabın kullanılmasını sağlamıştık. Fakat daha sonra, standart oluşturarak SQL Server servisi için kullanılacak olan hesabı değiştirdik. Akabinde, sunucuların ikisine RDP yaparak, SSMS ile diğer bir tanesine bağlanırken bu hatayı almaya başladık. Bağlanma işlemini tam ters yönlü yaptığımızda, yani bağlanırken sorun yaşadığımız sunucudan diğer iki sunucuya yaptığımızda bir hata ile karşılaşmıyorduk.

Hata mesajında SSPI'ı ve SQL Server Error Log'unda yukarıdaki hatayı gördüğümde, sorunun SPN ile ilgili olduğunu anladım. Domain yöneticisi arkadaşımla oraya odaklandık.

ÇÖZÜM:
SPN kayıtlarını SQL Server servis hesabını değiştirmeden önce oluşturmuştuk. Domain yöneticisi arkadaşım iki sunucu ile ilgili kayıtları silip yeniden oluşturmuş, fakat bir sunucunun kaydını güncellemeyi atlamışız. Bu hata mesajını aldığımız sunucu da oydu. Eski SPN kaydını sildik ve yeni SPN kaydını yeni servis hesabını kullanarak oluşturduk, böylece sorun çözülmüş oldu. Artık Error Log'da da SQL Server servis açılışında SPN ile ilgili olması gerektiği gibi aşağıdaki kaydı görüyoruz:

The SQL Server Network Interface library successfully registered the Service Principal Name (SPN) [ MSSQLSvc/..local: ] for the SQL Server service.

Ekrem Önsoy

20 Ekim 2015 Salı

HATA: AlwaysOn AG'de MultiSubnetFailover parametresi kullanıldığında oluşan zaman aşımı sorunu

HATA:
Connection Timeout Expired.  The timeout period elapsed while attempting to consume the pre-login handshake acknowledgement.  This could be because the pre-login handshake failed or the server was unable to respond back in time.  The duration spent while attempting to connect to this server was - [Pre-Login] initialization=63121; handshake=42327;

"Timeout expired.  The timeout period elapsed prior to obtaining a connection from the pool.  This may have occurred because all pooled connections were in use and max pool size was reached."

AÇIKLAMA:
AlwaysOn Availability Group SQL Listener'ınıza aşağıdaki gibi bir Connection String ile bağlanırken, yukarıdaki gibi hatalar alabilirsiniz.

Connection String örneği:
Data Source=tcp:, ; Initial Catalog=; User Id=; Password=; Connection Timeout=60; MultiSubnetFailover=True

ÇÖZÜM:
Ben bu hata ile Windows 7 işletim sistemlerinde karşılaştım. Test ortamımda an itibariyle diğer işletim sistemleri olmadığından deneyemedim, o yüzden onlarda da var mı, yok mu şu anda bilemiyorum.

Sorun, aşağıda paylaştığım Microsoft Knowledge Base'te de anlatıldığı gibi Tdx.sys isimli sürücünün, işlev bir TCP/IP elsıkışması ortasında çağrıldığında Closesocket() isimli işlevi başarıyla kontrol edememesinden kaynaklanıyormuş.

https://support.microsoft.com/en-us/kb/2870437

Eğer Windows 7 kullanıyorsanız ve burada izah ettiğim sorunu yaşıyorsanız, yukarıdaki KB'deki Hotfix'i indirip kurduktan sonra bu sorundan kurtulacaksınız.

Ekrem Önsoy

HATA: Invalid Urn filter on server level: filter must be empty, or server attribute must be equal with the true server name.

HATA:
Invalid Urn filter on server level: filter must be empty, or server attribute must be equal with the true server name.

AÇIKLAMA:
AlwaysOn Availability Group'unuzdaki bir Secondary Replica'yı Primary Replica'dan değil de, başka bir Secondary Replica'dan silmeye çalışırken bu hata mesajıyla karşılaşabilirsiniz.

Güncelleme (23.10.2015):
Bu hatanın daha genel bir hata mesajı olduğunu gördüm. Bu hatayı aldığınız zaman, işlem yapmak istediğiniz sunucuya SQL Listener adıyla değil, doğrudan o SQL Server Instance'ının adını kullanarak bağlanın, o zaman bu hatayı almazsınız.

ÇÖZÜM:
Secondary Replica'yı Primary Replica'ya bağlanarak oradan gruptan çıkarmayı deneyin.

Ekrem Önsoy

13 Ekim 2015 Salı

SQL Server Audit - Filtreleme

Selam millet,

En son SQL Server Auditing özelliğini SQL Server 2008 kullanılan bir ortamda etraflıca kullanmıştım, bugün tekrar böyle bir kurulum yaparken ve yenilikleri incelerken fark ettim ki SQL Server 2012 ile birlikte loglanacak verinin filtrelenebilmesi sağlanmış. Bu loglama, tam olarak veri log dosyasına yazılmadan önce yapılıyor.

Bu ayar, SSMS->Object Explorer->Security->Audits öğesinde ayarladığınız hedef dosyanın özelliklerindeki Filter bölümünden veya T-SQL ile ayarlanabiliyor.


Örneğin yukarıdaki örnekte özellikle 2 Login'e ait işlemlerin loglanmamasını istediğimi belirttim. Bu alanı WHERE'den sonra Predicate'leri tanımlıyoruz gibi düşünün.

İlgili sayfa olan https://msdn.microsoft.com/en-us/library/cc280525(v=sql.110).aspx adresinde bu konuda özet bir bilgi verilmiş ve şöyle denmiş:

"Optionally, on the Filter page, enter a predicate, or WHERE clause, to the server audit to specify additional options not available from the General page. Enclose the predicate in parentheses; for example: (object_name = 'EmployeesTable')."

Yani efendime söyleyeyim "İsterseniz Filter sayfasında bir Predicate veya WHERE cümleciği girebilir ve General sayfasında varolmayan bazı seçenekleri belirtebilirsiniz. Predicate'leri parantez içinde yazın, mesela (object_name = 'EmployeesTable') gibi." diyor.

Peki burada Predicate olarak kullanılabilecek alanlar nelerdir? Nereden bulacağız bu alanları? Aşağıda adresini paylaştığım dokümandaki alanları kullanabilirsiniz:


Kolay gelsin,
Ekrem Önsoy