64 votes

Des insertions de prendre plus de temps que prévu à l'aide de Dapper

Après la lecture de cet article j'ai décidé de regarder de plus près à la façon dont j'ai été l'aide de Dapper.

J'ai couru ce code sur une base de données vide

var members = new List<Member>();
for (int i = 0; i < 50000; i++)
{
    members.Add(new Member()
    {
        Username = i.toString(),
        IsActive = true
    });
}

using (var scope = new TransactionScope())
{
    connection.Execute(@"
insert Member(Username, IsActive)
values(@Username, @IsActive)", members);

    scope.Complete();
}

il a fallu environ 20 secondes. C'est 2500 insertions/seconde. Pas mauvais, mais pas génial non plus en considérant le blog a été la réalisation de 45k insertions/seconde. Est-il un moyen plus efficace pour ce faire, dans Dapper?

Aussi, comme une note de côté, l'exécution de ce code par le débogueur Visual Studio a pris plus de 3 minutes! J'ai pensé que le débogueur serait-il ralentir un peu, mais j'ai été vraiment surpris de voir que beaucoup.

Mise à JOUR

Donc, ce

using (var scope = new TransactionScope())
{
    connection.Execute(@"
insert Member(Username, IsActive)
values(@Username, @IsActive)", members);

    scope.Complete();
}

et ce

    connection.Execute(@"
insert Member(Username, IsActive)
values(@Username, @IsActive)", members);

tous les deux pris 20 secondes.

Mais cela a pris 4 secondes!

SqlTransaction trans = connection.BeginTransaction();

connection.Execute(@"
insert Member(Username, IsActive)
values(@Username, @IsActive)", members, transaction: trans);

trans.Commit();

72voto

qntmfred Points 10246

Le meilleur, j'ai pu obtenir, c'est 50k enregistrements dans les 4 secondes à l'aide de cette approche

SqlTransaction trans = connection.BeginTransaction();

connection.Execute(@"
insert Member(Username, IsActive)
values(@Username, @IsActive)", members, transaction: trans);

trans.Commit();

11voto

Fredrik Ljung Points 890

Je suis tombé en travers de cela récemment et a remarqué que le TransactionScope est créé après que la connexion est ouverte (je suppose que c'depuis Dappers Exécuter ne pas ouvrir la connexion, à la différence de la Requête). En fonction de la réponse T4 ici: http://stackoverflow.com/a/2886326/455904 qui n'entraînera pas la connexion pour être traitées par le TransactionScope. Mon collègue fait quelques tests rapides, et l'ouverture de la connexion à l'extérieur de la TransactionScope considérablement diminué le rendement.

Par conséquent, la modification à la suivante devrait fonctionner:

// Assuming the connection isn't already open
using (var scope = new TransactionScope())
{
    connection.Open();
    connection.Execute(@"
insert Member(Username, IsActive)
values(@Username, @IsActive)", members);

    scope.Complete();
}

-4voto

razon Points 36

la variante la plus rapide pour moi:

con.Execute(string.Format(@"insert into Member(Username, IsActive)
{0}", string.Join(" union all ", members.Select(x => string.Format("select '{0}','{1}'", x.Username, x.IsActive)))));

qui génèrent sql comme:

INSERT TABLENAME (Column1,Column2,...)
 SELECT 'Column1Row1Value','Column2Row1Value'...
 UNION ALL
 SELECT 'Column1Row2value','Column2Row2Value'...
 UNION ALL
 SELECT 'Column1Row3value','Column2Row3Value'...

cette requête fonctionne plus rapidement parce que sql ajoute un ensemble de lignes au lieu d'ajouter 1 ligne à la fois. Le goulot d'étranglement n'est pas d'écrire les données, il est écrit ce que vous faites dans le journal.

Aussi, regardez dans les règles de journalisation minimale des transactions.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X