Понимание затрат времени отклика - PullRequest
0 голосов
/ 04 июня 2019

Я новичок в Голанге и пытаюсь написать простой веб-сервер с ответом json. Одна из конечных точек - для доступа к базе данных. При статическом ответе (таком как любая статическая строка или простое преобразование данных) среднее время ответа составляет около 0 с (<1 мс). Если я добавлю какой-нибудь запрос к базе данных, среднее время увеличится до 250 мс, и это не зависит от содержимого функции. Поэтому я пытаюсь понять время этой функции. </p>

Что я уже знаю:

  1. Запрос к БД стоит около 40-50 мс
  2. Расходы на преобразование даты и времени> 1 мс
  3. json.Marshal стоит <1 мс </li>
  4. string(json.Marshal) <1 мс </li>

Вся моя проблема в rows.Next():

  1. все внутри rows.Next() стоит <1 мс отдельно </li>
  2. Стоимость каждой row.Next() процедуры зависит от длины строки и составляет около 10-50 мс.

Если я понимаю, db.Query() получает все строки в одном запросе и сохраняет его в памяти, предоставляя виртуальный курсор в качестве итератора (.Next()). Почему это так дорого стоит?

  1. Все row.Next() обработка данных стоит около 180 мс. При 40 мс db.Query вместе это приблизительно равно общему времени отклика (~ 240 мс)
  2. Если я прокомментирую весь цикл row.Next() и просто наберу db.Query, общий ответ по-прежнему стоит 200-240 мс

Пакеты, которые я использую:

import (
    "database/sql"
    "encoding/json"
    "fmt"
    "log"
    "os"
    "strconv"
    "time"
    "github.com/kshvakov/clickhouse"
    routing "github.com/qiangxue/fasthttp-routing"
    "github.com/valyala/fasthttp"
)
func getRecs(client string, dt string) string {
    dtT := transformDate(&dt)
    rows, err := db.Query(getRecsRequest(&client, &dtT))

    if err != nil {
        log.Fatal(err)
    }
    defer rows.Close()

    start := time.Now()

    got := []databaseRows{}
    elapsed := time.Since(start)
    for rows.Next() {
        elapsed = time.Since(start)
        log.Printf("Next took %s", elapsed)
        var r databaseRows
        err := rows.Scan(&r.ItemIds, &r.DtRecommendation, &r.Number)
        if err != nil {
            log.Fatal(err)
        }
        got = append(got, r)
        start = time.Now()
    }

    jsonData, err := json.Marshal(&got)
    return string(jsonData)
}
func getRecs(client string, dt string) string {
    dtT := transformDate(&dt)
    rows, err := db.Query(getRecsRequest(&client, &dtT))

    if err != nil {
        log.Fatal(err)
    }
    defer rows.Close()

    // start := time.Now()

    got := "[]databaseRows{}"
    // elapsed := time.Since(start)
    // for rows.Next() {
    //  elapsed = time.Since(start)
    //  log.Printf("Next took %s", elapsed)
    //  var r databaseRows
    //  err := rows.Scan(&r.ItemIds, &r.DtRecommendation, &r.Number)
    //  if err != nil {
    //      log.Fatal(err)
    //  }
    //  got = append(got, r)
    //  start = time.Now()
    // }

    jsonData, err := json.Marshal(&got)
    return string(jsonData)
}

Можно ли ожидать, что время отклика веб-сервера будет примерно равно времени запроса базы данных?

UPDATE.

Я пробовал "github.com/jmoiron/sqlx" с "github.com/kshvakov/clickhouse", и результат был таким же; изменен только синтаксис.

Я также попробовал "github.com/mailru/go-clickhouse" с "database / sql". Он предоставляет доступ к HTTP-интерфейсу для clickhouse. Я получил все строки в одном запросе, как и ожидалось, но он требует около 240 мс для HTTP-запроса и около 0 мс для обработки данных. Поэтому я ищу возможность держать соединение TCP открытым, чтобы можно было получить все строки nessasery в одном запросе и работать с моей буфера памяти на стороне программы (с некоторым ограничением буфера в сочетании с буфером TCP / сокета).

ОБНОВЛЕНИЕ 2.

Хорошо, я пробовал собственный клиент clickhouse (и другой такой DBeaver), и ограничение приблизительно 250 мс для этого запроса. Но это может быть только ограничение родного TCPP интерфейса clickhouse. Но что такое операция 40-50ms db.Query (), которая не возвращает строк. Время для запроса на стороне сервера? Или просто проверка строки запроса?

...