LOB_DATA, analyses de table lentes et quelques questions d'E / S


19

J'ai un tableau assez grand avec l'une des colonnes étant des données XML avec une taille moyenne d'entrée XML de ~ 15 kilo-octets. Toutes les autres colonnes sont des entiers réguliers, des bigints, des GUID, etc. Pour avoir des chiffres concrets, disons que la table a un million de lignes et fait environ 15 Go.

Ce que j'ai remarqué, c'est que ce tableau est vraiment lent à sélectionner des données si je veux sélectionner toutes les colonnes. Quand je fais

SELECT TOP 1000 * FROM TABLE

il faut environ 20-25 secondes pour lire les données à partir du disque - même si je n'impose aucun ordre sur le résultat. J'exécute la requête avec le cache froid (c'est-à-dire après DBCC DROPCLEANBUFFERS). Voici les résultats des statistiques d'E / S:

Nombre de balayages 1, lectures logiques 364, lectures physiques 24, lectures anticipées 7191, lectures logiques lob 7924, lectures physiques lob 1690, lectures anticipées lob 3968.

Il capture ~ 15 Mo de données. Le plan d'exécution montre l'analyse d'index en cluster comme je m'y attendais.

Il n'y a pas d'E / S en cours sur le disque à part mes requêtes; J'ai également vérifié que la fragmentation des index cluster est proche de 0%. Il s'agit d'un disque SATA grand public, mais je pense toujours que SQL Server serait en mesure d'analyser la table plus rapidement que ~ 100-150 Mo / min.

La présence du champ XML fait que la plupart des données de table se trouvent sur les pages LOB_DATA (en fait, ~ 90% des pages de table sont LOB_DATA).

Je suppose que ma question est - ai-je raison de penser que les pages LOB_DATA peuvent provoquer des analyses lentes non seulement en raison de leur taille, mais aussi parce que SQL Server ne peut pas analyser efficacement l'index cluster lorsqu'il y a beaucoup de pages LOB_DATA dans la table?

Plus largement encore - est-il jugé raisonnable d'avoir une telle structure de tableau / modèle de données? Les recommandations d'utilisation de Filestream indiquent généralement des tailles de champ beaucoup plus grandes, donc je ne veux pas vraiment suivre cette voie. Je n'ai pas vraiment trouvé de bonnes informations sur ce scénario particulier.

J'ai pensé à la compression XML, mais elle doit être effectuée sur le client ou avec SQLCLR et nécessiterait un certain travail pour être implémentée dans le système.

J'ai essayé la compression, et comme les XML sont très redondants, je peux (dans l'application ac #) compresser le XML de 20 Ko à ~ 2,5 Ko et le stocker dans la colonne VARBINARY, empêchant l'utilisation des pages de données LOB. Cela accélère les sélections 20 fois dans mes tests.


Alex: je ne sais pas si vous avez vu la discussion liée à ma réponse (le lien est dans un commentaire sous ma réponse), mais j'ai pu me rapprocher de la reproduction de votre scénario. J'ai rempli un tableau correspondant (autant que j'avais des informations pour) à votre description et obtenu des statistiques d'E / S qui sont très similaires. Sauf que les "LOB Physical Reads" n'ont jamais été aussi proches. Je me demandais donc si vous aviez mis à jour le XML (mais pas les autres colonnes) et / ou eu beaucoup de fragmentation physique de vos fichiers de données. Cela ne me dérangerait toujours pas d'obtenir la DDL de votre table et votre paramètre de croissance automatique pour chaque fichier de données, et réduisez-vous vos fichiers de données?
Solomon Rutzky

Tout d'abord - merci beaucoup pour la réponse détaillée, je n'ai pas pu participer à la discussion à l'époque à cause du manque de temps. Maintenant que vous l'avez mentionné (je n'y ai pas pensé quand on m'a posé la question) - Le champ XML est mis à jour plusieurs fois après sa création, et il est créé petit. Je soupçonne donc qu'au départ, il est stocké en ligne, et après quelques mises à jour, il est déplacé dans une structure de page LOB, puis obtient quelques mises à jour supplémentaires.
Alexander Shelemin

(Suite) J'ai vérifié la fragmentation physique des fichiers avant de poser la question, et l'outil Windows intégré a pensé que c'était OK, donc je n'ai pas approfondi la question. La croissance automatique est par défaut, de 1 Mo je crois, et les fichiers de données n'ont pas été réduits.
Alexander Shelemin

Sélectionner le top 1000 * est important dans mon cas particulier. Je comprends certainement que cela est considéré comme une mauvaise pratique, mais certaines décisions de conception d'applications sont vraiment difficiles à modifier après avoir été en place depuis longtemps. Select * est essentiellement utilisé comme stratégie de réplication entre bases de données entre différents composants de notre application. Il y a des avantages, par exemple, nous pouvons faire beaucoup de manipulations arbitraires avec des données / schémas à la volée, ce qui serait difficile avec des techniques de réplication intégrées, mais cela comporte ses problèmes.
Alexander Shelemin

Alex, ce SELECT *n'est pas le problème si vous avez besoin des données XML. Ce n'est un problème que si vous ne voulez pas les données XML, dans ce cas, pourquoi ralentir la requête pour récupérer les données que vous n'utilisez pas? J'ai posé des questions sur les mises à jour du XML en me demandant si la fragmentation sur les pages LOB n'était pas signalée avec précision. C'est pourquoi j'avais demandé dans ma réponse comment exactement avez-vous déterminé que l'index cluster n'était pas fragmenté? Pouvez-vous fournir la commande que vous avez exécutée? Et avez-vous fait une RECONSTRUCTION complète sur l'Index Clustered? (suite)
Solomon Rutzky

Réponses:


11

La présence du champ XML fait que la plupart des données de table se trouvent sur les pages LOB_DATA (en fait, ~ 90% des pages de table sont LOB_DATA).

Le simple fait d'avoir la colonne XML dans le tableau n'a pas cet effet. C'est la présence de données XML qui, dans certaines conditions , entraîne le stockage d'une partie des données d'une ligne hors ligne, sur les pages LOB_DATA. Et même si un (ou peut-être plusieurs ;-) pourrait faire valoir que duh, la XMLcolonne implique qu'il y aura effectivement des données XML, il n'est pas garanti que les données XML devront être stockées hors ligne: à moins que la ligne soit à peu près déjà remplie en dehors de leur caractère XML, les petits documents (jusqu'à 8 000 octets) peuvent tenir en ligne et ne jamais aller sur une page LOB_DATA.

ai-je raison de penser que les pages LOB_DATA peuvent provoquer des analyses lentes non seulement en raison de leur taille, mais aussi parce que SQL Server ne peut pas analyser efficacement l'index cluster lorsqu'il y a beaucoup de pages LOB_DATA dans le tableau?

La numérisation consiste à regarder toutes les lignes. Bien sûr, lorsqu'une page de données est lue, toutes les données en ligne sont lues, même si vous avez sélectionné un sous-ensemble des colonnes. La différence avec les données LOB est que si vous ne sélectionnez pas cette colonne, les données hors ligne ne seront pas lues. Par conséquent, il n'est pas vraiment juste de tirer une conclusion sur l'efficacité avec laquelle SQL Server peut analyser cet index clusterisé, car vous n'avez pas exactement testé cela (ou vous en avez testé la moitié). Vous avez sélectionné toutes les colonnes, ce qui inclut la colonne XML, et comme vous l'avez mentionné, c'est là que se trouvent la plupart des données.

Nous savons donc déjà que le SELECT TOP 1000 *test ne consistait pas simplement à lire une série de pages de données de 8k, toutes d'affilée, mais plutôt à sauter vers d'autres emplacements pour chaque ligne . La structure exacte de ces données LOB peut varier en fonction de leur taille. Sur la base des recherches présentées ici ( Quelle est la taille du pointeur LOB pour les types (MAX) comme Varchar, Varbinary, Etc? ), Il existe deux types d'allocations LOB hors ligne:

  1. Racine en ligne - pour les données comprises entre 8001 et 40 000 (vraiment 42 000) octets, si l'espace le permet, il y aura 1 à 5 pointeurs (24 à 72 octets) EN RANG qui pointent directement vers les pages LOB.
  2. TEXT_TREE - pour les données de plus de 42 000 octets, ou si les 1 à 5 pointeurs ne peuvent pas tenir en ligne, il n'y aura alors qu'un pointeur de 24 octets vers la page de départ d'une liste de pointeurs vers les pages LOB (c'est-à-dire le " page text_tree ").

