Как оптимизировать время отклика для простого веб-приложения и базы данных - PullRequest
0 голосов
/ 05 июня 2019

Я новичок в golang, а также в оптимизации баз данных.

У меня есть простое приложение, написанное на ходу, и база данных mysql, где отправляются запросы, инициализированные через Интернет.

Для получения запросов требуется около 5 секунд или чуть больше? Можно ли как-то его оптимизировать?

Также, если обновить несколько раз, то ответ может быть уже 50 с и даже больше, исключения с «неверным адресом памяти или разыменованием нулевого указателя» или «Ошибка 1040: может появиться слишком много соединений».

Как избежать этого и сделать все запросы управляемыми в течение эффективного периода времени?

Это структура таблицы

 CREATE TABLE sportsmen (
    sp_no int(11) NOT NULL, 
    birth_date date NOT NULL, 
    first_name varchar(14) NOT NULL, 
    last_name varchar(16) NOT NULL, 
    gender enum('M','F') NOT NULL, 
    start_date date NOT NULL, 
    PRIMARY KEY (sp_no)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

CREATE TABLE points (
sp_no INT NOT NULL,
point INT NOT NULL,
date DATE NOT NULL
);

Количество записей составляет около 300000 для спортсменов и 1 000 000 для их очков.

это функция, которая вызывается при каждом запросе

var db *sql.DB

func init() {
    db, _ = sql.Open("mysql", "<connection>?charset=utf8")
    //checkErr(errcon)
    err := db.Ping()
    checkErr(err)
    yt := reflect.TypeOf(db).Kind()
    fmt.Printf("%T: %s\n", yt, yt)
}

func sportsmanPoints(w http.ResponseWriter, r *http.Request) {

    start := time.Now()

    sportsmen, err := db.Query("SELECT sp_no, first_name FROM sportsmen LIMIT ?,20", rand.Intn(100000))
    checkErr(err)

    for sportsmen.Next() {
        var spNo string
        var firstName string
        err = sportsmen.Scan(&spNo, &firstName)
        checkErr(err)
        spPoints, err := db.Query("SELECT max(point) FROM points WHERE sp_no =" + spNo)
        for spPoints.Next() {
            var spPoint int
            err = spPoints.Scan(&spPoint)
            checkErr(err)
            points.Data = ​append​(points.Data, Point{Name: firstName, Point: spPoint})
        }
    }

    data, err := json.Marshal(points.Data)
    if​ err != ​nil​ {
           log.Fatal(err)
     }

    fmt.Fprintln(w, ​string​(data))
    elapsed := time.Since(start)
    fmt.Println(​"Date:"​, time.Now(), ​"Response time:"​, elapsed)
    points.Data = ​nil
    data = ​nil
}

func​ ​checkErr​(err error) {
    if​ err != ​nil​ {
        panic​(err)
    }
}

func​ ​main​() {
    http.HandleFunc(​"/"​, sportsmanPoints)
    err := http.ListenAndServe(​":9090"​, ​nil​)
    if​ err != ​nil​ {
        log.Fatal(​"ListenAndServe: "​, err)
    }
}

Спасибо.

Ответы [ 2 ]

3 голосов
/ 05 июня 2019

Вы подключаетесь к своей базе данных в своем обработчике каждый раз, когда на ваш сервер делается запрос. Одна эта операция может занять несколько секунд. Как только обработчик возвращается, вы просто отбрасываете это соединение (вы даже не закрываете его, так что это может привести к простоям в течение некоторого времени, прежде чем он будет закрыт, из-за того, что ресурсы сервера базы данных перегружены, поскольку соединения, скорее всего, ограничены на сервере). Не делай этого.

Подключитесь к своей базе данных один раз при запуске приложения и используйте это соединение в своем обработчике. База данных будет держать незанятые соединения открытыми, готовыми к немедленному использованию в случае необходимости в другом запросе.

Переместите переменную db наружу, например, к переменной уровня пакета, подключитесь к вашей базе данных и инициализируйте эту переменную db один раз, например, в вашей main() или в пакете init() функции, и просто используйте ее в своем обработчике.

sql.Open() документы, которые:

Возвращенная БД безопасна для одновременного использования несколькими программами и поддерживает собственный пул свободных соединений. Таким образом, функция Open должна вызываться только один раз. Редко нужно закрывать БД.

См. Аналогичный вопрос: mgo - производительность запросов кажется постоянно низкой (500-650 мс)

0 голосов
/ 06 июня 2019
SELECT sp_no, first_name FROM sportsmen LIMIT ?,20", rand.Intn(100000)

Ужасная производительность.И плохие результаты.

  • Он будет выбирать только из первой 1/3 таблицы (ср. 100000 против 300000).

  • Это будетиногда выбирайте те же 20 или перекрывающиеся 20.

  • Он должен пропустить до 100000 строк, прежде чем найти 20. (Это проблема производительности.)

У меня есть несколько способов улучшить все эти три проблемы: http://mysql.rjweb.org/doc.php/random

И при ускорении запроса «слишком много соединений», скорее всего, исчезнет.

...