【问题标题】:Surprisingly high latencies for selects/inserts in spanner扳手中选择/插入的延迟非常高
【发布时间】:2021-01-06 08:21:48
【问题描述】:

对于 spanner 中的简单查询(按主键更新或选择),我得到大约 50-100 毫秒的延迟。从同一项目/区域连接到扳手。这是预期的行为吗?我预计这些延迟会低得多。

【问题讨论】:

  • 能否请您发布一个示例查询?另外,谷歌控制台的查询时间是多少。

标签: google-cloud-spanner


【解决方案1】:

不,使用主键进行简单选择的延迟应该比这低很多。

我根据您在上面提供的信息使用以下简单程序进行了快速基准测试:

package main

import (
    "context"
    "fmt"
    "math/rand"
    "time"

    "cloud.google.com/go/spanner"
    "github.com/montanaflynn/stats"
    "google.golang.org/api/iterator"
)

func main() {
    fmt.Printf("Simple Spanner benchmarking...\n")
    source := rand.NewSource(time.Now().UnixNano())
    rnd := rand.New(source)
    client, err := spanner.NewClient(context.Background(), "projects/my-project/instances/my-instance/databases/my-database")
    if err != nil {
        fmt.Printf("Client creation failed: %v", err)
        return
    }
    var times stats.Float64Data
    for i := 0; i < 25; i++ {
        id := rnd.Int63n(1000) + 100000
        statement := spanner.NewStatement("SELECT * FROM Singers WHERE SingerId=@id")
        statement.Params["id"] = id
        start := time.Now()
        iter := client.Single().Query(context.Background(), statement)
        for {
            row, err := iter.Next()
            if err == iterator.Done {
                break
            }
            if err != nil {
                fmt.Printf("Query failure: %v", err)
                break
            }
            var fullName string
            row.ColumnByName("FullName", &fullName)
            fmt.Printf("Singer name: %s\n", fullName)
            elapsed := time.Since(start)
            fmt.Printf("Time: %v\n", elapsed)
            times = append(times, float64(elapsed.Milliseconds()))
        }
        iter.Stop()
    }
    median, _ := stats.Median(times)
    avg, _ := stats.Mean(times)
    p90, _ := stats.Percentile(times, 90)
    fmt.Printf("Median: %v\n", median)
    fmt.Printf("P90: %v\n", p90)
    fmt.Printf("Avg: %v\n", avg)
}

应用程序在与 Spanner 实例位于同一区域的尽可能小的 Google Cloud Compute Engine 虚拟机上执行。结果是:

Simple Spanner benchmarking...
Singer name: FirstName LastName 100960
Time: 374.627846ms
Singer name: FirstName LastName 100865
Time: 4.102019ms
Singer name: FirstName LastName 100488
Time: 3.479059ms

...

Singer name: FirstName LastName 100542
Time: 3.986866ms
Singer name: FirstName LastName 100822
Time: 3.978838ms
Singer name: FirstName LastName 100235
Time: 4.511711ms
Singer name: FirstName LastName 100020
Time: 3.476673ms
Singer name: FirstName LastName 100234
Time: 3.191529ms
Singer name: FirstName LastName 100219
Time: 4.451639ms

Median: 3
P90: 4
Avg: 18.44

因此,您大约 50-100 毫秒的执行时间听起来很长。在这个(简单)测试用例中,单行选择的正常执行时间约为 3-4 毫秒(第一个请求除外,因为这也初始化了后备会话池)。

  • 可能是您的表有一个使用单调递增值的主键吗?这可以在主键的后备索引中使用create hotspots
  • 您是否正在关闭并在每个查询之间创建一个新客户端?这是否需要为每个新查询重新初始化会话池?
  • 您的查询是否使用一次性只读事务?还是您正在使用其他类型的事务来读取数据?

能否请您提供一些关于您是如何执行查询的详细信息(最好提供代码示例)?

  • 您使用的是客户端库吗?如果有,是哪一个? (Java、Node、Go,...?)
  • 您是否只测量启动应用程序后执行的第一个查询?第一个查询会比后面的查询慢,因为客户端库需要先创建一个会话,然后再执行查询。
  • 您写道,您正在从同一个项目/区域进行连接。这是否意味着您的客户端代码正在 Google Cloud VM 或类似设备上运行?

【讨论】:

  • 使用 golang 库。测量所有查询(这是一个长期运行的应用程序/服务)。它在老式计算实例上运行,是的。
  • 我得到了更好的数据(更随机)。看起来当我获取硬编码的主键(从不改变)时,它应该被缓存,在这种情况下延迟徘徊在 20 毫秒左右。这是人们通常看到的吗?我将其更改为选择随机键以查看效果而无需缓存。
  • 感谢详细解答!我正在尝试从工作应用程序中提取一个很好的示例,稍后将在此处发布自包含示例。回答其他问题:我试图避免热点情况,但让我得到具体的查询和表结构。很确定客户端被重用了。
猜你喜欢
  • 1970-01-01
  • 2015-10-25
  • 1970-01-01
  • 2020-09-18
  • 2020-08-27
  • 1970-01-01
  • 2019-03-08
  • 1970-01-01
  • 2023-01-18
相关资源
最近更新 更多