Arbres B, bases de données, insertions séquentielles et randoms et vitesse. Random est gagnant

MODIFIER

@Remus a corrigé mon motif de test. Vous pouvez voir la version corrigée sur sa réponse ci-dessous.

J'ai pris la suggestion de replace l'INT par DECIMAL (29,0) et les résultats étaient:

Décimal: 2133
GUID: 1836

Les insertions randoms sont toujours gagnantes, même avec une ligne légèrement plus grande.

Malgré les explications qui indiquent que les inserts randoms sont plus lents que les séquentiels, ces tests montrent qu'ils sont apparemment plus rapides. Les explications que je reçois ne sont pas d'accord avec les repères. Par conséquent, ma question rest cinput sur les b-trees, les inserts séquentiels et la vitesse.

Je sais par expérience que les b-trees ont une performance terrible quand datatables leur sont ajoutées séquentiellement (quelle que soit la direction). Cependant, lorsque des données sont ajoutées de manière random, les meilleures performances sont obtenues.

C'est facile à démontrer avec les goûts d'un tree RB. Les écritures séquentielles provoquent l'exécution d'un nombre maximum de balances arborescentes.

Je sais que très peu de bases de données utilisent des trees binarys, mais utilisent plutôt des trees équilibrés d'ordre n. Je suppose logiquement qu'ils subissent un sort similaire aux trees binarys quand il s'agit d'inputs séquentielles.

Cela a déclenché ma curiosité.

Si tel est le cas, alors on pourrait en déduire que l'écriture d'identifiants séquentiels (comme dans IDENTITY (1,1)) provoquerait plusieurs rééquilibres de l'tree. J'ai vu beaucoup de messages se battre contre les GUID comme "ceux-ci vont causer des écritures randoms". Je n'utilise jamais de GUID, mais j'ai été frappé par le fait que ce "mauvais" point était en fait un bon point.

J'ai donc décidé de le tester. Voici mon code:

SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE TABLE [dbo].[T1]( [ID] [int] NOT NULL CONSTRAINT [T1_1] PRIMARY KEY CLUSTERED ([ID] ASC) ) GO CREATE TABLE [dbo].[T2]( [ID] [uniqueidentifier] NOT NULL CONSTRAINT [T2_1] PRIMARY KEY CLUSTERED ([ID] ASC) ) GO declare @i int, @t1 datetime, @t2 datetime, @t3 datetime, @c char(300) set @t1 = GETDATE() set @i = 1 while @i < 2000 begin insert into T2 values (NEWID(), @c) set @i = @i + 1 end set @t2 = GETDATE() WAITFOR delay '0:0:10' set @t3 = GETDATE() set @i = 1 while @i < 2000 begin insert into T1 values (@i, @c) set @i = @i + 1 end select DATEDIFF(ms, @t1, @t2) AS [Int], DATEDIFF(ms, @t3, getdate()) AS [GUID] drop table T1 drop table T2 

Notez que je ne soustrais pas de time pour la création du GUID ni pour la taille considérablement supplémentaire de la ligne. Les résultats sur ma machine étaient les suivants:

Int: 17 340 ms GUID: 6 746 ms

Cela signifie que dans ce test, les insertions randoms de 16 octets étaient presque 3 fois plus rapides que les insertions séquentielles de 4 octets .

Quelqu'un voudrait-il commenter?

Ps. Je comprends que ce n'est pas une question. C'est une invitation à la discussion, et cela est pertinent pour apprendre la programmation optimale.

returnner l'opération et l'int est plus rapide .. avez-vous pris en count le journal et la croissance des files de données? Exécuter chacun séparément

 declare @i int, @t1 datetime, @t2 datetime set @t1 = GETDATE() set @i = 1 while @i < 10000 begin insert into T2 values (NEWID()) set @i = @i + 1 END set @t2 = GETDATE() set @i = 1 while @i < 10000 begin insert into T1 values (@i) set @i = @i + 1 end select DATEDIFF(ms, @t1, @t2) AS [UID], DATEDIFF(ms, @t2, getdate()) AS [Int] 

