为什么这段很小的代码执行这几个数据库调用要花费11秒的时间?

huangapple go评论74阅读模式
英文:

Why this small bit of code takes 11 seconds to execute these few database calls?

问题

我有这段代码:

package tradebot

import (
	"log"
	"net/http"
	"strconv"

	"github.com/Philipp15b/go-steam/tradeoffer"
	"github.com/gorilla/mux"
)

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

	vars := mux.Vars(r)
	var tradeId tradeoffer.TradeOfferId
	var sTradeId string
	if _, has := vars["TradeId"]; has {
		var err error
		sTradeId = vars["TradeId"]
		id, err := strconv.ParseUint(vars["TradeId"], 10, 64)
		if err != nil {
			http.Error(w, "Error accepting trade offer", 500)
		}
		tradeId = tradeoffer.TradeOfferId(id)
	}

	err := Trader.Accept(tradeId)
	if err != nil {
		log.Println(err.Error())
		http.Error(w, "Failed to accept trade try again after", 500)
	}
	rows, err := Db.Query("SELECT DepositedBy, Price FROM skinbank WHERE tradeofferid=? AND accepted=?", sTradeId, 0)
	if err != nil {
		log.Println(err)
		http.Error(w, "Your trade offer was accepted but there was an error on our side. Please open a ticket with the trade ID: "+sTradeId+" so we can address this issue. Thank you and sorry for the inconvenience", 500)
	}
	foundRows := false
	for rows.Next() {
		foundRows = true
		var price float64
		var depositedby string
		err = rows.Scan(&depositedby, &price)
		if err != nil {
			log.Println(err)
			http.Error(w, "Your trade offer was accepted but there was an error on our side. Please open a ticket with the trade ID: "+sTradeId+" so we can address this issue. Thank you and sorry for the inconvenience", 500)
		}
		if err != nil {
			log.Println(err)
			http.Error(w, "Your trade offer was accepted but there was an error on our side. Please open a ticket with the trade ID: "+sTradeId+" so we can address this issue. Thank you and sorry for the inconvenience", 500)
		}
		log.Println(depositedby, price)
		_, err := Db.Query("UPDATE accounts SET credits = credits + ? WHERE steamid=?", price, depositedby)
		if err != nil {
			log.Println(err)
			http.Error(w, "Your trade offer was accepted but there was an error on our side. Please open a ticket with the trade ID: "+sTradeId+" so we can address this issue. Thank you and sorry for the inconvenience", 500)
		}
	}

	_, err = Db.Query("UPDATE skinbank SET accepted=? WHERE tradeofferid=?", 1, sTradeId)
	if err != nil {
		log.Println(err)
		http.Error(w, "Your trade offer was accepted but there was an error on our side. Please open a ticket with the trade ID: "+sTradeId+" so we can address this issue. Thank you and sorry for the inconvenience", 500)
	}
	log.Println("Trade accepted: " + sTradeId)
	if foundRows {
		http.Redirect(w, r, "/profile", 303)
	} else {
		http.Error(w, "No trade with that id, either doesn't exist or already accepted.", 500)
	}
}

这主要是一些对数据库的MySQL调用,用于更新/获取一些信息。我想知道是否有人能告诉我在这里我做得不高效的地方,导致它运行需要11秒(在for rows.Next()调用中循环大约6个项目)。

英文:

I have this bit of code:

package tradebot

