当前位置:网站首页>Common fault analysis and Countermeasures of MySQL in go language
Common fault analysis and Countermeasures of MySQL in go language
2022-06-23 11:53:00 【InfoQ】

1、
Resources are not released in time

// QueryContext Query some results
// query:select * from test limit 10
func (db *DB) QueryContext(ctx context.Context, query string, args ...any) (*Rows, error)
type Rows struct{
Close( ) error
ColumnTypes( ) ( [ ]*ColumnType, error)
Columns( ) ( [ ]string, error)
Err( ) error
Next( ) bool
NextResultSet( ) bool
Scan(dest ...any) error
}
1.1 experiment 1- Do not call Rows.Close()
select * from user;
+----+-------+---------------------+----------+--------+
| id | email | register_time | password | status |
+----+-------+---------------------+----------+--------+
| 2 | dw | 2011-11-11 11:01:00 | d | 0 |
+----+-------+---------------------+----------+--------+
1 row in set (0.03 sec)
package main
import (
"context"
"database/sql"
"encoding/json"
"fmt"
"sync"
"time"
_ "github.com/go-sql-driver/mysql"
)
func main() {
db, err := sql.Open("mysql", "root:@tcp(127.0.0.1:3306)/test")
if err != nil {
panic(err)
}
db.SetMaxOpenConns(1)
// Start a separate collaboration , For output DB Status information
go func() {
tk := time.NewTicker(3 * time.Second)
defer tk.Stop()
for range tk.C {
bf, _ := json.Marshal(db.Stats())
fmt.Println("db.Stats=", string(bf))
}
}()
// start-up 10 Collaborators cheng , Query data at the same time
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
queryOne(id, db)
}(i)
}
wg.Wait()
fmt.Println("finish")
}
func queryOne(id int, db *sql.DB) {
start := time.Now()
rows, err := db.QueryContext(context.Background(), "select * from user limit 1")
if err != nil {
panic(err)
}
// defer rows.Close()
// Not from Rows Read the results in , No call rows.Close
fmt.Println("id=", id, "hasNext=", rows.Next(), "cost=", time.Since(start))
}
id= 0 hasNext= true cost= 9.607371ms
db.Stats= {"MaxOpenConnections":1,"OpenConnections":1,"InUse":1,"Idle":0,"WaitCount":9,"WaitDuration":0,"MaxIdleClosed":0,"MaxIdleTimeClosed":0,"MaxLifetimeClosed":0}
db.Stats= {"MaxOpenConnections":1,"OpenConnections":1,"InUse":1,"Idle":0,"WaitCount":9,"WaitDuration":0,"MaxIdleClosed":0,"MaxIdleTimeClosed":0,"MaxLifetimeClosed":0}
db.Stats= {"MaxOpenConnections":1,"OpenConnections":1,"InUse":1,"Idle":0,"WaitCount":9,"WaitDuration":0,"MaxIdleClosed":0,"MaxIdleTimeClosed":0,"MaxLifetimeClosed":0}
db.Stats= {"MaxOpenConnections":1,"OpenConnections":1,"InUse":1,"Idle":0,"WaitCount":9,"WaitDuration":0,"MaxIdleClosed":0,"MaxIdleTimeClosed":0,"MaxLifetimeClosed":0}
db.Stats= {"MaxOpenConnections":1,"OpenConnections":1,"InUse":1,"Idle":0,"WaitCount":9,"WaitDuration":0,"MaxIdleClosed":0,"MaxIdleTimeClosed":0,"MaxLifetimeClosed":0}
db.Stats= {"MaxOpenConnections":1,"OpenConnections":1,"InUse":1,"Idle":0,"WaitCount":9,"WaitDuration":0,"MaxIdleClosed":0,"MaxIdleTimeClosed":0,"MaxLifetimeClosed":0}
db.Stats= {"MaxOpenConnections":1,"OpenConnections":1,"InUse":1,"Idle":0,"WaitCount":9,"WaitDuration":0,"MaxIdleClosed":0,"MaxIdleTimeClosed":0,"MaxLifetimeClosed":0}
db.Stats= {"MaxOpenConnections":1,"OpenConnections":1,"InUse":1,"Idle":0,"WaitCount":9,"WaitDuration":0,"MaxIdleClosed":0,"MaxIdleTimeClosed":0,"MaxLifetimeClosed":0}
db.Stats= {"MaxOpenConnections":1,"OpenConnections":1,"InUse":1,"Idle":0,"WaitCount":9,"WaitDuration":0,"MaxIdleClosed":0,"MaxIdleTimeClosed":0,"MaxLifetimeClosed":0}
{
"MaxOpenConnections": 1, // Maximum number of open connections , Consistent with the code settings , yes 1
"OpenConnections": 1, // Number of open connections
"InUse": 1, // Number of connections in use
"Idle": 0, // Number of idle connections
"WaitCount": 9, // Number of waiting connections
"WaitDuration": 0, // The total waiting time ( Count while waiting for exit )
"MaxIdleClosed": 0, // Exceed the maximum idle Count the total number of closed connections
"MaxIdleTimeClosed": 0, // Overtake and catch up idle Total number of connections closed at time
"MaxLifetimeClosed": 0 // The total number of connections closed over the maximum lifetime
}
1.2 experiment 2- call Rows.Close()
func queryOne(id int, db *sql.DB) {
start := time.Now()
rows, err := db.QueryContext(context.
Background(), "select * from user limit 1")
if err != nil {
panic(err)
}
defer rows.Close() // Opened the comment here ,Close Methods release resources
fmt.Println("id=", id, "hasNext=", rows.Next(), "cost=", time.Since(start))
}
# go run main.go
id= 9 hasNext= true cost= 4.082448ms
id= 3 hasNext= true cost= 5.670052ms
id= 8 hasNext= true cost= 5.745443ms
id= 5 hasNext= true cost= 6.238615ms
id= 6 hasNext= true cost= 6.520818ms
id= 7 hasNext= true cost= 6.697782ms
id= 4 hasNext= true cost= 6.953454ms
id= 1 hasNext= true cost= 7.1079ms
id= 0 hasNext= true cost= 7.3036ms
id= 2 hasNext= true cost= 7.464726ms
finish
1.3 experiment 3- Use the... With timeout Context
func queryOne(id int, db *sql.DB) {
start := time.Now()
ctx,cancel:=context.WithTimeout(context.Background(),time.Second) // The key
defer cancel() // The key . If you replace this line with _=cancel, Another result
rows, err := db.QueryContext(ctx , "select * fro m user limit 1")
if err != nil {
// panic (err)
fmt.Println("BeginTx failed:",err)
return
}
// defer rows.Close () // Opened the note here Interpretation of the ,Close Methods release resources
fmt.Println("id=" , id, "hasNext=", rows.Next(), "cost=", time.Since (start))
}
id= 9 hasNext= true cost= 1.483715ms
id= 3 hasNext= true cost= 175.675µs
id= 6 hasNext= true cost= 1.277596ms
id= 1 hasNext= true cost= 174.307µs
id= 7 hasNext= true cost= 108.061µs
id= 4 hasNext= true cost= 115.072µs
id= 2 hasNext= true cost= 104.046µs
id= 0 hasNext= true cost= 96.833µs
id= 8 hasNext= true cost= 123.758µs
id= 5 hasNext= true cost= 92.791µs
finish
d= 9 hasNext= true cost= 2.581813ms
BeginTx failed: context deadline exceeded
BeginTx failed: context deadline exceeded
BeginTx failed: context deadline exceeded
BeginTx failed: context deadline exceeded
BeginTx failed: context deadline exceeded
BeginTx failed: context deadline exceeded
BeginTx failed: context deadline exceeded
BeginTx failed: context deadline exceeded
BeginTx failed: context deadline exceeded
1.4 Solution
- We should use QueryContext This class supports incoming context Function of , And pass in the... With timeout control context, And after the logic execution is completed , You should use defer Methods will context Cancel .
- For results that return a stream type , After use, you must call Close Method to free resources .
- all *sql.DB、*sql.Tx、*sql.Stmt Return *Conn、*Stmt、*Rows These types all need Close:
type DB/Tx/Stmt struct{
Conn(ctx context.Context) (*Conn, error)
Prepare(query string) (*Stmt, error)
PrepareContext(ctx context.Context, query string) (*Stmt, error)
Query(query string, args ...any) (*Rows, error)
QueryContext(ctx context.Context, query string, args ...any) (*Rows, error)
}
type user struct {
ID int64 `ddb:"id"`
Status uint8 `ddb:"status"`
}
func readUsers(ctx context.Context)([]*user,error)
rows, err := cli.QueryContext(ctx, "select * from user where status=1 limit 5")
if err != nil {
return nil,err
}
var userList []*user
err=mysql.ReadRowsClose(rows, &userList)
return userList,err
}
b := &SimpleBuilder{
SQL: "SELECT id,name from user where id=1",
}
type user struct{
Name string `ddb:"name"`
ID int `ddb:"id"`
}
var us []*user
err = mysql.QueryWithBuilderScan(ctx, client, b, &us)
2、 The transaction is incomplete
func (db *DB) BeginTx(ctx context.Context, opts *TxOptions) (*Tx, error)
type Tx
func (tx *Tx) Commit() error // Commit transaction
func (tx *Tx) Rollback ( ) error // Roll back the transaction
func (tx *Tx) Exec(query string, args ...any) (Result, error)
func (tx *Tx) ExecContext(ctx context.Context, query string, args ...any) (Result, error)
func (tx *Tx) Prepare(query string) (*Stmt, error)
func (tx *Tx) PrepareContext(ctx context.Context, query string) (*Stmt, error)
func (tx *Tx) Query(query string, args ...any) (*Rows, error)
func (tx *Tx) QueryContext(ctx context.Context, query string, args ...any) (*Rows, error)
func (tx *Tx) QueryRow(query string, args ...any) *Row
func (tx *Tx) QueryRowContext(ctx context.Context, query string, args ...any) *Row
func (tx *Tx) Stmt(stmt *Stmt) *Stmt
func (tx *Tx) StmtContext(ctx context.Context, stmt *Stmt) *Stmt
2.1 and PHP The difference between
<?php
/* Start a transaction , Turn off auto submit */
$dbh->beginTransaction();
/* Insert multiple rows of records based on all or nothing ( Or insert them all , Or not to insert them all ) */
$sql = 'INSERT INTO fruit(name, colour, calories) VALUES (?, ?, ?)';
$sth = $dbh->prepare($sql);
foreach ($fruits as $fruit) {
$sth->execute(array(
$fruit->name,
$fruit->colour,
$fruit->calories,
));
}
/* Submit changes */
$dbh->commit();
// This code comes from https://www.php.net/manual/zh/pdo.commit.php
import (
"context"
"database/sql"
"log"
)
var (
ctx context.Context
db *sql.DB
)
func main() {
tx, err := db.BeginTx(ctx, &sql.TxOptions{Isolation: sql.LevelSerializable})
if err != nil {
log.Fatal(err)
}
id := 37
// Use Tx perform Update sentence , Instead of continuing to use db.Exec
_, execErr := tx.Exec(`UPDATE users SET status = ? WHERE id = ?`, "paid", id)
if execErr != nil {
_ = tx.Rollback()
log.Fatal(execErr)
}
if err := tx.Commit(); err != nil {
log.Fatal(err)
}
}
// This code comes from :https://pkg.go.dev/database/[email protected]#example-DB.BeginTx
2.2 experiment
func queryOne(id int, db *sql.DB) {
tx,err:=db.BeginTx(context.Background(),nil)
if err!=nil{
panic(err)
}
// defer tx.Rollback()
start := time.Now()
rows, err := tx.QueryContext(context.Background(), "select * from user limit 1")
if err != nil {
panic(err)
}
defer rows.Close()
// The transaction did not roll back 、 Submit
fmt.Println("id=", id, "hasNext=", rows.Next(), "cost=", time.Since(start))
}
id= 9 hasNext= true cost= 11.670369ms
db.Stats= {"MaxOpenConnections":1,"OpenConnections":1,"InUse":1,"Idle":0,"WaitCount":9,"WaitDuration":0,"MaxIdleClosed":0,"MaxIdleTimeClosed":0,"MaxLifetimeClosed":0}
db.Stats= {"MaxOpenConnections":1,"OpenConnections":1,"InUse":1,"Idle":0,"WaitCount":9,"WaitDuration":0,"MaxIdleClosed":0,"MaxIdleTimeClosed":0,"MaxLifetimeClosed":0}
db.Stats= {"MaxOpenConnections":1,"OpenConnections":1,"InUse":1,"Idle":0,"WaitCount":9,"WaitDuration":0,"MaxIdleClosed":0,"MaxIdleTimeClosed":0,"MaxLifetimeClosed":0}
db.Stats= {"MaxOpenConnections":1,"OpenConnections":1,"InUse":1,"Idle":0,"WaitCount":9,"WaitDuration":0,"MaxIdleClosed":0,"MaxIdleTimeClosed":0,"MaxLifetimeClosed":0}
2.3 Solution
// Definition of business logic function , In this function, you can add, delete, change and query in the transaction
// return error==nil be tx.Commit(), otherwise tx.Rollback()
type doFunc func(ctx context.Context, qe QueryExecuto r) error
func BeginTx(ctx context.Context, cli CanBeginTx, opts *sql.TxOptions, do doFunc) error
var cli mysql.Client
updateUserNameByID := func(ctx context.Context, id uint64, name string) error {
// Use BeginTx Method , Can handle affairs more easily
err := mysql.BeginTx(ctx, cli, nil, func(ctx context.Context, qe mysq.QueryExecutor) error {
// Other database update logic is omitted
b1 := &mysql.SimpleBuilder{}
b1.Append("select name from user where uid=?", id)
var oldName string
if err := mysql.QueryRowWithBuilderScan(ctx, qe, b1, &oldName); err != nil {
return err
}
if oldName == " Zhugeliang " || oldName == name {
// return err,mysql.BeginTx Method will rollback the transaction
return fmt.Errorf(" No need to update , Overall transaction rollback ")
}
b2 := &mysql.SimpleBuilder{}
b2.Append("update user set name=? where id=?", name, id)
_, err := mysql.ExecWithBuilder(ctx, qe, b2)
if err != nil {
return err
}
// return nil,mysql.BeginTx Method will commit the transaction
return nil
})
return err
}
3、 Other reasons
3.1 Preprocessing is not supported
Name = "demo"
# Other configuration items are omitted
[MySQL]
Username = "example"
# Other parameters are omitted
DSNParams ="charset=utf8&timeout=90s&collation=utf8mb4_unicode_ci&parseTime=true&interpolateParams=true"
4、 How to check
{
"MaxOpenConnections" : 1 , // Maximum number of open connections , Consistent with the code settings , yes 1
"OpenConnections" : 1 , // Number of open connections
"InUse" : 1 , // Number of connections in use
"Idle" : 0 , // Number of idle connections
"WaitCount" : 9 , // Number of waiting connections
"WaitDuration" : 0 , // The total waiting time ( Count while waiting for exit )
"MaxIdleClosed" : 0 , // Exceed the maximum idle Count the total number of closed connections
"MaxIdleTimeClosed" : 0 , // Overtake and catch up idle Total number of connections closed at time
"MaxLifetimeClosed" : 0 // The total number of connections closed over the maximum lifetime
}
4.1 Integrate GDP Application panel

