LOB_DATA, pemindaian tabel lambat, dan beberapa pertanyaan I / O


19

Saya memiliki tabel yang agak besar dengan salah satu kolom menjadi data XML dengan ukuran rata-rata entri XML ~ 15 kilobyte. Semua kolom lainnya adalah int reguler, bigint, GUID, dll. Untuk memiliki angka konkret, misalkan tabel memiliki sejuta baris dan berukuran ~ 15 GB.

Apa yang saya perhatikan adalah bahwa tabel ini sangat lambat untuk memilih data jika saya ingin memilih semua kolom. Kapan saya melakukannya

SELECT TOP 1000 * FROM TABLE

dibutuhkan sekitar 20-25 detik untuk membaca data dari disk - meskipun saya tidak memaksakan pemesanan pada hasilnya. Saya menjalankan query dengan cache dingin (yaitu setelah DBCC DROPCLEANBUFFERS). Inilah hasil statistik IO:

Pindai hitungan 1, bacaan logis 364, bacaan fisik 24, bacalah bacaan 7191, lob logis bacalah 7924, lob fisik bacaan 1690, bacus bacalah bacaan 3968.

Itu mengambil ~ 15 MB data. Rencana pelaksanaan menunjukkan Pemindaian Indeks Berkelompok seperti yang saya harapkan.

Tidak ada IO yang terjadi pada disk selain pertanyaan saya; Saya juga telah memeriksa bahwa fragmentasi indeks berkerumun dekat dengan 0%. Ini adalah drive SATA tingkat konsumen, namun saya masih berpikir SQL Server akan dapat memindai tabel lebih cepat dari ~ 100-150 MB / menit.

Kehadiran bidang XML menyebabkan sebagian besar data tabel berada di halaman LOB_DATA (bahkan ~ 90% dari halaman tabel adalah LOB_DATA).

Saya kira pertanyaan saya adalah - apakah saya benar dalam berpikir bahwa halaman LOB_DATA dapat menyebabkan pemindaian lambat bukan hanya karena ukurannya, tetapi juga karena SQL Server tidak dapat memindai indeks yang dikelompokkan secara efektif ketika ada banyak halaman LOB_DATA di tabel?

Bahkan lebih luas lagi - apakah dianggap layak untuk memiliki struktur tabel / pola data seperti itu? Rekomendasi untuk menggunakan Filestream biasanya menyatakan ukuran bidang yang jauh lebih besar, jadi saya tidak benar-benar ingin menempuh rute itu. Saya belum benar-benar menemukan info bagus tentang skenario khusus ini.

Saya sudah memikirkan kompresi XML, tetapi perlu dilakukan pada klien atau dengan SQLCLR dan akan membutuhkan beberapa pekerjaan untuk diimplementasikan dalam sistem.

