Mengapa disarankan untuk menyimpan BLOB di tabel SQL Server yang terpisah?

29

Jawaban SO yang sangat mutakhir ini merekomendasikan untuk menempatkan gambar dalam tabel terpisah, meskipun hanya ada hubungan 1: 1 dengan tabel lain:

Jika Anda memutuskan untuk memasukkan gambar Anda ke tabel SQL Server, saya akan sangat menyarankan menggunakan tabel terpisah untuk menyimpan foto-foto itu - jangan menyimpan foto karyawan di tabel karyawan - simpan di tabel terpisah. Dengan begitu, tabel Karyawan dapat tetap ramping dan berarti serta sangat efisien, dengan asumsi Anda tidak selalu perlu memilih foto karyawan juga, sebagai bagian dari kueri Anda.

Mengapa? Saya mendapat kesan bahwa SQL Server hanya menyimpan pointer ke beberapa struktur data BLOB khusus dalam tabel, jadi mengapa repot-repot membuat secara manual lapisan lain tipuan? Apakah itu benar-benar meningkatkan kinerja secara signifikan? Jika ya, mengapa?

Heinzi
sumber

Jawaban:

15

Meskipun saya tidak setuju bahwa BLOB seharusnya ada di tabel lain - mereka tidak boleh ada di database sama sekali . Menyimpan pointer ke tempat file tinggal di disk, dan kemudian dapatkan dari database ...

Masalah utama yang mereka sebabkan (bagi saya) adalah pengindeksan. Menggunakan XML dengan paket kueri, karena semua orang mengerti, mari kita buat tabel:

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)

Ini hanya 1000 baris, tetapi memeriksa ukuran ...

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

Ini lebih dari 40 MB hanya untuk 1000 baris. Dengan asumsi Anda menambahkan 40 MB setiap 1000 baris, itu bisa menjadi sangat jelek dengan cepat. Apa yang terjadi ketika Anda menekan 1 juta baris? Itu hanya sekitar 1 TB data, di sana.

GILA

Setiap pertanyaan yang perlu menggunakan indeks berkerumun Anda sekarang harus membaca semua data BLOB itu menjadi klarifikasi memori : ketika kolom data BLOB direferensikan.

Bisakah Anda memikirkan cara yang lebih baik untuk menggunakan memori SQL Server daripada menyimpan BLOB? Karena saya yakin bisa.

Memperluasnya ke indeks nonclustered:

CREATE INDEX ix_noblob ON dbo.index_test (ID)

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

Anda dapat mendesain indeks nonclustered Anda untuk sebagian besar menghindari kolom BLOB sehingga permintaan reguler dapat menghindari indeks clustered, tetapi segera setelah Anda membutuhkan kolom BLOB itu, Anda memerlukan indeks clustered.

Jika Anda menambahkannya sebagai INCLUDEDkolom pada indeks yang tidak dikelompokkan untuk menghindari skenario pencarian kunci, Anda akan berakhir dengan indeks raksasa yang tidak tercakup:masukkan deskripsi gambar di sini

Lebih banyak masalah yang mereka sebabkan:

  • Jika ada yang menjalankan SELECT *kueri, mereka mendapatkan semua data BLOB itu.
  • Mereka mengambil ruang dalam cadangan dan memulihkan, memperlambatnya
  • Mereka melambat DBCC CHECKDB, karena saya tahu Anda sedang memeriksa korupsi, kan?
  • Dan jika Anda melakukan pemeliharaan indeks, mereka memperlambatnya juga.

Semoga ini membantu!

Erik Darling
sumber
7
Karena pengguna biasanya mengetik SELECT *.
Brent Ozar
Saya pikir kelemahan yang Anda sebutkan adalah bagian dari mengapa ia merekomendasikan menempatkan gambar di meja yang terpisah. Jika saya menjalankan berbagai laporan tentang pengguna, saya tidak memerlukan file gambar mereka. Jika saya memuat halaman profil pengguna tunggal, maka saat itulah saya bergabung dengan tabel blob, kan? Apakah saya kehilangan sesuatu di sini (yaitu apakah kerugian Anda sebenarnya masih berlaku bahkan dalam skenario yang saya uraikan ini?)
BVernon
11

Seberapa besar gambar-gambar ini, dan berapa banyak yang Anda harapkan? Sementara saya sebagian besar setuju dengan @sp_BlitzErik , saya pikir ada beberapa skenario di mana itu ok untuk melakukan ini, dan itu akan membantu untuk memiliki gambaran yang lebih jelas tentang apa yang sebenarnya diminta di sini.

Beberapa opsi untuk dipertimbangkan yang mengurangi sebagian besar aspek negatif yang ditunjukkan oleh Erik adalah:

Kedua opsi ini dirancang sebagai jalan tengah antara menyimpan BLOB sepenuhnya di SQL Server atau sepenuhnya di luar (kecuali untuk string colun untuk mempertahankan jalur). Mereka memungkinkan BLOB untuk menjadi bagian dari model data dan berpartisipasi dalam Transaksi sambil tidak membuang-buang ruang dalam buffer pool (yaitu memori). Data BLOB masih termasuk dalam cadangan, yang membuatnya mengambil lebih banyak ruang dan lebih lama untuk mencadangkan danuntuk mengembalikan. Namun, saya mengalami kesulitan melihat ini sebagai benar negatif mengingat bahwa jika itu adalah bagian dari aplikasi maka perlu didukung entah bagaimana, dan hanya memiliki kolom string yang berisi path sepenuhnya terputus dan memungkinkan file BLOB untuk mendapatkan dihapus tanpa indikasi tentang hal itu di DB (yaitu pointer tidak valid / file hilang). Ini juga memungkinkan file untuk "dihapus" dalam DB tetapi masih ada pada sistem file yang pada akhirnya perlu dibersihkan (yaitu sakit kepala). Tetapi, jika file-file tersebut BESAR, maka mungkin yang terbaik adalah meninggalkan seluruhnya di luar SQL Server kecuali untuk kolom path.

