使用Go驱动包Go-MySQL-Driver连接MySQL数据库

如果您的应用侧使用Go语言,且数据库连接创建频繁(例如短连接场景)或连接数量较大(大于MySQL数据库的连接数限制),您可以参考本教程通过Go驱动包Go-MySQL-Driver连接RDS MySQL数据库,降低连接建立频率以减少数据库主线程的开销。此外,本文还提供连接后操作RDS MySQL数据库的方法。

说明

Go-MySQL-Driver为Go语言的第三方数据库驱动包,支持连接MySQL与MariaDB数据库,详细信息请参见Go-MySQL-Driver

使用限制

RDS MySQL实例的数据库版本仅支持5.7、8.0。

准备工作

  • 已下载并安装Go环境,建议安装Go 1.20及以上版本。

  • 已将客户端IP地址添加至RDS实例白名单中。具体操作,请参见设置白名单

    说明

    若您的应用程序部署在阿里云ECS服务器上,且ECS与RDS实例位于同一地域和同一VPC,则无需设置IP白名单。

使用Go-MySQL-Driver连接数据库

1. 依赖引入

  1. 在Go项目的go.mod文件中,添加go-sql-driver依赖:

    require (
    	github.com/go-sql-driver/mysql v1.8.1
    )
  2. .go文件中导入数据库驱动的依赖:

    import (
    	"github.com/go-sql-driver/mysql"
    )

2. 连接池初始化

.go文件的main方法中初始化连接池并配置以下参数,示例如下:

Addr	// 创建一个数据库连接
    cfg := mysql.NewConfig()
    cfg.User = "****"  //您需手动替换为实际数据库用户名
    cfg.Passwd = "****"  //您需手动替换为实际数据库密码
    cfg.Net = "tcp"  //连接类型为TCP,保持默认,无需您手动修改
    cfg.Addr = "rm-2zefwjx1s8156******.mysql.rds.aliyuncs.com:3306"  //您需手动替换为实际MySQL数据库连接地址和端口号
    cfg.DBName = "****"  //您需手动替换为实际数据库名称

    conn, err := mysql.NewConnector(cfg)
    if err != nil {
	panic(err.Error())
    }

    db := sql.OpenDB(conn)
    defer db.Close()

    // 设置连接池参数
    db.SetMaxOpenConns(20)   // 设置连接池中最大打开的连接数, 您可根据实际需要手动调整
    db.SetMaxIdleConns(2)    // 设置连接池中最大空闲的连接数, 您可根据实际需要手动调整
    db.SetConnMaxIdleTime(10 * time.Second)   // 连接在连接池中的最长的空闲时间, 您可根据实际需要手动调整
    db.SetConnMaxLifetime(80 * time.Second)   // 设置连接的最大生命周期, 您可根据实际需要手动调整
说明

RDS MySQL数据库连接地址和端口的获取方法,请参见查看和管理实例连接地址和端口

附录:连接池关键参数配置

重要
  • 推荐配置“推荐配置的参数”模块,降低数据库运行风险;您可以选择性配置“可选择配置的参数模块”,用以提升使用性能。

  • 为了最大程度地避免潜在的风险和不确定性,您在将新的参数值用于生产环境前,建议至少进行一轮完整的功能测试和性能测试,以确保系统稳定性和可靠性。

推荐配置的参数

参数名

含义

默认值

推荐值

说明

maxIdleTime

设置最大空闲时间(单位:分钟)。

0

10~30

  • 默认值0表示不超时。

  • 建议根据应用程序的实际需求配置该参数。可通过SetConnMaxIdleTime方法进行设置。

maxLifetime

设置连接的生命周期时间(单位:小时)。

0

1~8

  • 默认值0表示永久存在,即没有设置最大生命周期。

  • 设置该参数是为防止连接长时间存在,浪费资源。可通过SetConnMaxLifetime方法进行设置。

maxOpen

设置连接池中允许的最大连接数。

0