Saya mencoba kompresi, dan karena XML sangat redundan, saya dapat (dalam aplikasi ac #) mengkompresi XML dari 20KB menjadi ~ 2.5KB dan menyimpannya dalam kolom VARBINARY, mencegah penggunaan halaman data LOB. Ini mempercepat SELECT 20x kali dalam pengujian saya.


Alex: tidak yakin apakah Anda melihat diskusi yang terkait dengan jawaban saya (tautan ada di komentar di bawah jawaban saya), tetapi saya bisa mendekati untuk mereproduksi skenario Anda. Saya mengisi tabel yang cocok (sebanyak yang saya punya info) deskripsi Anda dan mendapat statistik I / O yang sangat mirip. Kecuali, "LOB Physical Reads" bahkan tidak pernah dekat. Jadi saya bertanya-tanya apakah Anda memperbarui XML (tetapi bukan kolom lainnya) dan / atau memiliki banyak fragmentasi fisik file data Anda. Saya masih tidak keberatan mendapatkan DDL dari meja Anda dan pengaturan pertumbuhan otomatis Anda untuk setiap file data, dan apakah Anda mengecilkan file data Anda?
Solomon Rutzky

Pertama-tama - terima kasih banyak untuk jawaban terinci, saya tidak dapat berpartisipasi dalam diskusi pada saat itu karena kurangnya waktu. Sekarang Anda menyebutkan ini (saya tidak memikirkannya ketika mengajukan pertanyaan) - Bidang XML diperbarui beberapa kali setelah dibuat, dan dibuat kecil. Jadi saya akan curiga bahwa awalnya disimpan dalam baris, dan setelah beberapa pembaruan itu dipindahkan ke struktur halaman LOB, dan kemudian mendapat beberapa pembaruan lagi.
Alexander Shelemin

(Lanjutan) Saya memeriksa fragmentasi fisik file sebelum mengajukan pertanyaan, dan alat Windows bawaan berpikir bahwa itu OK, jadi saya tidak memeriksanya lebih jauh. Pertumbuhan otomatis adalah default, menurut 1 MB saya percaya, dan file data belum menyusut.
Alexander Shelemin

Pilih top 1000 * sangat penting dalam kasus khusus saya. Saya tentu mengerti bahwa ini dianggap praktik yang buruk, namun beberapa keputusan desain aplikasi sangat sulit untuk diubah setelah mereka berada di tempat untuk waktu yang lama. Select * pada dasarnya digunakan sebagai strategi replikasi lintas-database antara berbagai komponen di aplikasi kami. Ada kelebihan untuk itu, misalnya kita dapat melakukan banyak manipulasi sewenang-wenang dengan data / skema on the fly, yang akan sulit dengan teknik replikasi bawaan, tetapi ia datang dengan masalah-masalahnya.
Alexander Shelemin

Alex, SELECT *bukan masalah jika Anda membutuhkan data XML. Ini hanya masalah jika Anda tidak ingin data XML, dalam hal ini mengapa memperlambat permintaan untuk mendapatkan kembali data yang tidak Anda gunakan? Saya bertanya tentang pembaruan XML yang bertanya-tanya apakah fragmentasi pada halaman LOB tidak dilaporkan secara akurat. Itulah mengapa saya bertanya dalam jawaban saya bagaimana tepatnya Anda menentukan bahwa indeks yang dikelompokkan tidak terfragmentasi? Bisakah Anda memberikan perintah yang Anda jalankan? Dan sudahkah Anda melakukan REBUILD penuh pada Indeks Clustered? (lanjutan)
Solomon Rutzky

Jawaban:


11

Kehadiran bidang XML menyebabkan sebagian besar data tabel berada di halaman LOB_DATA (bahkan ~ 90% dari halaman tabel adalah LOB_DATA).

Hanya memiliki kolom XML di tabel tidak memiliki efek itu. Keberadaan data XML yang, dalam kondisi tertentu , menyebabkan sebagian data baris disimpan di luar baris, pada halaman LOB_DATA. Dan sementara satu (atau mungkin beberapa ;-) mungkin berpendapat bahwa ya, XMLkolom menyiratkan bahwa memang akan ada data XML, itu tidak dijamin bahwa data XML perlu disimpan dari baris: kecuali baris cukup banyak sudah diisi di luar data XML apa pun, dokumen kecil (hingga 8000 byte) mungkin sesuai dan tidak pernah masuk ke halaman LOB_DATA.

apakah saya benar dalam berpikir bahwa halaman LOB_DATA dapat menyebabkan pemindaian lambat bukan hanya karena ukurannya, tetapi juga karena SQL Server tidak dapat memindai indeks berkerumun secara efektif ketika ada banyak halaman LOB_DATA dalam tabel?

Pemindaian mengacu pada melihat semua baris. Tentu saja, ketika halaman data dibaca, semua data in-row dibaca, bahkan jika Anda memilih subset dari kolom. Perbedaannya dengan data LOB adalah bahwa jika Anda tidak memilih kolom itu, maka data offline tidak akan dibaca. Oleh karena itu tidak benar-benar adil untuk menarik kesimpulan tentang seberapa efisien SQL Server dapat memindai Indeks Clustered ini karena Anda tidak benar-benar menguji itu (atau Anda menguji setengahnya). Anda memilih semua kolom, yang termasuk kolom XML, dan seperti yang Anda sebutkan, di situlah sebagian besar data berada.

Jadi kita sudah tahu bahwa SELECT TOP 1000 *tes tersebut tidak hanya membaca serangkaian halaman data 8k, semuanya berturut-turut, tetapi melompat ke lokasi lain per setiap baris . Struktur pasti dari data LOB tersebut dapat bervariasi berdasarkan pada seberapa besar itu. Berdasarkan penelitian yang ditunjukkan di sini ( Berapa Ukuran Pointer LOB untuk (MAX) Jenis Seperti Varchar, Varbinary, Etc? ), Ada dua jenis alokasi LOB offline:

  1. Inline Root - untuk data antara 8001 dan 40.000 (benar-benar 42.000) byte, ruang memungkinkan, akan ada 1 hingga 5 pointer (24 - 72 byte) DALAM ROW yang mengarah langsung ke halaman LOB.
  2. TEXT_TREE - untuk data lebih dari 42.000 byte, atau jika 1 hingga 5 pointer tidak dapat dimasukkan secara berturut-turut, maka hanya akan ada pointer 24 byte ke halaman awal dari daftar pointer ke halaman LOB (yaitu " text_tree "halaman).

Salah satu dari dua situasi ini terjadi setiap kali Anda mengambil data LOB yang lebih dari 8000 byte atau hanya tidak sesuai di baris. Saya memposting skrip pengujian pada PasteBin.com (skrip T-SQL untuk menguji alokasi LOB dan membaca ) yang menunjukkan 3 jenis alokasi LOB (berdasarkan ukuran data) serta efek masing-masing memiliki pada logis dan berbunyi secara fisik. Dalam kasus Anda, jika data XML benar-benar kurang dari 42.000 byte per baris, maka tidak satu pun (atau sangat sedikit) yang seharusnya berada dalam struktur TEXT_TREE paling tidak efisien.

Jika Anda ingin menguji seberapa cepat SQL Server dapat memindai Indeks Clustered itu, lakukan SELECT TOP 1000tetapi tentukan satu atau lebih kolom yang tidak termasuk kolom XML itu. Bagaimana hal itu memengaruhi hasil Anda? Seharusnya sedikit lebih cepat.

apakah masuk akal untuk memiliki struktur tabel / pola data seperti itu?

Mengingat kami memiliki deskripsi yang tidak lengkap tentang struktur tabel aktual dan pola data, jawaban apa pun mungkin tidak optimal tergantung pada detail yang hilang tersebut. Dengan pemikiran itu, saya akan mengatakan bahwa tidak ada yang jelas tidak masuk akal tentang struktur tabel atau pola data Anda.

Saya dapat (dalam aplikasi # ac) mengompresi XML dari 20KB ke ~ 2.5KB dan menyimpannya dalam kolom VARBINARY, mencegah penggunaan halaman data LOB. Ini mempercepat SELECT 20x kali dalam pengujian saya.

Itu membuat memilih semua kolom, atau bahkan hanya data XML (sekarang masuk VARBINARY) lebih cepat, tetapi sebenarnya menyakiti permintaan yang tidak memilih data "XML". Dengan asumsi Anda memiliki sekitar 50 byte di kolom lain dan memiliki FILLFACTOR100, maka:

  • Tanpa Kompresi: 15k XMLdata harus memerlukan 2 halaman LOB_DATA, yang kemudian membutuhkan 2 petunjuk untuk Inline Root. Pointer pertama adalah 24 byte dan yang kedua adalah 12, untuk total 36 byte yang disimpan dalam baris untuk data XML. Ukuran baris total adalah 86 byte, dan Anda dapat memuat sekitar 93 baris tersebut ke halaman data 8060 byte. Karenanya, 1 juta baris membutuhkan 10.753 halaman data.

  • Kompresi Kustom: 2.5k VARBINARYdata akan sesuai di baris. Ukuran baris total adalah 2610 (2,5 * 1024 = 2560) byte, dan Anda hanya bisa memasukkan 3 baris tersebut ke halaman data 8060 byte. Karenanya, 1 juta baris membutuhkan 333.334 halaman data.

Ergo, menerapkan hasil kompresi khusus dalam peningkatan 30x halaman data untuk Indeks Clustered. Artinya, semua kueri yang menggunakan pemindaian Indeks Clustered sekarang memiliki sekitar 322.500 lebih banyak halaman data untuk dibaca. Silakan lihat bagian terperinci di bawah ini untuk konsekuensi tambahan dari melakukan jenis kompresi ini.

Saya akan memperingatkan untuk tidak melakukan refactoring apa pun berdasarkan kinerja SELECT TOP 1000 *. Itu sepertinya bukan permintaan yang bahkan akan dikeluarkan aplikasi, dan tidak boleh digunakan sebagai satu-satunya dasar untuk optimasi yang mungkin tidak perlu.

Untuk info lebih rinci dan lebih banyak tes untuk mencoba, silakan lihat bagian di bawah ini.


Pertanyaan ini tidak dapat diberikan jawaban yang pasti, tetapi setidaknya kita dapat membuat beberapa kemajuan dan menyarankan penelitian tambahan untuk membantu menggerakkan kita lebih dekat untuk mencari tahu masalah yang sebenarnya (idealnya berdasarkan bukti).

Apa yang kita ketahui:

  1. Tabel memiliki sekitar 1 juta baris
  2. Ukuran meja sekitar 15 GB
  3. Tabel berisi satu XMLkolom dan beberapa kolom lain dari jenis: INT, BIGINT, UNIQUEIDENTIFIER, "dll"
  4. XMLKolom "ukuran" adalah, rata-rata sekitar 15k
  5. Setelah berjalan DBCC DROPCLEANBUFFERS, dibutuhkan 20 - 25 detik untuk menyelesaikan kueri berikut:SELECT TOP 1000 * FROM TABLE
  6. Indeks Clustered sedang dipindai
  7. Fragmentasi pada Indeks Berkelompok dekat dengan 0%

Apa yang kami pikir kami tahu:

  1. Tidak ada aktivitas disk lain di luar kueri ini. Apakah kamu yakin Bahkan jika tidak ada permintaan pengguna lain, apakah ada operasi latar belakang yang terjadi? Apakah ada proses eksternal ke SQL Server yang berjalan pada mesin yang sama yang bisa mengambil beberapa IO? Mungkin tidak ada, tetapi tidak jelas hanya berdasarkan info yang diberikan.
  2. 15 MB data XML sedang dikembalikan. Berdasarkan apa nomor ini? Perkiraan yang diperoleh dari 1000 baris kali rata-rata 15rb data XML per baris? Atau agregasi terprogram dari apa yang diterima untuk permintaan itu? Jika itu hanya perkiraan, saya tidak akan bergantung padanya karena distribusi data XML mungkin tidak bahkan dengan cara yang tersirat oleh rata-rata sederhana.
  3. Kompresi XML mungkin membantu. Bagaimana tepatnya Anda melakukan kompresi di .NET? Melalui kelas GZipStream atau DeflateStream ? Ini bukan opsi tanpa biaya. Ini tentu akan memampatkan beberapa data dengan persentase besar, tetapi juga akan membutuhkan lebih banyak CPU karena Anda akan memerlukan proses tambahan untuk mengompres / mendekompresi data setiap kali. Paket ini juga akan sepenuhnya menghilangkan kemampuan Anda untuk:

    • query data XML melalui .nodes, .value, .query, dan .modifyfungsi XML.
    • indeks data XML.

      Harap diingat (karena Anda menyebutkan bahwa XML "sangat redundan") bahwa XMLdatatype sudah dioptimalkan karena ia menyimpan elemen dan nama atribut dalam kamus, menetapkan ID indeks integer untuk setiap item, dan kemudian menggunakan ID integer itu di seluruh dokumen (karenanya tidak mengulangi nama lengkap per setiap penggunaan, juga tidak mengulanginya lagi sebagai tag penutup untuk elemen). Data aktual juga memiliki ruang putih asing yang dihapus. Inilah sebabnya mengapa dokumen XML yang diekstraksi tidak mempertahankan struktur aslinya dan mengapa elemen kosong mengekstraksi <element />bahkan jika mereka masuk sebagai<element></element>. Jadi setiap keuntungan dari mengompresi melalui GZip (atau apa pun) hanya akan ditemukan dengan mengompresi nilai elemen dan / atau atribut, yang merupakan area permukaan yang jauh lebih kecil yang dapat ditingkatkan daripada yang diperkirakan kebanyakan orang, dan kemungkinan besar tidak sebanding dengan hilangnya kemampuan seperti disebutkan secara langsung di atas.

      Harap juga diingat bahwa mengompresi data XML dan menyimpan VARBINARY(MAX)hasilnya tidak akan menghilangkan akses LOB, itu hanya akan mengurangi itu. Bergantung pada ukuran data lainnya pada baris, nilai yang dikompresi mungkin cocok dalam baris, atau mungkin masih membutuhkan halaman LOB.

Informasi itu, walaupun bermanfaat, tidak cukup. Ada banyak faktor yang memengaruhi kinerja kueri, jadi kita perlu gambaran yang lebih rinci tentang apa yang terjadi.

Apa yang tidak kita ketahui, tetapi perlu:

  1. Mengapa kinerja SELECT *materi? Apakah ini pola yang Anda gunakan dalam kode. Jika demikian, mengapa?
  2. Apa kinerja memilih hanya kolom XML? Apa statistik dan waktu jika Anda melakukan hal: SELECT TOP 1000 XmlColumn FROM TABLE;?
  3. Berapa banyak dari 20 - 25 detik yang diperlukan untuk mengembalikan 1000 baris ini terkait dengan faktor-faktor jaringan (mendapatkan data melalui kabel), dan berapa banyak yang terkait dengan faktor-faktor klien (rendering sekitar 15 MB ditambah sisa non- Data XML ke dalam grid di SSMS, atau mungkin disimpan ke disk)?

    Anjak kedua aspek operasi ini kadang-kadang dapat dilakukan dengan tidak mengembalikan data. Sekarang, orang mungkin berpikir untuk memilih ke dalam Tabel Sementara atau Tabel Variabel, tetapi ini hanya akan memperkenalkan beberapa variabel baru (yaitu disk I / O untuk tempdb, tulis Transaction Log, kemungkinan tumbuhnya otomatis data tempdb dan / atau file log, perlu ruang di Buffer Pool, dll). Semua faktor baru itu sebenarnya dapat meningkatkan waktu kueri. Sebagai gantinya, saya biasanya menyimpan kolom ke variabel (dari tipe data yang sesuai; tidak SQL_VARIANT) yang ditimpa dengan setiap baris baru (yaitu SELECT @Column1 = tab.Column1,...).

    NAMUN , seperti yang ditunjukkan oleh @PaulWhite dalam DBA ini. Tanya Jawab T&J, Logika berbunyi berbeda ketika mengakses data LOB yang sama , dengan penelitian tambahan yang saya posting di PasteBin ( skrip T-SQL untuk menguji berbagai skenario untuk pembacaan LOB ) , LOB tidak diakses secara konsisten antara SELECT, SELECT INTO, SELECT @XmlVariable = XmlColumn, SELECT @XmlVariable = XmlColumn.query(N'/'), dan SELECT @NVarCharVariable = CONVERT(NVARCHAR(MAX), XmlColumn). Jadi pilihan kita sedikit lebih terbatas di sini, tetapi di sini adalah apa yang bisa dilakukan:

    1. Singkirkan masalah jaringan dengan menjalankan kueri di server yang menjalankan SQL Server, baik dalam SSMS atau SQLCMD.EXE.
    2. Singkirkan masalah klien di SSMS dengan masuk ke Opsi Kueri -> Hasil -> Kisi dan centang opsi untuk "Buang hasil setelah eksekusi". Harap dicatat bahwa opsi ini akan mencegah SEMUA output, termasuk pesan, tetapi masih dapat berguna untuk mengesampingkan waktu yang dibutuhkan SSMS untuk mengalokasikan memori per setiap baris dan kemudian menggambarnya di grid.
      Atau, Anda bisa mengeksekusi query melalui sqlcmd.exe dan mengarahkan output untuk pergi ke mana-mana melalui: -o NUL:.
  4. Apakah ada Tipe Tunggu yang dikaitkan dengan permintaan ini? Jika ya, Jenis Tunggu apa itu?
  5. Berapa ukuran data aktual untuk XMLkolom yang dikembalikan ? Ukuran rata-rata kolom di seluruh tabel tidak terlalu menjadi masalah jika baris "TOP 1000" berisi sebagian besar dari total XMLdata secara tidak proporsional . Jika Anda ingin tahu tentang baris TOP 1000, maka lihatlah baris itu. Silakan jalankan yang berikut ini:

    SELECT TOP 1000 tab.*,
           SUM(DATALENGTH(tab.XmlColumn)) / 1024.0 AS [TotalXmlKBytes],
           AVG(DATALENGTH(tab.XmlColumn)) / 1024.0 AS [AverageXmlKBytes]
           STDEV(DATALENGTH(tab.XmlColumn)) / 1024.0 AS [StandardDeviationForXmlKBytes]
    FROM   SchemaName.TableName tab;
  6. The tepat tabel skema. Harap berikan pernyataan lengkap CREATE TABLE , termasuk semua indeks.
  7. Paket pertanyaan? Apakah itu sesuatu yang dapat Anda posting? Info itu mungkin tidak akan mengubah apa pun, tetapi lebih baik untuk mengetahui bahwa itu tidak akan terjadi daripada menebak bahwa itu tidak akan dan salah ;-)
  8. Apakah ada fragmentasi fisik / eksternal pada file data? Meskipun ini mungkin bukan faktor besar di sini, karena Anda menggunakan "SATA tingkat konsumen" dan bukan SSD atau bahkan Super-Mahal SATA, efek dari sektor yang dipesan secara tidak optimal akan lebih terlihat, terutama karena jumlah sektor tersebut yang perlu dibaca meningkat.
  9. Apa hasil pasti dari kueri berikut:

    SELECT * FROM sys.dm_db_index_physical_stats(DB_ID(),
                              OBJECT_ID(N'dbo.SchemaName.TableName'), 1, 0, N'LIMITED');

MEMPERBARUI

Terpikir oleh saya bahwa saya harus mencoba mereproduksi skenario ini untuk melihat apakah saya mengalami perilaku yang sama. Jadi, saya membuat tabel dengan beberapa kolom (mirip dengan deskripsi yang tidak jelas dalam Pertanyaan), dan kemudian mengisinya dengan 1 juta baris, dan kolom XML memiliki sekitar 15k data per baris (lihat kode di bawah).

Apa yang saya temukan adalah melakukan SELECT TOP 1000 * FROM TABLEselesai dalam 8 detik pertama kali, dan 2 - 4 detik setiap kali sesudahnya (ya, mengeksekusi DBCC DROPCLEANBUFFERSsebelum menjalankan setiap SELECT *query). Dan laptop saya yang berusia beberapa tahun tidak cepat: Edisi Pengembang SQL Server 2012 SP2, 64 bit, RAM 6 GB, dual 2,5 Ghz Core i5, dan drive SATA 5400 RPM. Saya juga menjalankan SSMS 2014, SQL Server Express 2014, Chrome, dan beberapa hal lainnya.

Berdasarkan waktu respons sistem saya, saya akan mengulangi bahwa kami memerlukan lebih banyak info (yaitu spesifik tentang tabel dan data, hasil tes yang disarankan, dll) untuk membantu mempersempit penyebab waktu respons 20 - 25 detik yang Anda lihat.

SET ANSI_NULLS, NOCOUNT ON;
GO

IF (OBJECT_ID(N'dbo.XmlReadTest') IS NOT NULL)
BEGIN
    PRINT N'Dropping table...';
    DROP TABLE dbo.XmlReadTest;
END;

PRINT N'Creating table...';
CREATE TABLE dbo.XmlReadTest 
(
    ID INT NOT NULL IDENTITY(1, 1),
    Col2 BIGINT,
    Col3 UNIQUEIDENTIFIER,
    Col4 DATETIME,
    Col5 XML,
    CONSTRAINT [PK_XmlReadTest] PRIMARY KEY CLUSTERED ([ID])
);
GO

DECLARE @MaxSets INT = 1000,
        @CurrentSet INT = 1;

WHILE (@CurrentSet <= @MaxSets)
BEGIN
    RAISERROR(N'Populating data (1000 sets of 1000 rows); Set # %d ...',
              10, 1, @CurrentSet) WITH NOWAIT;
    INSERT INTO dbo.XmlReadTest (Col2, Col3, Col4, Col5)
        SELECT  TOP 1000
                CONVERT(BIGINT, CRYPT_GEN_RANDOM(8)),
                NEWID(),
                GETDATE(),
                N'<test>'
                  + REPLICATE(CONVERT(NVARCHAR(MAX), CRYPT_GEN_RANDOM(1), 2), 3750)
                  + N'</test>'
        FROM        [master].[sys].all_columns sac1;

    IF ((@CurrentSet % 100) = 0)
    BEGIN
        RAISERROR(N'Executing CHECKPOINT ...', 10, 1) WITH NOWAIT;
        CHECKPOINT;
    END;

    SET @CurrentSet += 1;
END;

--

SELECT COUNT(*) FROM dbo.XmlReadTest; -- Verify that we have 1 million rows

-- O.P. states that the "clustered index fragmentation is close to 0%"
ALTER INDEX [PK_XmlReadTest] ON dbo.XmlReadTest REBUILD WITH (FILLFACTOR = 90);
CHECKPOINT;

--

DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;

SET STATISTICS IO, TIME ON;
SELECT TOP 1000 * FROM dbo.XmlReadTest;
SET STATISTICS IO, TIME OFF;

/*
Scan count 1, logical reads 21,       physical reads 1,     read-ahead reads 4436,
              lob logical reads 5676, lob physical reads 1, lob read-ahead reads 3967.

 SQL Server Execution Times:
   CPU time = 171 ms,  elapsed time = 8329 ms.
*/

Dan, karena kami ingin memperhitungkan waktu yang diperlukan untuk membaca halaman non-LOB, saya menjalankan kueri berikut untuk memilih semua kecuali kolom XML (salah satu tes yang saya sarankan di atas). Ini kembali dalam 1,5 detik cukup konsisten.

DBCC DROPCLEANBUFFERS WITH NO_INFOMSGS;

SET STATISTICS IO, TIME ON;
SELECT TOP 1000 ID, Col2, Col3, Col4 FROM dbo.XmlReadTest;
SET STATISTICS IO, TIME OFF;

/*
Scan count 1, logical reads 21,    physical reads 1,     read-ahead reads 4436,
              lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 1666 ms.
*/

Kesimpulan (untuk saat ini)
Berdasarkan upaya saya untuk membuat ulang skenario Anda, saya tidak berpikir kita dapat menunjuk ke drive SATA atau I / O non-sekuensial sebagai penyebab utama 20 - 25 detik, terutama karena kita masih tidak tahu seberapa cepat kueri kembali ketika tidak termasuk kolom XML. Dan saya tidak dapat mereproduksi sejumlah besar Logical Reads (non-LOB) yang Anda tampilkan, tetapi saya merasa bahwa saya perlu menambahkan lebih banyak data ke setiap baris dengan mengingatnya dan pernyataan:

~ 90% halaman tabel adalah LOB_DATA

Tabel saya memiliki 1 juta baris, masing-masing memiliki lebih dari sys.dm_db_index_physical_stats15rb data XML, dan menunjukkan bahwa ada 2 juta halaman LOB_DATA. 10% sisanya akan menjadi 222k IN_ROW halaman data, namun saya hanya memiliki 11.630 halaman. Jadi sekali lagi, kita memerlukan lebih banyak info mengenai skema tabel aktual dan data aktual.



10

saya benar dalam berpikir bahwa halaman LOB_DATA dapat menyebabkan pemindaian lambat bukan hanya karena ukurannya, tetapi juga karena SQL Server tidak dapat memindai indeks berkerumun secara efektif

Ya, membaca data LOB yang tidak disimpan dalam baris mengarah ke IO acak, bukan IO berurutan. Metrik kinerja disk yang digunakan di sini untuk memahami mengapa ia cepat atau lambat adalah Random Read IOPS.

Data LOB disimpan dalam struktur pohon di mana halaman data dalam indeks berkerumun menunjuk ke halaman Data LOB dengan struktur akar LOB yang pada gilirannya menunjuk ke data LOB yang sebenarnya. Ketika melintasi node root dalam indeks cluster SQL Server hanya bisa mendapatkan data in-row dengan membaca berurutan. Untuk mendapatkan data LOB, SQL Server harus pergi ke tempat lain pada disk.

Saya kira jika Anda mengubah ke disk SSD, Anda tidak akan menderita sebanyak ini karena IOPS acak untuk SSD jauh lebih tinggi daripada disk berputar.

apakah masuk akal untuk memiliki struktur tabel / pola data seperti itu?

Ya bisa. Bergantung pada apa yang dilakukan tabel ini untuk Anda.

Biasanya masalah kinerja dengan XML dalam SQL Server terjadi ketika Anda ingin menggunakan T-SQL untuk permintaan ke XML dan bahkan lebih ketika Anda ingin menggunakan nilai-nilai dari XML dalam predikat di mana klausa atau bergabung. Jika itu masalahnya Anda bisa melihat-lihat promosi properti atau indeks XML selektif atau mendesain ulang struktur tabel Anda merobek XML ke tabel saja.

Saya mencoba kompresi

Saya melakukannya sekali dalam suatu produk sedikit lebih dari 10 tahun yang lalu dan telah menyesalinya sejak itu. Saya sangat merindukan tidak bisa bekerja dengan data menggunakan T-SQL, jadi saya tidak akan merekomendasikan itu kepada siapa pun jika itu dapat dihindari.


Terima kasih banyak atas jawabannya. Mengenai kompresi: Saya tidak yakin apakah anti-rekomendasi ketat seperti itu dibenarkan, karena kebutuhan untuk benar-benar meminta data dari T-SQL jelas tergantung pada sifat data yang disimpan. Dalam kasus saya, saya memutuskan untuk menggunakan kompresi untuk saat ini.
Alexander Shelemin
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.