4.2 Integrated monitoring
client_connpool{servicer="demo_mysql",stats="ConnType"} 1
client_connpool{servicer="demo_mysql",stats="IPTotal"} 1
client_connpool{servicer="demo_mysql",stats="InUseAvg"} 0
client_connpool{servicer="demo_mysql",stats="InUseMax"} 0
client_connpool{servicer="demo_mysql",stats="InUseTotal"} 0
client_connpool{servicer="demo_mysql",stats="NumOpenAvg"} 0
client_connpool{servicer="demo_mysql",stats="NumOpenCfg"} 100
client_connpool{servicer="demo_mysql",stats="NumOpenMax"} 0
client_connpool{servicer="demo_mysql",stats="NumOpenTotal"} 0
4.3 Output to log
边栏推荐
- 杜邦分析法解读:安阳钢铁股份有限公司企业投资价值何在?
- 股权转让热点:重庆建科建设工程质量检测有限公司93.75%股权转让
- 自动化或电气专业必备软件
- php 手写一个完美的守护进程
- 请问连接oracle时,这个version 1.54 是什么的version?
- Leetcode 1209. Delete all adjacent duplicates II in the string (not in the initial version)
- Necessary software for automation or electrical specialty
- “梦想童行” 2022年广汽本田儿童道路安全公益行走进东北
- 【云驻共创】华为云HCIA-IoT V2.5培训系列内容之物联网概览
- tensorflow2的GradientTape求梯度
猜你喜欢

