【问题标题】:SQL result to JSON as fast as possible尽快将 SQL 结果转为 JSON
【发布时间】:2017-04-02 14:52:17
【问题描述】:

我正在尝试将 Go 内置 sql 结果转换为 JSON。我为此使用 goroutines,但我遇到了问题。

基本问题:

有一个非常大的数据库,大约有 20 万用户,我必须通过基于微服务的系统中的 tcp 套接字为他们提供服务。从数据库中获取用户花费了 20 毫秒,但是将这组数据转换为 JSON 花费了 10 秒,而当前的解决方案是。这就是我想使用 goroutines 的原因。

使用 Goroutines 的解决方案:

func getJSON(rows *sql.Rows, cnf configure.Config) ([]byte, error) {
    log := logan.Log{
        Cnf: cnf,
    }

    cols, _ := rows.Columns()

    defer rows.Close()

    done := make(chan struct{})
    go func() {
        defer close(done)
        for result := range resultChannel {
            results = append(
                results,
                result,
            )
        }
    }()

    wg.Add(1)
    go func() {
        for rows.Next() {
            wg.Add(1)
            go handleSQLRow(cols, rows)
        }
        wg.Done()
    }()

    go func() {
        wg.Wait()
        defer close(resultChannel)
    }()

    <-done

    s, err := json.Marshal(results)
    results = []resultContainer{}
    if err != nil {
        log.Context(1).Error(err)
    }
    rows.Close()
    return s, nil
}

func handleSQLRow(cols []string, rows *sql.Rows) {
    defer wg.Done()
    result := make(map[string]string, len(cols))
    fmt.Println("asd -> " + strconv.Itoa(counter))
    counter++
    rawResult := make([][]byte, len(cols))
    dest := make([]interface{}, len(cols))

    for i := range rawResult {
        dest[i] = &rawResult[i]
    }
    rows.Scan(dest...) // GET PANIC
    for i, raw := range rawResult {
        if raw == nil {
            result[cols[i]] = ""
        } else {
            fmt.Println(string(raw))
            result[cols[i]] = string(raw)
        }
    }
    resultChannel <- result
}

这个解决方案让我感到恐慌,并显示以下消息:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x45974c]

goroutine 408 [running]:
panic(0x7ca140, 0xc420010150)
    /usr/lib/golang/src/runtime/panic.go:500 +0x1a1
database/sql.convertAssign(0x793960, 0xc420529210, 0x7a5240, 0x0, 0x0, 0x0)
    /usr/lib/golang/src/database/sql/convert.go:88 +0x1ef1
database/sql.(*Rows).Scan(0xc4203e4060, 0xc42021fb00, 0x44, 0x44, 0x44, 0x44)
    /usr/lib/golang/src/database/sql/sql.go:1850 +0xc2
github.com/PumpkinSeed/zerodb/operations.handleSQLRow(0xc420402000, 0x44, 0x44, 0xc4203e4060)
    /home/loow/gopath/src/github.com/PumpkinSeed/zerodb/operations/operations.go:290 +0x19c
created by github.com/PumpkinSeed/zerodb/operations.getJSON.func2
    /home/loow/gopath/src/github.com/PumpkinSeed/zerodb/operations/operations.go:258 +0x91
exit status 2

当前可行但花费太多时间的解决方案:

func getJSON(rows *sql.Rows, cnf configure.Config) ([]byte, error) {
    log := logan.Log{
        Cnf: cnf,
    }
    var results []resultContainer
    cols, _ := rows.Columns()
    rawResult := make([][]byte, len(cols))
    dest := make([]interface{}, len(cols))

    for i := range rawResult {
        dest[i] = &rawResult[i]
    }

    defer rows.Close()

    for rows.Next() {

        result := make(map[string]string, len(cols))
        rows.Scan(dest...)
        for i, raw := range rawResult {
            if raw == nil {

                result[cols[i]] = ""

            } else {
                result[cols[i]] = string(raw)
            }
        }

        results = append(results, result)
    }
    s, err := json.Marshal(results)
    if err != nil {
        log.Context(1).Error(err)
    }
    rows.Close()
    return s, nil
}

问题:

为什么goroutine解决方案给我一个错误,这不是一个明显的恐慌,因为第一个~200个goroutine运行正常?!

更新

原始工作解决方案的性能测试:

INFO[0020] setup taken -> 3.149124658s                   file=operations.go func=operations.getJSON line=260 service="Database manager" ts="2017-04-02 19:45:27.132881211 +0100 BST"
INFO[0025] toJSON taken -> 5.317647046s                  file=operations.go func=operations.getJSON line=263 service="Database manager" ts="2017-04-02 19:45:32.450551417 +0100 BST"

要映射的 sql 是 3 秒,要 json 是 5 秒。

【问题讨论】:

  • 瓶颈,不出所料,是 json.Marshal。还有一些其他的 3rd 方库声称可以更快地处理 JSON 数据。您是否严格要求使用 JSON?
  • 看看你的数据库是否会为你做转换。 Postgres 肯定会,ZeroDB 似乎接受 JSON 查询,但我不确定响应格式是什么。
  • @tier1 这是这个项目的早期版本,所以我愿意听取您的建议而不是 JSON。
  • @DmitriGoldring 你是什么意思我的数据库这样做?在数据库级别还是在编程语言级别?
  • 看起来 MySQL 5.7(+?) 支持JSON

标签: json go goroutine


【解决方案1】:

Go 例程不会提高 CPU 密集型操作(如 JSON 封送处理)的性能。您需要的是更高效的 JSON 封送拆收器。有一些可用的,虽然我没有用过。一个简单的谷歌搜索“更快的 JSON 编组”会出现很多结果。一个流行的是ffjson。我建议从那里开始。

【讨论】:

  • 更新了帖子,你说得对,JSON 元帅花费了太多时间,但我尝试了 ffjson 并且得到了相同的数字。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-07
  • 2011-06-26
  • 2018-05-09
  • 2016-11-21
  • 1970-01-01
相关资源
最近更新 更多