英文:
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 skinbank
和show 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论