100

  • 默认值0表示没有限制。

  • 如果MaxIdleConns大于0且新的MaxOpenConns小于MaxIdleConns,则MaxIdleConns将被缩减以匹配新的MaxOpenConns限制。

  • 建议根据应用程序的实际需求配置该参数。可通过SetMaxOpenConns方法进行设置。

maxIdleCount

设置连接池中允许的最大空闲连接数。

2

20

  • 适当配置该参数可以预留一定数量的连接,快速处理突发增加的数据库请求。

  • 如果MaxOpenConns大于0但小于新的MaxIdleConns,则新的MaxIdleConns将被缩减以匹配MaxOpenConns的限制。

  • 可通过SetMaxIdleConns方法进行设置。

  • 如果设置传入的参数n <= 0,则不会保留空闲连接。

readTimeout

I/O读取的超时时间,您可按需指定为毫秒(ms)、秒(s)或分钟(m)。

0

10000ms-60000ms

  • 默认值0表示没有超时限制。建议保持默认值,如果要修改此值,请根据实际业务调整。

  • 该值必须是一个带有单位后缀的小数,例如“30s”表示30秒,“0.5m”表示0.5分钟。

writeTimeout

I/O写入的超时时间,您可按需指定为毫秒(ms)、秒(s)或分钟(m)。

0

10000ms-60000ms

  • 默认值0表示没有超时限制。建议保持默认值,如果要修改此值,请根据实际业务调整。

  • 该值必须是一个带有单位后缀的小数,例如“30s”表示30秒,“0.5m”表示0.5分钟。

可选择配置的参数

参数名

含义

默认值

推荐值

说明

timeout

连接建立的超时时间,您可按需指定为毫秒(ms)、秒(s)或分钟(m)。

默认值是根据操作系统的默认值来设定。

3000ms

  • 该值必须是一个带有单位后缀的小数,例如“30s”表示30秒,“0.5m”表示0.5分钟。

  • 创建时的配置,建议设置1~10秒之间,取决于网络质量高低,以及应用端与服务端的距离。

操作数据库

创建表

本文以创建一个名为 userinfo 的表为例演示。

_, err = db.Exec("create table  if not exists userinfo( uid int auto_increment, username varchar(20) not null default '', departname varchar(20) not null default '', created varchar(10) not null default '', primary key(uid) );")
if err != nil {
    fmt.Println("create table error ", err)
    return
}

写入数据

本文以在 userinfo 表中写入数据为例演示。

方法一:直接写入数据

result, err := db.Exec("INSERT INTO userinfo (username, departname, created) VALUES (?, ?, ?)", "Linda", "Testing", "2016-06-21")
checkErr(err)
ids, err := result.LastInsertId()
fmt.Println(ids)

方法二:通过绑定参数的方式写入数据

stmt, err := db.Prepare("INSERT INTO userinfo SET username=?,departname=?,created=?")
res, err := stmt.Exec("James", "Research", "2016-06-17")
id, err := res.LastInsertId()
checkErr(err)
fmt.Println(id)

更新数据

本文以更新userinfo 表中的数据为例演示。

stmt, err = db.Prepare("UPDATE userinfo SET username=?, departname=?, created=? WHERE uid=?")
if err != nil {
    // 如果准备语句时出现错误,则进行错误处理
}
res, err = stmt.Exec("Robert", "Sales", "2024-09-23", 2)
if err != nil {
    fmt.Println(err)
}
rowCnt, _ := res.RowsAffected()
fmt.Println(rowCnt)

查询数据

本文以查询 userinfo 表中username=Linda的数据为例演示。

方法一:直接查询数据

rows, err := db.Query("SELECT username,departname,created FROM userinfo WHERE username=?", "Linda")
for rows.Next() {
	var username, departname, created string
	if err := rows.Scan(&username, &departname, &created); err != nil {
		fmt.Println(err)
	}
	fmt.Println("username:", username, "departname:", departname, "created:", created)
}

方法二:通过绑定参数的方式执行参数化查询

