BLOB'ları ayrı SQL Server tablolarında saklamanız neden önerilir?


28

Bu çok fazla oy alan SO cevabı , başka bir tabloyla yalnızca 1: 1 bir ilişki olsa bile görüntüleri ayrı tablolara koymanızı önerir:

Resimlerinizi bir SQL Server tablosuna koymaya karar verirseniz, bu resimleri saklamak için ayrı bir tablo kullanmanızı şiddetle tavsiye ederim - çalışan fotoğrafını çalışan tablosunda saklamayın - ayrı bir tabloda saklayın. Bu şekilde, Çalışan tablosu, sorularınızın bir parçası olarak her zaman çalışanın fotoğrafını seçmenize gerek kalmayacağını düşünerek, yalın ve ortalama ve çok verimli kalabilir.

Niye ya? SQL Server'ın tablodaki adanmış bir BLOB veri yapısına sadece bir işaretçi koyduğu izlenimi altındaydım, peki neden başka bir dolaylı katmanı oluşturmak için uğraşmıyorsunuz? Gerçekten performansı önemli ölçüde arttırıyor mu? Evet ise neden?

Yanıtlar:


15

BLOB'ların sadece başka bir masada olması gerektiğine katılmıyorum - veritabanında hiç olmamalıdır . Dosyanın diskte bulunduğu yere bir işaretçi koyun ve ardından bunu veritabanından alın ...

Neden oldukları ilk sorun (benim için) endekslemeyle ilgili. Sorgu planları ile XML kullanmak, çünkü herkes var, bir tablo yapalım:

SELECT TOP 1000
ID = IDENTITY(INT,1,1),
deq.query_plan
INTO dbo.index_test
FROM sys.dm_exec_cached_plans AS dec
CROSS APPLY sys.dm_exec_query_plan(dec.plan_handle) AS deq

ALTER TABLE dbo.index_test ADD CONSTRAINT pk_id PRIMARY KEY CLUSTERED (ID)

Sadece 1000 satır, ama boyutlarını kontrol etmek ...

sp_BlitzIndex @DatabaseName = 'StackOverflow', @SchemaName = 'dbo', @TableName = 'index_test'

Sadece 1000 satır için 40 MB'ın üzerinde. Her 1000 satıra 40 MB eklediğinizi varsayalım, bu oldukça çabuk bir şekilde çirkinleşebilir. 1 milyon satıra çarptığınızda ne olur? Orada sadece 1 TB veri var.

FINDIK

Kümelenmiş dizininizi kullanması gereken tüm sorguların artık tüm bu BLOB verilerini belleğe okuması gerekir açıklamasına : BLOB veri sütunu referans alındığında.

SQL Server belleği kullanmanın BLOB'ları depolamaktan daha iyi bir yol olduğunu düşünebiliyor musunuz? Çünkü yapabilirim.

Kümelenmemiş dizinlere genişletme:

CREATE INDEX ix_noblob ON dbo.index_test (ID)

CREATE INDEX ix_returnoftheblob ON dbo.index_test (ID) INCLUDE (query_plan)

Kümelenmemiş dizinlerinizi BLOB sütununu büyük ölçüde önlemek için tasarlayabilirsiniz, böylece düzenli sorgular kümelenmiş dizini engelleyebilir, ancak o BLOB sütununa ihtiyaç duyduğunuz anda kümelenmiş dizine ihtiyacınız vardır.

Olarak eklerseniz, INCLUDEDÖnemli bir arama senaryosundan kaçınmak için, kümelenmemiş bir dizine sütun , devasa kümelenmemiş dizinlerle bitirdiniz:görüntü tanımını buraya girin

Neden oldukları daha fazla sorun:

  • Kimse çalışırsa SELECT * sorgu , tüm bu BLOB verilerini alırlar.
  • Yedekleme ve geri yükleme işlemlerinde yer kaplar ve onları yavaşlatır
  • Yavaşlarlar DBCC CHECKDB , çünkü yolsuzluğu kontrol ettiğinizi biliyorum, değil mi?
  • Ve herhangi bir indeks bakımı yaparsanız, bunu da yavaşlatırlar.