Itu membantu dengan pertanyaan "di dalam atau di luar", tetapi tidak menyentuh pada tabel tunggal vs beberapa pertanyaan tabel. Saya dapat mengatakan bahwa, di luar pertanyaan spesifik ini, pasti ada kasus yang valid untuk memecah tabel menjadi kelompok kolom berdasarkan pola penggunaan. Seringkali ketika seseorang memiliki 50 kolom atau lebih ada beberapa yang sering diakses dan beberapa yang tidak. Beberapa kolom sering ditulis sementara sebagian besar dibaca. Memisahkan kolom yang sering diakses vs yang jarang diakses ke dalam beberapa tabel yang memiliki hubungan 1: 1 cukup sering menguntungkan karena mengapa membuang ruang dalam Buffer Pool untuk data yang mungkin tidak Anda gunakan (mirip dengan mengapa menyimpan gambar besar secara teraturVARBINARY(MAX)kolom adalah masalah)? Anda juga meningkatkan kinerja kolom yang sering diakses dengan mengurangi ukuran baris dan karenanya memasang lebih banyak baris ke halaman data, menjadikan pembacaan (baik fisik dan logis) lebih efisien. Tentu saja, Anda juga memperkenalkan beberapa inefisiensi dengan perlu menduplikasi PK, dan sekarang kadang-kadang Anda harus bergabung dengan dua tabel, yang juga menyulitkan (bahkan jika hanya sedikit) beberapa pertanyaan.

Jadi, ada beberapa pendekatan yang bisa Anda ambil, dan apa yang terbaik tergantung pada lingkungan Anda dan apa yang ingin Anda capai.


Saya mendapat kesan bahwa SQL Server hanya menyimpan pointer ke beberapa struktur data BLOB khusus dalam tabel

Tidak sesederhana itu. Anda dapat menemukan beberapa info bagus di sini, Berapa Ukuran Pointer LOB untuk (MAX) Jenis Seperti Varchar, Varbinary, Etc? , tetapi dasarnya adalah:

  • TEXT,, NTEXTdan IMAGEtipe data (secara default): 16 byte pointer
  • VARCHAR(MAX), NVARCHAR(MAX), VARBINARY(MAX)(Secara default):
    • Jika data bisa masuk baris, maka akan ditempatkan di sana
    • Jika data kurang dari kira-kira. 40.000 byte (posting blog tertaut menunjukkan 40.000 sebagai batas atas tetapi pengujian saya menunjukkan nilai yang sedikit lebih tinggi) DAN jika ada ruang di baris untuk struktur ini, maka akan ada antara 1 dan 5 tautan langsung ke halaman LOB, mulai dari 24 byte untuk tautan pertama ke 8000 byte pertama, dan naik 12 byte per setiap tautan tambahan untuk setiap rangkaian tambahan 8000 byte, hingga maksimal 72 byte byte.
    • Jika data lebih dari sekitar. 40.000 byte ATAU tidak ada ruang yang cukup untuk menyimpan jumlah tautan langsung yang sesuai (mis., Hanya 40 byte yang tersisa di baris dan nilai 20.000 byte membutuhkan 3 tautan, yaitu 24 byte untuk yang pertama ditambah 12 untuk dua tautan tambahan untuk 48 byte total diperlukan dalam ruang baris), maka hanya akan ada pointer 24 byte ke halaman pohon teks yang berisi tautan ke halaman LOB).
Solomon Rutzky
sumber
7

Jika data harus disimpan dalam SQL Server untuk alasan apa pun saya dapat memikirkan beberapa manfaat untuk menyimpannya dalam tabel terpisah. Beberapa lebih meyakinkan daripada yang lain.

  1. Menempatkan data dalam tabel terpisah berarti Anda dapat menyimpannya dalam database terpisah. Ini dapat memiliki keuntungan untuk pemeliharaan terjadwal. Misalnya, Anda DBCC CHECKDBhanya dapat berjalan di database yang berisi data BLOB.

  2. Jika Anda tidak selalu memasukkan lebih dari 8000 byte ke dalam BLOB, maka dimungkinkan untuk disimpan dalam baris untuk beberapa baris. Anda mungkin tidak menginginkannya karena itu akan memperlambat permintaan yang mengakses data menggunakan indeks berkerumun bahkan jika kolom tidak diperlukan oleh kueri. Menempatkan data dalam tabel terpisah menghilangkan risiko ini.

  3. Ketika disimpan dari baris SQL Server menggunakan pointer hingga 24 byte untuk menunjuk ke halaman baru. Itu memakan ruang dan membatasi jumlah kolom BLOB yang bisa Anda tambahkan ke satu tabel. Lihat jawaban srutzky untuk lebih jelasnya.

  4. Indeks toko kolom berkerumun tidak dapat didefinisikan pada tabel yang berisi kolom BLOB. Batasan ini telah dihapus akan dihapus di SQL Server 2017.

  5. Jika Anda akhirnya memutuskan bahwa data harus dipindahkan di luar SQL Server mungkin lebih mudah untuk melakukan perubahan itu jika data sudah dalam tabel terpisah.

Joe Obbish
sumber
1
Beberapa poin bagus di sini (+1). Tetapi untuk menjadi jelas tentang # 3 (re: 24 byte pointer untuk data off-row), itu tidak selalu benar. Saya jelaskan (secara singkat) di bagian bawah jawaban saya bagaimana tipe data, ukuran nilai, dan jumlah ruang kosong pada baris menentukan ukuran pointer.
Solomon Rutzky