如何为简单的Web和数据库应用程序优化响应时间

I'm new to golang and also to database optimizations.

I have simple app written on go and mysql database where send queries initialised through the web.

For receiving requests it take around 5s or little bit more? Is it possible somehow to optimize it?

Also if refreshing several times, then response could be already 50s and even more, exceptions with "invalid memory address or nil pointer dereference" or "Error 1040: Too many connections could appear".

How to avoid this and have all requests to be managiable in a efficient time frame?

This is table structure

 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
);

Number of records is around 300000 for sportsmen and 1 000 000 for their points.

this is function which is called on every request

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
", 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)
    }
}

Thank you.

doyhq66282
doyhq66282 是的,让我尝试一下。
大约一年之前 回复
dongyan7950
dongyan7950 另一件事会有所帮助:使用联接,而不是在循环中使用多个查询。
大约一年之前 回复
duan97689
duan97689 感谢您的评论。重点在于,因为这是我不熟悉数据库优化和Go的新知识,所以这就是我要问的方向。说明我已经开始寻找。
大约一年之前 回复
dqy1265
dqy1265 这个问题太广泛了。首先学习如何使用EXPLAIN来查看查询的评估方式,然后尝试使用索引来优化查询。了解如何将嵌套查询合并为一个。了解如何调整Go程序的性能。
大约一年之前 回复

2个回答



每次对服务器发出请求时,您都将连接到处理程序中的数据库。 仅此操作就可能需要数秒钟。 一旦处理程序返回,您就可以丢弃该连接(您甚至不会关闭该连接,因此在关闭之前可能会空闲一段时间,因为连接很可能受到服务器的限制,因此会占用db服务器资源)。 不要这样做。</ p>

在应用程序启动时连接到数据库一次,并在处理程序中使用此连接。 </ p>

将您的 db </ code>变量移至外部,例如,将数据库保持打开状态,以便在需要时立即重用。 到包级变量,请连接到数据库,然后一次初始化此 db </ code>变量,例如 在您的 main()</ code>或包中的 init()</ code>函数中,然后在处理程序中使用它即可。</ p>

sql.Open()</ code> 文档说明:</ p> \ n


返回的数据库可以安全地被多个goroutine并发使用,并维护其自己的空闲连接池。 因此,Open函数应仅被调用一次。 </ p>
</ blockquote>

请参阅类似的问题: mgo-查询性能似乎一直很慢(500-650ms) </ p>
</ div>

展开原文

原文