L'une de ces deux situations se produit chaque fois que vous récupérez des données LOB qui dépassent 8 000 octets ou qui ne tiennent tout simplement pas en ligne. J'ai posté un script de test sur PasteBin.com (script T-SQL pour tester les allocations et les lectures LOB ) qui montre les 3 types d'allocations LOB (en fonction de la taille des données) ainsi que l'effet de chacune d'elles sur la logique et lectures physiques. Dans votre cas, si les données XML sont réellement inférieures à 42 000 octets par ligne, aucune d'entre elles (ou très peu) ne devrait se trouver dans la structure TEXT_TREE la moins efficace.

Si vous souhaitez tester la vitesse à laquelle SQL Server peut analyser cet index clusterisé, procédez comme suit, SELECT TOP 1000mais spécifiez une ou plusieurs colonnes n'incluant pas cette colonne XML. Comment cela affecte-t-il vos résultats? Cela devrait être un peu plus rapide.

est-il jugé raisonnable d'avoir une telle structure de tableau / modèle de données?

Étant donné que nous avons une description incomplète de la structure réelle du tableau et du modèle de données, toute réponse peut ne pas être optimale en fonction de ces détails manquants. Dans cet esprit, je dirais qu'il n'y a rien de manifestement déraisonnable dans la structure de votre table ou le modèle de données.