stmt, err = db.Prepare("SELECT username,departname,created FROM userinfo WHERE username=?")
if err != nil {
	fmt.Println("prepare error", err)
	return
}
rows, err = stmt.Query("Linda")
if err != nil {
	fmt.Println("query data error", err)
	return
}
defer rows.Close()
for rows.Next() {
	var username, departname, created string
	if err := rows.Scan(&username, &departname, &created); err != nil {
		fmt.Println(err)
	}
	fmt.Println("username:", username, "departname:", departname, "created:", created)
}

删除数据

本文以删除userinfo 表中uid=1的数据为例演示。

del_stmt, _ := db.Prepare("DELETE FROM userinfo WHERE uid=?")
del_stmt.Exec(1)

完整示例

package main

import (
    "database/sql"
    "fmt"
    "github.com/go-sql-driver/mysql"
    "time"
)

func main() {
    //创建一个数据库连接
    cfg := mysql.NewConfig()
    cfg.User = "****"
    cfg.Passwd = "****"
    cfg.Net = "tcp"
    cfg.Addr = "rm-2zefwjx1s8156******.mysql.rds.aliyuncs.com:3306"
    cfg.DBName = "****"

    conn, err := mysql.NewConnector(cfg)
    if err != nil {
       panic(err.Error())
    }

    db := sql.OpenDB(conn)
    defer db.Close()

    // 设置连接池参数
    db.SetMaxOpenConns(20)                 // 设置连接池中最大打开的连接数
    db.SetMaxIdleConns(2)                  // 设置连接池中最大空闲的连接数
    db.SetConnMaxIdleTime(5 * time.Minute) // 设置连接在连接池中的最长的空闲时间
    db.SetConnMaxLifetime(8 * time.Minute) // 设置连接的最大生命周期
    checkErr(err)

    _, err = db.Exec("create table  if not exists userinfo( uid int auto_increment, username varchar(20) not null default '', departname varchar(20) not null default '', created varchar(10) not null default '', primary key(uid) );")
    if err != nil {
       fmt.Println("create table error ", err)
       return
    }

    stmt, err := db.Prepare("INSERT userinfo SET username=?,departname=?,created=?")
    res, err := stmt.Exec("zhja", "研发", "2016-06-17")
    id, err := res.LastInsertId()
    checkErr(err)
    fmt.Println(id)

    result, err := db.Exec("INSERT INTO userinfo (username, departname, created) VALUES (?, ?, ?)", "lily", "销售", "2016-06-21")
    checkErr(err)
    ids, err := result.LastInsertId()
    fmt.Println(ids)

    del_stmt, _ := db.Prepare("DELETE FROM userinfo WHERE uid=?")
    del_stmt.Exec(1)

    stmt, err = db.Prepare("UPDATE userinfo SET username=?, departname=?, created=? WHERE uid=?")
    if err != nil {
       // 处理错误
    }
    res, err = stmt.Exec("lisi", "测试", "2024-09-23", 2)
    if err != nil {
       fmt.Println(err)
    }
    rowCnt, _ := res.RowsAffected()
    fmt.Println(rowCnt)

    rows, err := db.Query("SELECT username,departname,created FROM userinfo WHERE username=?", "lisi")
    for rows.Next() {
       var username, departname, created string
       if err := rows.Scan(&username, &departname, &created); err != nil {
          fmt.Println(err)
       }
       fmt.Println("username:", username, "departname:", departname, "created:", created)
    }

    stmt, err = db.Prepare("SELECT username,departname,created FROM userinfo WHERE username=?")
    if err != nil {
       fmt.Println("prepare error", err)
       return
    }
    rows, err = stmt.Query("lisi")
    if err != nil {
       fmt.Println("query data error", err)
       return
    }
    defer rows.Close()
    for rows.Next() {
       var username, departname, created string
       if err := rows.Scan(&username, &departname, &created); err != nil {
          fmt.Println(err)
       }
       fmt.Println("username:", username, "departname:", departname, "created:", created)
    }

}

func checkErr(err error) {
    if err != nil {
       panic(err)
    }
}

相关文档