le problème avec les UUIDs est lors de la mise en cluster sur eux et ne pas utiliser NEWSEQUENTIALID () est qu'ils provoquent des sauts de page et la fragmentation de la table

Maintenant, essayez comme ça et vous voyez qu'il est presque le même

 declare @i int, @t1 datetime, @t2 datetime set @t1 = GETDATE() set @i = 1 while @i < 10000 begin insert into T2 values (NEWID()) set @i = @i + 1 END select DATEDIFF(ms, @t1, getdate()) set @t1 = GETDATE() set @i = 1 while @i < 10000 begin insert into T1 values (@i) set @i = @i + 1 end select DATEDIFF(ms, @t1, getdate()) 

Et inversé

 declare @i int, @t1 datetime, @t2 datetime set @t1 = GETDATE() set @i = 1 while @i < 10000 begin insert into T1 values (@i) set @i = @i + 1 end set @t1 = GETDATE() set @i = 1 while @i < 10000 begin insert into T2 values (NEWID()) set @i = @i + 1 END select DATEDIFF(ms, @t1, getdate()) 

Vous ne mesurez pas la vitesse INSERT. Vous mesurez les performances de votre flush de journal. Puisque vous vous engagez après chaque INSERT, tous ces tests sont en attente d'engagement pour durcir le journal. Cela n'est guère pertinent pour la performance INSERT. Et s'il vous plaît ne pas postr des mesures de «performance» lorsque SET NOCOUNT est OFF

Essayons donc cela sans bavardage inutile entre le client et le server, avec des données correctement dimensionnées, des validations par lots et des bases de données pré-développées:

 :setvar dbname testdb :setvar testsize 1000000 :setvar batchsize 1000 use master; go if db_id('$(dbname)') is not null begin drop database [$(dbname)]; end go create database [$(dbname)] on (name='test_data', filename='c:\temp\test_data.mdf', size=10gb) log on (name='test_log', filename='c:\temp\test_log.ldf', size=100mb); go use [$(dbname)]; go CREATE TABLE [dbo].[T1]( [ID] [int] NOT NULL CONSTRAINT [T1_1] PRIMARY KEY CLUSTERED ([ID] ASC) ) GO CREATE TABLE [dbo].[T2]( [ID] [uniqueidentifier] NOT NULL CONSTRAINT [T2_1] PRIMARY KEY CLUSTERED ([ID] ASC) ) GO set nocount on; go declare @i int, @t1 datetime, @t2 datetime set @t1 = GETDATE() set @i = 1 begin transaction; while @i < $(testsize) begin insert into T1 values (@i) set @i = @i + 1 if @i % $(batchsize) = 0 begin commit; begin transaction; end end commit set @t2 = GETDATE() set @i = 1 begin transaction while @i < $(testsize) begin insert into T2 values (NEWID()) set @i = @i + 1 if @i % $(batchsize) = 0 begin commit; begin transaction; end end commit select DATEDIFF(ms, @t1, @t2) AS [Int], DATEDIFF(ms, @t2, getdate()) AS [UID] drop table T1 drop table T2 

INTS: 18s
GUIDES: 23s

QED

Je m'attends à ce que le rééquilibrage réel d'une database soit un problème mineur, car beaucoup d'inputs d'index vont tenir dans un seul bloc et aussi longtime.

Ce qui pourrait devenir plus d'un problème pourrait être contention à ce bloc unique contenant toutes les nouvelles inputs. Oracle dispose d'une fonction permettant de stocker les octets de la key dans l'ordre inverse afin de répartir les nouvelles inputs sur tous les blocs: http://oracletoday.blogspot.com/2006/09/there-is-option-to-create-index.html Je ne sais pas sur les autres bases de données.