Bu yardımcı olur umarım!


7
Çünkü kullanıcılar genellikle SELECT * yazar.
Brent Ozar

Bahsettiğiniz olumsuz tarafların, resimleri ayrı bir masaya koymasını önerme nedeninin bir parçası olduğunu düşünüyorum. Kullanıcılar hakkında çeşitli raporlar çalıştırıyorsam, resim dosyalarına ihtiyacım yok. Tek bir kullanıcının profil sayfasını yüklüyorsam, o zaman blob masasına katıldığımda değil mi? Burada bir şeyleri özlüyor muyum (yani olumsuz
taraflarınız

11

Bu görüntüler ne kadar büyük ve kaç tane olmasını bekliyorsunuz? Çoğunlukla @ sp_BlitzErik ile aynı fikirdeyim , bunu olduğu bazı senaryolar olduğunu düşünüyorum ve bu yüzden burada gerçekte talep edilen şeyin daha net bir resmini elde yardımcı olacağını düşünüyorum.

Erik'in belirttiği olumsuz yönlerin çoğunu hafifletmek için göz önünde bulundurulması gereken bazı seçenekler şunlardır:

Bu seçeneklerin her ikisi de BLOB'ları SQL Server'da veya tamamen dışında depolamak arasında bir orta yol olacak şekilde tasarlanmıştır (yolu korumak için bir dize colun dışında). BLOB'ların veri modelinin bir parçası olmasına izin verir ve arabellek havuzunda (yani hafıza) boşa harcamazken İşlemlere katılırlar. BLOB verileri hala yedeklemelere dahil edilir, bu da daha fazla yer kaplamalarını ve yedeklemeleri daha uzun sürmelerini sağlar veyenilemek. Ancak, uygulamanın bir parçasıysa, bir şekilde yedeklenmesi gerektiğine ve yalnızca yolu içeren bir dize sütununa sahip olmanın tamamen kesilmesi ve BLOB dosyalarının alınmasına izin vermesi koşuluyla, bunu gerçek bir negatif olarak görmekte zorlanıyorum. DB'de bunun belirtisi olmadan silinir (yani geçersiz işaretçiler / eksik dosyalar). Ayrıca, dosyaların DB içinde "silinmesine" izin verir, ancak yine de temizlenmesi gereken dosya sisteminde de vardır (örneğin baş ağrısı). Ancak, eğer dosyalar BÜYÜK ise, yol sütunu dışında tamamen SQL Server dışında bırakmak en iyisi olabilir.

Bu, "iç veya dış" sorusu ile yardımcı olur, ancak çoklu masa sorusuna karşı tek masaya dokunmaz. Bu özel sorunun ötesinde, tabloları kullanım modellerine göre sütun gruplarına bölmek için kesinlikle geçerli durumlar olduğunu söyleyebilirim. Genellikle, 50 veya daha fazla sütun olduğunda, sıkça erişilen, bazıları olmayan, bazıları vardır. Bazıları çoğunlukla okunurken, bazı sütunlar sık ​​sık yazılır. Sık sık erişilen ve nadiren erişilen sütunların 1: 1 ilişkisi olan birden fazla tabloya ayrılması, genellikle kullanmadığınız veriler için Arabellek Havuzundaki boşluğu neden boşa harcadığı için oldukça faydalıdır (büyük görüntüleri düzenli olarak saklama nedenine benzer)VARBINARY(MAX)sütunlar bir problemdir)? Ayrıca, satır boyutunu küçülterek ve dolayısıyla bir veri sayfasına daha fazla satır sığdırarak, okumaları (hem fiziksel hem de mantıksal) daha verimli hale getirerek sık erişilen sütunların performansını da artırırsınız. Tabii ki, ayrıca PK’yı çoğaltmaya ihtiyaç duyarak da bir miktar verimsizliği ortaya çıkarırsınız ve şimdi bazen bazı sorguları da karmaşıklaştıran (sadece biraz da olsa) iki tabloya katılmanız gerekir.

