Maison  >  Article  >  développement back-end  >  Pourquoi la requête Spanner utilisant ReadOnlyTransaction dans Golang Goroutine devient-elle progressivement plus lente ?

Pourquoi la requête Spanner utilisant ReadOnlyTransaction dans Golang Goroutine devient-elle progressivement plus lente ?

WBOY
WBOYavant
2024-02-08 21:00:121026parcourir

为什么在 golang goroutine 中使用 ReadOnlyTransaction 进行 Spanner 查询会逐渐变慢

Contenu de la question

J'essaie d'interroger environ 10 000 lignes d'une table. Après l'avoir essayé, impliqué limit offset 的各种其他选项并且没有找到所需的成功之后,我尝试在每个 goroutine 中查询单行。思路是每行只需要 ~5ms 来查询和获取,但是一批 10k 会接管 20s.

Vous trouverez ci-dessous une version simplifiée du code :

func queryEmp(IDs[]string, spannerClient *spanner.Client) (Employee,error){
query := "Select name from Employee Where id = @id"

    g, gCtx := errgroup.WithContext(ctx)
    for _, ID := range IDs {
        id := ID
        g.Go(func() error {
    
            tx := spannerClient.Single() 
            defer tx.Close()

            stmt2 := spanner.NewStatement(query)
            stmt2.Params = map[string]interface{}{
                "ID": id,
            }

            qstart := time.Now()
            it := tx.Query(gCtx, stmt2)
            defer it.Stop()
            logrus.Debugf("%s took %v \n", "query execution.", time.Since(qstart))

            for {
                row, err := it.Next()
                if err == iterator.Done {
                    break
                }
                if err != nil {
                    return err
                }

                var eID string
                if err := row.Column(0, &eID); err != nil {
                    return err
                }

            }

            return nil
        })
    }
    err = g.Wait()
}

Les résultats sont suivis comme suit :

{"message":"query execution. took 39.677µs \n","severity":"debug","time":"2023-11-03T20:51:29-04:00"}
{"message":"query execution. took 34.125µs \n","severity":"debug","time":"2023-11-03T20:51:29-04:00"}
{"message":"query execution. took 26.634µs \n","severity":"debug","time":"2023-11-03T20:51:29-04:00"}
{"message":"query execution. took 29.303µs \n","severity":"debug","time":"2023-11-03T20:51:29-04:00"}
...
...
...
{"message":"query execution. took 188.749562ms \n","severity":"debug","time":"2023-11-03T20:51:29-04:00"}
{"message":"query execution. took 276.424692ms \n","severity":"debug","time":"2023-11-03T20:51:29-04:00"}
{"message":"query execution. took 188.62849ms \n","severity":"debug","time":"2023-11-03T20:51:29-04:00"}
{"message":"query execution. took 217.067524ms \n","severity":"debug","time":"2023-11-03T20:51:29-04:00"}
{"message":"query execution. took 276.949166ms \n","severity":"debug","time":"2023-11-03T20:51:29-04:00"}
...
...
...
{"message":"query execution. took 454.64281ms \n","severity":"debug","time":"2023-11-03T20:51:29-04:00"}
{"message":"query execution. took 452.0848ms \n","severity":"debug","time":"2023-11-03T20:51:29-04:00"}
{"message":"query execution. took 525.748738ms \n","severity":"debug","time":"2023-11-03T20:51:29-04:00"}
{"message":"query execution. took 454.704656ms \n","severity":"debug","time":"2023-11-03T20:51:29-04:00"}
{"message":"query execution. took 455.4276ms \n","severity":"debug","time":"2023-11-03T20:51:29-04:00"}
...
...
...
{"message":"query execution. took 6.767574136s \n","severity":"debug","time":"2023-11-03T20:52:00-04:00"}
{"message":"query execution. took 6.780578444s \n","severity":"debug","time":"2023-11-03T20:52:00-04:00"}
{"message":"query execution. took 6.785085491s \n","severity":"debug","time":"2023-11-03T20:52:00-04:00"}
{"message":"query execution. took 6.779527006s \n","severity":"debug","time":"2023-11-03T20:52:00-04:00"}

Démarre bien et comme prévu, mais les temps de requête continuent d'augmenter.

MaxSessionsMinSessions 对于 spannerClient100 On pourrait donc imaginer qu'il y ait un léger ralentissement après 100, mais ce n'est pas le cas.

Veuillez lire ici :

<code>
Sessions can execute only one transaction at a time. Standalone reads, writes, and queries use a transaction internally, and count toward the one transaction limit.
</code>

Les requêtes non itératives (ReadRow etc.) me donnent les mêmes résultats.

L'utilisation de tx := spannerClient.Single() en dehors d'une boucle for donnera des résultats similaires.

Question :

  1. Cela signifie-t-il que malgré l'exécution de spannerClient.Single() à l'intérieur de la goroutine, la goroutine essaie toujours d'utiliser la même session/transaction ?
  2. Comment modifier le contenu ci-dessus pour résoudre ce problème ?

Bonne réponse


TLDR : La taille maximale par défaut du pool de sessions est de 400, ce qui signifie que vous ne pouvez jamais exécuter plus de 400 requêtes en parallèle. Vous devez augmenter la taille du pool de sessions pour obtenir cette simultanéité.

Tout d'abord : je ne pense pas qu'envoyer 10 000 requêtes en parallèle pour que chaque requête lise une ligne ne soit pas la solution la plus efficace à votre problème. S'il n'y a pas d'autres critères que vous pouvez utiliser pour filtrer que les identifiants des employés et que ces identifiants sont dispersés partout, alors créer la requête dans le formulaire sera toujours plus efficace

select * from employees where id in unnest(@ids)

Pour un exemple complet, voir ce commentaire : https:///github.com/googleapis/google-cloud-go/issues/858#issuecomment-550982307

Retour à votre question spécifique :

  1. Vous ne mesurez pas réellement le temps nécessaire pour exécuter la requête. C'est un peu déroutant, mais la ligne it := tx.Query(gCtx, stmt2) n'exécute it := tx.Query(gCtx, stmt2) 行确实执行查询,它只是准备执行查询。第一次调用 row, err := it.Next()pas
  2. la requête, elle la prépare simplement à être exécutée. Exécuté lorsque row, err := it.Next() est appelé pour la première fois. Vous pouvez également le voir dans les temps d’exécution enregistrés. La première instruction semble s'exécuter en 30 microsecondes, ce qui est impossible.
  3. Cela signifie que quelque chose chez votre client limite votre progression, dans ce cas, je suis presque sûr que c'est la taille maximale de votre pool de sessions. La taille maximale par défaut du pool de sessions est de 400. Cela signifie que jusqu'à 400 requêtes peuvent être exécutées en parallèle. Le temps d'attente croissant que vous voyez est dû au fait que la goroutine est placée dans une file d'attente en attendant que la session soit disponible. Les Goroutines en fin de file d’attente attendront plus longtemps.
🎜

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer