【问题标题】:How to optimize response time for simple web and database application如何优化简单 Web 和数据库应用程序的响应时间
【发布时间】:2019-10-20 19:14:47
【问题描述】:

我是 golang 和数据库优化的新手。

我有一个简单的应用程序编写在 go 和 mysql 数据库中,通过网络初始化发送查询。

接收请求大约需要 5 秒或更长时间?是否有可能以某种方式对其进行优化?

另外如果刷新几次,那么响应可能已经是50s甚至更多了, “无效的内存地址或 nil 指针取消引用”或“错误 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 条,他们的积分约为 1000000 条。

这是每个请求都会调用的函数

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

谢谢。

【问题讨论】:

  • 这个问题太宽泛了。首先学习如何使用 EXPLAIN 来查看如何评估您的查询,使用索引来优化您的查询。了解如何将嵌套查询合并为一个。了解如何调整 Go 程序的性能。
  • @Volker 谢谢你的评论。这是重点,因为我是数据库优化和 Go 的新手,所以我要问方向。解释我已经开始找了。
  • 另一件有帮助的事情:使用连接,而不是循环中的多个查询。
  • @Flimzy,对,让我试试。

标签: mysql performance go web query-optimization


【解决方案1】:

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

在应用启动时连接到您的数据库一次,然后在您的处理程序中使用此连接。数据库将保持空闲连接打开,以便在您在另一个请求中需要它时立即重用。

将您的 db 变量移到外部,例如到一个包级变量,连接到你的数据库并初始化这个db变量一次,例如在您的 main() 或包中的 init() 函数中,然后在您的处理程序中使用它。

sql.Open() 文件表明:

返回的数据库对于多个 goroutine 并发使用是安全的,并维护自己的空闲连接池。因此,Open 函数应该只被调用一次。很少需要关闭数据库。

查看类似问题:mgo - query performance seems consistently slow (500-650ms)

【讨论】:

  • 感谢您的回复。我也在考虑打开数据库,并且在这个go-database-sql.org/accessing.html 页面上也发现了,不要经常打开()和关闭()数据库。相反,为您需要访问的每个不同的数据存储创建一个 sql.DB 对象,并保留它直到程序完成对该数据存储的访问。根据需要传递它。我通过添加全局变量和init方法对代码和问题进行了更改,不幸的是结果相同。
  • 当我说同样的时候,我的意思是,第一次请求大约需要 5 秒,如果你想同时拥有 10 个,那么大约需要 50 多秒。此外,它会返回不同长度的结果,有时是 20 条记录,有时是 7 条,有时是 1 条记录。
  • @yart 您是否为查询准备了数据库索引?
  • 你说的是非聚集索引吗?可以提供样品吗?
【解决方案2】:
SELECT sp_no, first_name FROM sportsmen LIMIT ?,20", rand.Intn(100000)

糟糕的表现。结果很差。

  • 它只会从表格的前 1/3 中选择(cf 100000 vs 300000)。

  • 它偶尔会选择相同的 20 或重叠的 20。

  • 它必须跳过多达 100000 行才能找到第 20 行。(这是性能问题。)

我有多种方法可以改善所有这三个问题:http://mysql.rjweb.org/doc.php/random

并且通过加快查询速度,“连接过多”的问题可能会消失。

【讨论】:

  • 感谢您的评论。这就是我学习的地方。由于打开您的链接后,我仍然不完全清楚,您指的是您建议的特定案例吗?你能根据你的建议写样本吗?
  • @yart - 首先,哪种情况适用?并请提供SHOW CREATE TABLE sportsmen
  • 表 |创建表运动员 |创建表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 |
  • @yart - sp_no -- 不是AUTO_INCREMENT?最小值和最大值是多少? SELECT MIN(sp_no), MAX(sp_no) FROM sportsmen
  • @yart - 我倾向于“案例:AUTO_INCREMENT 有间隙,返回 1 行或更多行”,替换:id->sp_noRandTest->sportsmen,@987654339 @->20, 50->100.
猜你喜欢
  • 2011-03-12
  • 2011-01-10
  • 1970-01-01
  • 2023-04-10
  • 2016-07-16
  • 2013-06-26
  • 1970-01-01
  • 1970-01-01
  • 2017-11-15
相关资源
最近更新 更多