Je peux (dans une application ac #) compresser XML de 20 Ko à ~ 2,5 Ko et le stocker dans la colonne VARBINARY, empêchant l'utilisation des pages de données LOB. Cela accélère les sélections 20 fois dans mes tests.

Cela a rendu la sélection de toutes les colonnes, ou même juste les données XML (maintenant VARBINARY) plus rapide, mais cela bloque en fait les requêtes qui ne sélectionnent pas les données "XML". En supposant que vous avez environ 50 octets dans les autres colonnes et que vous en avez FILLFACTOR100, alors:

  • Aucune compression: 15k de XMLdonnées devraient nécessiter 2 pages LOB_DATA, ce qui nécessite alors 2 pointeurs pour la racine en ligne. Le premier pointeur fait 24 octets et le second 12, pour un total de 36 octets stockés en ligne pour les données XML. La taille totale des lignes est de 86 octets, et vous pouvez adapter environ 93 de ces lignes sur une page de données de 8060 octets. Par conséquent, 1 million de lignes nécessite 10 753 pages de données.

  • Compression personnalisée: 2,5 k de VARBINARYdonnées s'adapteront en ligne. La taille totale des lignes est de 2610 (2,5 * 1024 = 2560) octets, et vous ne pouvez insérer que 3 de ces lignes sur une page de données de 8060 octets. Par conséquent, 1 million de lignes nécessite 333 334 pages de données.

Ergo, l'implémentation d'une compression personnalisée entraîne une augmentation de 30 fois le nombre de pages de données pour l'index clusterisé. Autrement dit, toutes les requêtes utilisant une analyse d'index cluster ont maintenant environ 322 500 pages de données supplémentaires à lire. Veuillez consulter la section détaillée ci-dessous pour connaître les ramifications supplémentaires de ce type de compression.

Je mettrais en garde contre toute refactorisation basée sur les performances de SELECT TOP 1000 *. Il est peu probable qu'il s'agisse d'une requête que l'application émettra, et ne doit pas être utilisée comme seule base pour des optimisations potentiellement inutiles.

Pour des informations plus détaillées et d'autres tests à essayer, veuillez consulter la section ci-dessous.


Cette question ne peut pas recevoir de réponse définitive, mais nous pouvons au moins faire des progrès et suggérer des recherches supplémentaires pour nous aider à nous rapprocher de la détermination du problème exact (idéalement basé sur des preuves).

Ce que nous savons:

  1. Le tableau comprend environ 1 million de lignes
  2. La taille de la table est d'environ 15 Go
  3. Le tableau contient une XMLcolonne et plusieurs autres colonnes de types: INT, BIGINT, UNIQUEIDENTIFIER, « etc »
  4. XMLla "taille" de la colonne est en moyenne d' environ 15k
  5. Après l'exécution DBCC DROPCLEANBUFFERS, il faut 20 à 25 secondes pour que la requête suivante se termine:SELECT TOP 1000 * FROM TABLE
  6. L'index cluster est en cours d'analyse
  7. La fragmentation sur l'indice clusterisé est proche de 0%

Ce que nous pensons savoir:

  1. Aucune autre activité de disque en dehors de ces requêtes. Êtes-vous sûr? Même s'il n'y a pas d'autres requêtes utilisateur, des opérations d'arrière-plan ont-elles lieu? Existe-t-il des processus externes à SQL Server exécutés sur la même machine qui pourraient prendre une partie des E / S? Il n'y en a peut-être pas, mais ce n'est pas clair sur la seule base des informations fournies.
  2. 15 Mo de données XML sont retournés. Sur quoi est basé ce nombre? Une estimation dérivée des 1 000 lignes multipliées par la moyenne de 15 000 données XML par ligne? Ou une agrégation programmatique de ce qui a été reçu pour cette requête? S'il ne s'agit que d'une estimation, je ne m'y fonderais pas car la distribution des données XML pourrait ne pas être identique à celle qu'implique une simple moyenne.
  3. La compression XML pourrait vous aider. Comment feriez-vous exactement la compression dans .NET? Via les classes GZipStream ou DeflateStream ? Ce n'est pas une option à coût nul. Il compressera certainement certaines des données d'un grand pourcentage, mais il faudra également plus de CPU car vous aurez besoin d'un processus supplémentaire pour compresser / décompresser les données à chaque fois. Ce plan supprimerait également complètement votre capacité à:

    • interroger les données XML via les .nodes, .value, .queryet les .modifyfonctions XML.
    • indexer les données XML.

      Veuillez garder à l'esprit (puisque vous avez mentionné que XML est "hautement redondant") que le XMLtype de données est déjà optimisé en ce qu'il stocke les noms d'élément et d'attribut dans un dictionnaire, en attribuant un ID d'index entier à chaque élément, puis en utilisant cet ID entier dans tout le document (par conséquent, il ne répète pas le nom complet pour chaque utilisation, ni ne le répète à nouveau comme une balise de fermeture pour les éléments). Les données réelles ont également supprimé les espaces blancs superflus. C'est pourquoi les documents XML extraits ne conservent pas leur structure d'origine et pourquoi les éléments vides sont extraits comme <element />s'ils étaient entrés en tant que<element></element>. Ainsi, tout gain de compression via GZip (ou toute autre chose) ne sera trouvé qu'en compressant les valeurs des éléments et / ou des attributs, ce qui est une surface beaucoup plus petite qui pourrait être améliorée que la plupart ne le pensent et ne vaut probablement pas la perte de capacités comme indiqué ci-dessus.

      Veuillez également garder à l'esprit que la compression des données XML et le stockage du VARBINARY(MAX)résultat n'éliminera pas l'accès LOB, il le réduira simplement. Selon la taille du reste des données sur la ligne, la valeur compressée peut tenir dans la ligne, ou elle peut encore nécessiter des pages LOB.

Ces informations, bien qu'utiles, ne suffisent pas. Il existe de nombreux facteurs qui influencent les performances des requêtes, nous avons donc besoin d'une image beaucoup plus détaillée de ce qui se passe.

Ce que nous ne savons pas, mais devons:

  1. Pourquoi la performance de la SELECT *matière? Est-ce un modèle que vous utilisez dans le code. Si oui, pourquoi?
  2. Quelle est la performance de sélectionner uniquement la colonne XML? Quelles sont les statistiques et le calendrier si vous faites simplement SELECT TOP 1000 XmlColumn FROM TABLE;:?
  3. La durée des 20 à 25 secondes nécessaires pour renvoyer ces 1 000 lignes est liée aux facteurs réseau (obtention des données via le câble) et la quantité est liée aux facteurs clients (soit environ 15 Mo plus le reste des non Données XML dans la grille dans SSMS, ou éventuellement sauvegarde sur disque)?

    La prise en compte de ces deux aspects de l'opération peut parfois se faire simplement en ne renvoyant pas les données. Maintenant, on pourrait penser à sélectionner une table temporaire ou une variable de table, mais cela ne ferait qu'introduire quelques nouvelles variables (par exemple, les E / S de disque pour tempdb, les écritures du journal des transactions, la possible croissance automatique des données tempdb et / ou du fichier journal, le besoin dans le pool de tampons, etc.). Tous ces nouveaux facteurs peuvent en fait augmenter le temps de requête. Au lieu de cela, je stocke généralement les colonnes dans des variables (du type de données approprié; non SQL_VARIANT) qui sont écrasées à chaque nouvelle ligne (c'est-à-dire SELECT @Column1 = tab.Column1,...).

    CEPENDANT , comme l'a souligné @PaulWhite dans ce DBA.StackExchange Q & A, Logical lit différemment lors de l'accès aux mêmes données LOB , avec des recherches supplémentaires de moi-même publiées sur PasteBin ( script T-SQL pour tester divers scénarios pour les lectures LOB ) , LOB ne sont pas accessibles constamment entre SELECT, SELECT INTO, SELECT @XmlVariable = XmlColumn, SELECT @XmlVariable = XmlColumn.query(N'/')et SELECT @NVarCharVariable = CONVERT(NVARCHAR(MAX), XmlColumn). Nos options sont donc un peu plus limitées ici, mais voici ce qui peut être fait:

    1. Éliminez les problèmes de réseau en exécutant la requête sur le serveur exécutant SQL Server, dans SSMS ou SQLCMD.EXE.
    2. Éliminez les problèmes des clients dans SSMS en allant dans Options de requête -> Résultats -> Grille et en cochant l'option "Supprimer les résultats après exécution". Veuillez noter que cette option empêchera TOUTES les sorties, y compris les messages, mais peut toujours être utile pour exclure le temps nécessaire à SSMS pour allouer la mémoire par ligne, puis la dessiner dans la grille.
      Sinon, vous pouvez exécuter la requête via Sqlcmd.exe et diriger la sortie pour aller nulle part via: -o NUL:.
  4. Y a-t-il un type d'attente associé à cette requête? Si oui, quel est ce type d'attente?
  5. Quelle est la taille réelle des données pour les XMLcolonnes renvoyées ? La taille moyenne de cette colonne sur l'ensemble du tableau n'a pas vraiment d'importance si les lignes "TOP 1000" contiennent une partie disproportionnée du total des XMLdonnées. Si vous voulez en savoir plus sur les 1000 premières lignes, regardez ces lignes. Veuillez exécuter ce qui suit:

    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. Le schéma de table exact . Veuillez fournir la déclaration complète CREATE TABLE , y compris tous les index.
  7. Plan de requête? Est-ce quelque chose que vous pouvez publier? Cette information ne changera probablement rien, mais il vaut mieux savoir qu'elle ne changera pas que de deviner qu'elle ne le sera pas et qu'elle aura tort ;-)
  8. Y a-t-il une fragmentation physique / externe sur le fichier de données? Bien que cela ne soit peut-être pas un facteur important ici, puisque vous utilisez du "SATA de qualité grand public" et non un SSD ou même un SATA super-coûteux, l'effet des secteurs sous-optimisés sera plus visible, d'autant plus que le nombre de ces secteurs qui doit être lu augmente.
  9. Quels sont les résultats exacts de la requête suivante:

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

MISE À JOUR

Il m'est venu à l'esprit que je devrais essayer de reproduire ce scénario pour voir si je rencontre un comportement similaire. J'ai donc créé un tableau avec plusieurs colonnes (similaire à la description vague de la question), puis je l'ai rempli avec 1 million de lignes, et la colonne XML a environ 15k de données par ligne (voir le code ci-dessous).

Ce que j'ai trouvé, c'est que faire un SELECT TOP 1000 * FROM TABLEterminé en 8 secondes la première fois, et 2 à 4 secondes à chaque fois par la suite (oui, exécuter DBCC DROPCLEANBUFFERSavant chaque exécution de la SELECT *requête). Et mon ordinateur portable de plusieurs années n'est pas rapide: SQL Server 2012 SP2 Developer Edition, 64 bits, 6 Go de RAM, double 2,5 Ghz Core i5 et un lecteur SATA à 5400 tr / min. J'utilise également SSMS 2014, SQL Server Express 2014, Chrome et plusieurs autres choses.

Sur la base du temps de réponse de mon système, je répéterai que nous avons besoin de plus d'informations (c'est-à-dire des détails sur le tableau et les données, les résultats des tests suggérés, etc.) afin d'aider à réduire la cause du temps de réponse de 20 à 25 secondes que vous voyez.

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.
*/

Et, parce que nous voulons prendre en compte le temps nécessaire pour lire les pages non LOB, j'ai exécuté la requête suivante pour sélectionner tout sauf la colonne XML (l'un des tests que j'ai suggéré ci-dessus). Cela revient en 1,5 seconde assez régulièrement.

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.
*/

Conclusion (pour le moment)
Sur la base de ma tentative de recréer votre scénario, je ne pense pas que nous puissions indiquer le lecteur SATA ou les E / S non séquentielles comme la principale cause des 20 à 25 secondes, surtout parce que nous Je ne sais pas à quelle vitesse la requête retourne sans inclure la colonne XML. Et je n'ai pas pu reproduire le grand nombre de lectures logiques (non LOB) que vous montrez, mais j'ai le sentiment que j'ai besoin d'ajouter plus de données à chaque ligne à la lumière de cela et de la déclaration de:

~ 90% des pages du tableau sont LOB_DATA

Ma table comporte 1 million de lignes, chacune sys.dm_db_index_physical_statscontenant un peu plus de 15 000 données XML, et montre qu'il y a 2 millions de pages LOB_DATA. Les 10% restants seraient alors 222 000 pages de données IN_ROW, mais je n'en ai que 11 630. Donc, encore une fois, nous avons besoin de plus d'informations sur le schéma de table réel et les données réelles.



10

ai-je raison de penser que les pages LOB_DATA peuvent provoquer des analyses lentes non seulement en raison de leur taille, mais aussi parce que SQL Server ne peut pas analyser efficacement l'index en cluster

Oui, la lecture de données LOB non stockées en ligne conduit à des E / S aléatoires au lieu d'E / S séquentielles. La mesure des performances du disque à utiliser ici pour comprendre pourquoi elle est rapide ou lente est IOPS à lecture aléatoire.

Les données LOB sont stockées dans une arborescence où la page de données dans l'index clusterisé pointe vers une page de données LOB avec une structure racine LOB qui à son tour pointe vers les données LOB réelles. Lors de la traversée des nœuds racine dans l'index cluster, SQL Server ne peut obtenir les données en ligne que par des lectures séquentielles. Pour obtenir les données LOB, SQL Server doit aller ailleurs sur le disque.

Je suppose que si vous passiez à un disque SSD, vous n'en souffririez pas beaucoup, car les IOPS aléatoires pour un SSD sont bien plus élevés que pour un disque en rotation.

est-il jugé raisonnable d'avoir une telle structure de tableau / modèle de données?

Oui, ça pourrait l'être. Cela dépend de ce que ce tableau fait pour vous.

Habituellement, les problèmes de performances avec XML dans SQL Server se produisent lorsque vous souhaitez utiliser T-SQL pour interroger le XML et plus encore lorsque vous souhaitez utiliser des valeurs du XML dans un prédicat dans une clause where ou une jointure. Si tel est le cas, vous pouvez consulter la promotion des propriétés ou les index XML sélectifs ou une refonte de vos structures de table déchiquetant le XML en tables à la place.

J'ai essayé la compression

Je l'ai fait une fois dans un produit il y a un peu plus de 10 ans et je le regrette depuis. J'ai vraiment manqué de ne pas pouvoir travailler avec les données en utilisant T-SQL, donc je ne recommanderais cela à personne si cela peut être évité.


Merci beaucoup pour la réponse. En ce qui concerne la compression: je ne sais pas si une anti-recommandation aussi stricte est justifiée, car la nécessité d'interroger réellement les données de T-SQL dépend évidemment de la nature des données stockées. Dans mon cas, j'ai décidé d'aller avec la compression pour l'instant.
Alexander Shelemin
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.