2010-09-13 4 views
7

Nous avons un certain nombre de bases de données qui stockent 10s à 100s de gigaoctets de données dans un des tableaux. Il contient des données d'image. Le problème est que beaucoup de ces bases de données ont été créées incorrectement. Fondamentalement, la clé primaire n'est pas réellement une clé primaire. Les ont été créés avec un index unique sur une colonne nullable. Et certains d'entre eux ont un int comme clé primaire au lieu d'un bigint.Manière efficace de modifier 100GB table

Nous avons donc lentement procédé à la correction de ces bases de données. Ils s'exécutent sur SQL Server 2000 via SQL Server 2008, bien que la plupart de ceux avec des problèmes de clé primaire soient sur SQL Server 2000. Le problème est, nous ne voulons pas verrouiller la base de données pendant une journée entière pendant qu'il convertit la table. Nous avons expérimenté plusieurs stratégies:

  1. Dites à SQL Server de modifier directement le type de colonne. Cela verrouille la table jusqu'à ce qu'elle soit complète, et après l'avoir laissé la nuit dans de nombreux cas, elle n'était toujours pas terminée.

  2. Insérez toutes les images dans une nouvelle table en une fois. Cela a été plus facilement interrompu, mais la table entière est essentiellement écrite dans le fichier journal dans le processus.

  3. Insérez 100 lignes à la fois lorsque les lignes n'existent pas dans la table cible. L'avantage est qu'ils peuvent continuer à utiliser la base de données pendant que cela se passe (avec un gros succès) et qu'elle peut être arrêtée et redémarrée arbitrairement à tout moment, et cela empêche les fichiers journaux de plus de 100 Go. C'est ce que nous faisons actuellement, mais trouver les 100 premières lignes qui n'existent pas devient vraiment très lent lorsque la table cible devient de plus en plus grande. UPDATE STATISTICS et DBCC INDEXDEFRAG aident considérablement, mais dans la tentative la plus récente, nous sommes arrivés au point où même 100 images à la fois étaient là, ne répondant pas.

    INSERT INTO %s 
        SELECT TOP 100 Source.* 
        FROM %s AS Source WITH (NOLOCK) 
        LEFT OUTER JOIN %s AS Target WITH (NOLOCK) ON Source.DocumentID = Target.DocumentID 
        WHERE Target.DocumentID IS NULL 
        ORDER BY Source.DocumentID 
    

La question est, est-il une option qui peut copier des données en vrac d'une manière efficace et peut être repris? Il n'est pas nécessaire d'être précis à 100%, nous pouvons toujours revenir en arrière et corriger les divergences à la fin, à condition de faire 99% du travail.

Répondre

3

La jointure est le problème. Ne fais pas ça. Parcourez simplement votre table actuelle en utilisant un intervalle raisonnable, en utilisant l'index en cluster actuel. Quelque chose comme:

Declare @idrange int; 
Set @idrange = 1; 

WHILE @idrange < 10000000 

INSERT INTO Destination 
    SELECT * 
    FROM Source 
    WHERE DocumentID between @idrange and @idrange + 999 
    ORDER BY Source.DocumentID 

Set @idrange = @idrange + 1000 
End 

Notez que pour une meilleure vitesse, supprimer tous les index (y compris l'index cluster) de la table de destination, puis ajoutez les index une fois toutes les lignes ont été insérées.

EDIT: modifier l'intervalle de plage pour éviter un chevauchement (depuis inclut entre les points d'extrémité)

Une dernière précision: le point global de mon exemple de script est que vous voulez simplement marcher dans vos dossiers en cours dans un ordre raisonnable, et les mettre tous dans la nouvelle table par lots. Il n'y a aucune raison de continuer à vérifier la table de destination à chaque fois, puisque vous devriez déjà savoir ce que vous y avez placé et ce qui reste à faire. La plupart du temps, il est logique d'utiliser l'index cluster (s'il y en a un), car cela signifie qu'il peut parcourir l'ordre physique de la table sans effectuer de recherche de signets. Si la table n'a pas de cluster, utilisez simplement ce qui est le plus logique (votre PK, probablement).

+0

La clé primaire de cette table est non clusterisée. Convertir en un index clusterisé serait presque aussi douloureux que de recréer la table, car il doit réorganiser physiquement toutes les données. –

+0

Le faire par plages est certainement plus efficace que la méthode de jointure. Je vais devoir voir comment ça se passe en pratique, mais j'espérais qu'il y avait un moyen d'exécuter les insertions d'une manière non atomique, où si ça s'arrêtait à mi-chemin, ça laisserait ce qui est déjà fait, et évitez l'écriture supplémentaire dans le fichier journal. –

+0

@Bryce Wagner - Donc, si ce n'est pas en cluster sur le PK, alors faire une gamme équivalente ou un regroupement sur quoi * il * est * en cluster. (S'il vous plaît dites-moi qu'il a un cluster, et que ce n'est pas juste un HEAP) – BradC

Questions connexes