Dolayısıyla, alabileceğiniz birkaç yaklaşım var ve en iyisi ortamınıza ve başarmaya çalıştığınız şeye bağlı.


SQL Server'ın tablodaki adanmış bir BLOB veri yapısına sadece bir işaretçi koyduğu izlenimi altındaydım.

O kadar basit değil. Burada bazı iyi bilgiler bulabilirsiniz, Varchar, Varbinary, Etc gibi (MAX) Türleri için LOB İşaretçisinin Boyutu Nedir? , ancak temel bilgiler:

  • TEXT,, NTEXTve IMAGEveri türleri (varsayılan olarak): 16 baytlık işaretçi
  • VARCHAR(MAX), NVARCHAR(MAX), VARBINARY(MAX)(Varsayılan olarak):
    • Veri sıraya sığabilecekse, o zaman oraya yerleştirilecektir.
    • Veri yakl. 40.000 bayt (bağlantılı blog yazısı üst sınır olarak 40.000’i gösterir, ancak testim biraz daha yüksek bir değer gösterdi) VE bu yapı için satırda yer varsa, o zaman başlayarak LOB sayfalarına 1 ile 5 arasında doğrudan bağlantı olacaktır. İlk 8000 bayt ilk bağlantı için 24 bayt ve her 8000 bayt ek set için her ek bağlantı için 12 bayt, en fazla 72 bayt.
    • Veri yakl. 40.000 bayt VEYA uygun sayıda doğrudan bağlantıyı saklamak için yeterli yer yok (ör. Satırda yalnızca 40 bayt kaldı ve 20.000 bayt değer, ilk bayt için 24 bayt olan 48 bağlantı için iki ek bağlantı için 12 olan 3 bağlantıya ihtiyaç duyar) toplam satır içi boşluğu gerekli), o zaman sadece LOB sayfalarına bağlantılar içeren bir metin ağacı sayfasına 24 baytlık bir gösterici olacaktır).

7

Verilerin herhangi bir nedenle SQL Server'da saklanması gerekiyorsa, ayrı bir tabloda saklamanın birkaç faydası olduğunu düşünebilirim. Bazıları diğerlerinden daha ikna edici.

  1. Verileri ayrı bir tabloya koymak, ayrı bir veritabanında saklayabileceğiniz anlamına gelir. Bunun planlı bakım için avantajları olabilir. Örneğin, DBCC CHECKDByalnızca BLOB verilerini içeren veritabanında çalıştırabilirsiniz .

  2. BLOB'a her zaman 8000 bayttan fazla koymuyorsanız , bazı satırlar için sıralı olarak depolanması mümkündür . Bunu yapmak istemeyebilirsiniz, çünkü sütun sorgu tarafından gerekli olmasa bile, kümelenmiş dizini kullanarak verilere erişen sorguları yavaşlatır. Verileri ayrı bir tabloya koymak bu riski ortadan kaldırır.

  3. Satır kapalı saklandığında, SQL Server yeni sayfaya işaret etmek için en fazla 24 baytlık işaretçi kullanır. Bu, yer kaplar ve tek bir tabloya ekleyebileceğiniz toplam BLOB sütunu sayısını sınırlar. Daha fazla ayrıntı için srutzky'nin cevabına bakınız.

  4. Kümelenmiş bir sütun deposu dizini, BLOB sütunu içeren bir tabloda tanımlanamaz. Bu sınırlama kaldırıldı, SQL Server 2017'de kaldırılacak.

  5. Sonunda verilerin SQL Server dışına taşınması gerektiğine karar verirseniz, veriler zaten ayrı bir tablodaysa bu değişikliği yapmak daha kolay olabilir.


1
Burada bazı iyi noktalar (+1). Ancak # 3 (satır dışı veriler için re: 24 bayt işaretçisi) hakkında net olmak gerekirse, bu her zaman doğru değildir. Cevabımın alt kısmında veri tipinin, değerin büyüklüğünün ve satırdaki boş alan miktarının işaretçinin boyutunu nasıl belirlediğini açıklarım (kısaca) .
Solomon Rutzky
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.