import (
	"log"
	"net/http"
	"strconv"

	"github.com/Philipp15b/go-steam/tradeoffer"
	"github.com/gorilla/mux"
)

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

	vars := mux.Vars(r)
	var tradeId tradeoffer.TradeOfferId
	var sTradeId string
	if _, has := vars["TradeId"]; has {
		var err error
		sTradeId = vars["TradeId"]
		id, err := strconv.ParseUint(vars["TradeId"], 10, 64)
		if err != nil {
			http.Error(w, "Error accepting trade offer", 500)
		}
		tradeId = tradeoffer.TradeOfferId(id)
	}

	err := Trader.Accept(tradeId)
	if err != nil {
		log.Println(err.Error())
		http.Error(w, "Failed to accept trade try again after", 500)
	}
	rows, err := Db.Query("SELECT DepositedBy, Price FROM skinbank WHERE tradeofferid=? AND accepted=?", sTradeId, 0)
	if err != nil {
		log.Println(err)
		http.Error(w, "Your trade offer was accepted but there was an error on our side. Please open a ticket with the trade ID: "+sTradeId+" so we can address this issue. Thank you and sorry for the inconvenience", 500)
	}
	foundRows := false
	for rows.Next() {
		foundRows = true
		var price float64
		var depositedby string
		err = rows.Scan(&depositedby, &price)
		if err != nil {
			log.Println(err)
			http.Error(w, "Your trade offer was accepted but there was an error on our side. Please open a ticket with the trade ID: "+sTradeId+" so we can address this issue. Thank you and sorry for the inconvenience", 500)
		}
		if err != nil {
			log.Println(err)
			http.Error(w, "Your trade offer was accepted but there was an error on our side. Please open a ticket with the trade ID: "+sTradeId+" so we can address this issue. Thank you and sorry for the inconvenience", 500)
		}
		log.Println(depositedby, price)
		_, err := Db.Query("UPDATE accounts SET credits = credits + ? WHERE steamid=?", price, depositedby)
		if err != nil {
			log.Println(err)
			http.Error(w, "Your trade offer was accepted but there was an error on our side. Please open a ticket with the trade ID: "+sTradeId+" so we can address this issue. Thank you and sorry for the inconvenience", 500)
		}
	}

	_, err = Db.Query("UPDATE skinbank SET accepted=? WHERE tradeofferid=?", 1, sTradeId)
	if err != nil {
		log.Println(err)
		http.Error(w, "Your trade offer was accepted but there was an error on our side. Please open a ticket with the trade ID: "+sTradeId+" so we can address this issue. Thank you and sorry for the inconvenience", 500)
	}
	log.Println("Trade accepted: " + sTradeId)
	if foundRows {
		http.Redirect(w, r, "/profile", 303)
	} else {
		http.Error(w, "No trade with that id, either doesn't exist or already accepted.", 500)
	}
}

It is mostly just a few MySQL calls to the database to update / get a bit of information, I was wondering if anybody can tell me what I have done inefficiently here that it's causing this to take 11 seconds to run (with about 6 items to loop in the for rows.Next() call.

答案1

得分: 2

样本A:

rows, err := Db.Query("SELECT DepositedBy, Price FROM skinbank WHERE tradeofferid=? AND accepted=?", sTradeId, 0)

样本B:(在for循环内部)

_, err := Db.Query("UPDATE accounts SET credits = credits + ? WHERE steamid=?", price, depositedby)

样本C:

_, err = Db.Query("UPDATE skinbank SET accepted=? WHERE tradeofferid=?", 1, sTradeId)

在这些代码周围添加性能分析计时语句。如果你不知道如何做,请问一下。如果需要更多帮助,请通过执行show create table skinbankshow create table credits来显示表模式。这将为索引提供一些线索。告诉我们这两个表的行数。

如果你有大量的行,如果没有适当的索引,可能会执行表扫描而不是快速查找,以获取更新语句中的行。实际上,在选择语句中也是如此。现在你不知道时间。

至少,在以下字段上建立索引:

  • skinbank(tradeofferid,accepted) - 一个组合索引,对样本A有用
  • credits(steamid) - 对样本B有用

至于样本C,它应该选择刚刚建议的组合索引的最左边。因此,如果你选择组合路线,只在skinbank(tradeofferid)上建立一个单独的索引就足够了。

英文:

Specimen A:

rows, err := Db.Query("SELECT DepositedBy, Price FROM skinbank 
WHERE tradeofferid=? AND accepted=?", sTradeId, 0)

Specimen B: (inside for loop)

_, err := Db.Query("UPDATE accounts SET credits = credits + ? WHERE steamid=?", price, depositedby)

Specimen C:

_, err = Db.Query("UPDATE skinbank SET accepted=? WHERE tradeofferid=?", 1, sTradeId)

Put profiling timing statement around these. If you don't know how, ask. Show the table schemas if you need more help by issuing show create table skinbank and show create table credits. This will enable some clue on indexes. Tell us the rowcounts of the two tables.

If you have a ton of rows, it is possible without proper indexes that you are performing tablescans, not fast lookups, to get to the rows in the update where clause. Heck, in the select too. Right now you don't know the timings.

At the very least, have indexes on

skinbank(tradeofferid,accepted) -- a composite index, useful for Specimen A
credits(steamid) -- useful for Specimen B

As for specimen C, it should pick up the left-most of the composite index just suggested. So a separate index on just skinbank(tradeofferid) is overkill if you are going the composite route.

huangapple
  • 本文由 发表于 2015年8月30日 16:39:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/32294767.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定