成熟的知识管理,应具备哪些条件?

【零基础微信小程序】基于百度大脑人像分割的证件照换底色小程序实战开发

学习笔记 scrapy 爬虫框架

Redis 入门-第一篇-数据结构与对象-简单动态字符串(SDS)

Getting started with redis - Chapter 4 - data structures and objects - jump table
![Openharmony application development [01]](/img/b1/1e37cecd3d3f9e46444c202cfb1b99.png)
Openharmony application development [01]

汉源高科USB3.0光端机USB工业触摸屏光端机USB3.0光纤延长器USB3.0光纤传输器

Meta称英安全法可能“扫描所有私人信息” 或侵犯隐私

六维图剖析:中国建筑集团有限公司企业成长性分析

@黑马粉丝,这份「高温补贴」你还没领?
随机推荐
Tamidog | analysis of investor types and enterprise investment types
Use monotone stack to solve problems
汉源高科USB3.0光端机USB工业触摸屏光端机USB3.0光纤延长器USB3.0光纤传输器
Getting started with redis - Chapter 4 - data structures and objects - jump table
OpenHarmony应用开发【01】
Simulation questions and answers of the latest national fire-fighting facility operators (primary fire-fighting facility operators) in 2022
电感有极性吗?
杜邦分析法解读:安阳钢铁股份有限公司企业投资价值何在?
go-zero微服务实战系列(六、缓存一致性保证)
汉源高科1路千兆光口转4路千兆以太网电口千兆1光4电光纤收发器
KDD 2022 | 基于分层图扩散学习的癫痫波预测
十大劵商如何开户?在线开户安全么?
RF analyzer demo setup
Mobile securities account opening transaction? Is it safe to open an account online now?
Necessary software for automation or electrical specialty
Introduction and use of vector
【云驻共创】无码时代,软件开发如何走向每个人?
惊!AMD 350亿美元收购赛灵思!
quarkus+saas多租户动态数据源切换实现简单完美
有没有碰到过flink1.15.0 和 flink-cdc-mysql 2.2.1不兼容的情况?f