You connect to your database in your handler, every time a request is made to your server. This operation alone could take multiple seconds. Once the handler returns, you just throw away that connection (you don't even close that, so that may be idling for some time before getting closed, hogging db server resources as connections are most likely limited on the server). Don't do that.

Connect to your database once, on app startup, and use this connection in your handler. The database will keep idle connections open, ready to be reused immediately should you need it in another request.

Move your db variable outside, e.g. to a package-level variable, connect to your database and initialzie this db variable once, e.g. in your main() or in a package init() function, and just use it in your handler.

sql.Open() documents that:

The returned DB is safe for concurrent use by multiple goroutines and maintains its own pool of idle connections. Thus, the Open function should be called just once. It is rarely necessary to close a DB.

See similar question: mgo - query performance seems consistently slow (500-650ms)

duanbi1888
duanbi1888 sql.DB管理内部连接池,默认情况下它不限制打开的连接。 使用DB.SetMaxOpenConns()限制最大打开连接数。
大约一年之前 回复
douyue1481
douyue1481 再次感谢。 再澄清一下。 尽管我已经全局定义了到数据库的连接,但是如果我同时发出1000个请求,或者仍然以某种方式控制db变量的全局定义,那么我仍然可能会有“打开的连接过多”的情况?
大约一年之前 回复
dts777777
dts777777 这种波动可能是许多事情造成的,例如 如果在后台建立新的数据库连接,显然要比仅重用现有的数据库连接花费更多的时间。 同样,如果数据库服务器被缓存,则它们可能会更快地响应过去的查询。 即使最慢的时间也只有70毫秒,您不必担心。
大约一年之前 回复
douyigua5381
douyigua5381 固定密钥和打开数据库连接方式的组合在大约20ms-70ms的时间返回。 谢谢。 您知道为什么有时还是20毫秒,有时还是70毫秒吗?
大约一年之前 回复
druybew06513
druybew06513 请参见MySQL-INDEXES和MySQL如何使用索引。
大约一年之前 回复
doubihuai8468
doubihuai8468 您是在谈论非聚集索引吗? 您能提供样品吗?
大约一年之前 回复
douwen5681
douwen5681 您是否有查询数据库索引?
大约一年之前 回复
duanlinpi0265
duanlinpi0265 当我说相同的意思时,第一次请求大约需要5秒,如果您想同时拥有10个请求,则大约需要50多秒。 而且,它返回不同长度的结果,有时会返回20条记录,有时会返回7条记录,有时会返回1条记录。
大约一年之前 回复
doula2426
doula2426 感谢您的回复。 我也在考虑打开数据库,并且还在go-database-sql.org/accessing.html页面上找到了Do n't t Open()和Close()数据库。 而是,为您需要访问的每个不同的数据存储创建一个sql.DB对象,并将其保留直到程序完成对该数据存储的访问。 根据需要将其传递。 我通过添加全局变量和init方法对代码和问题进行了更改,不幸的是,结果相同。
大约一年之前 回复



  SELECT sp_no,来自运动员的名字LIMIT?,20“,rand.Intn(100000)
</ code> < / pre>

糟糕的性能。而且效果不佳。</ p>


  • 它只会从表格的前1/3中选取( cf 100000 vs 300000)。</ p> </ li>

  • 它将偶尔选择相同的20个或重叠的20个。</ p> </ li>

  • 它 在找到20个之前,必须跳过多达100000行。(这是性能问题。)</ p> </ li>
    </ ul>

    我在这三个方面都有多种改进方法 其中的问题: http://mysql.rjweb.org/doc.php/random </ p>

    ,通过加快查询速度,“太多连接”可能会消失。</ p>
    </ div>

展开原文

原文

SELECT sp_no, first_name FROM sportsmen LIMIT ?,20", rand.Intn(100000)

Terrible performance. And poor results.

  • It will only pick from the first 1/3 of the table (cf 100000 vs 300000).

  • It will pick the same 20 or overlapping 20 occasionally.

  • It must skip over up to 100000 rows before finding the 20. (This is the performance problem.)

I have multiple ways to improve on all three of those issues: http://mysql.rjweb.org/doc.php/random

And by speeding up the query, the "too many connections" is likely to go away.

doupao5296
doupao5296 -我倾向于“案例:有间隙的AUTO_INCREMENT,返回1或更多行”,替换为:id-> sp_no,RandTest-> sportsmen,10-> 20、50-> 100。
大约一年之前 回复
dtr32787
dtr32787 -sp_no-不是AUTO_INCREMENT吗? 最小和最大值是多少? 从运动员中选择MIN(sp_no),MAX(sp_no)
大约一年之前 回复
douzhoubing2805
douzhoubing2805 桌子 创建桌运动员| 创建表运动员(sp_no int(11)NOT NULL,出生日期date NOT NULL,first_name varchar(14)NOT NULL,last_name varchar(16)NOT NULL,性别enum('M','F')NOT NULL,start_date date NOT NULL,主键(sp_no))ENGINE = InnoDB DEFAULT CHARSET = latin1 |
大约一年之前 回复
dpgbh20688
dpgbh20688 -首先,哪种情况适用? 并请提供SHOW CREATE TABLE运动员
大约一年之前 回复
doupu2722
doupu2722 谢谢你的评论。 这是我要学习的地方。 由于打开链接后,对于我来说还是不清楚的,所以您是指所建议的特定情况吗? 您能否根据您的建议编写示例?
大约一年之前 回复
Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问