アプリケーションで Go プログラミング言語を使用しており、ApsaraDB RDS for MySQL インスタンスへの接続を頻繁に確立する必要がある場合 (短期間の接続など)、またはアプリケーションと RDS for MySQL インスタンス間の接続数が制限を超えている場合は、Go-MySQL-Driver を使用して RDS インスタンスに接続できます。 これにより、アプリケーションが RDS インスタンスに接続する頻度を減らし、RDS インスタンスのメインスレッドのオーバーヘッドを削減できます。 このトピックでは、Go-MySQL-Driver を使用して RDS インスタンスに接続する方法と、接続された RDS インスタンスのデータベースで操作を実行する方法について説明します。
Go-MySQL-Driver は、Go が提供するサードパーティのデータベースドライバパッケージです。 Go-MySQL-Driver を使用して、MySQL および MariaDB データベースに接続できます。
制限事項
Go-MySQL-Driver を使用して接続できるのは、MySQL 5.7 または 8.0 を実行する RDS インスタンスのみです。
準備
Go 1.20 以降がインストールされています。 詳細については、「ダウンロードとインストール」をご参照ください。
クライアントの IP アドレスが RDS インスタンスの IP アドレスホワイトリストに追加されています。 詳細については、「IP ホワイトリストを構成する」をご参照ください。
説明RDS インスタンスと同じリージョンおよび VPC (仮想プライベートクラウド) にある ECS (Elastic Compute Service) インスタンスにアプリケーションがデプロイされている場合は、IP アドレスホワイトリストを構成する必要はありません。
Go-MySQL-Driver を使用して RDS インスタンスに接続する
1. 依存関係を追加する
go-sql-driver
依存関係を Go プロジェクトのgo.mod
ファイルに追加します。require ( github.com/go-sql-driver/mysql v1.8.1 )
必要なパッケージを
.go
ファイルにインポートします。import ( // 一般的な SQL インターフェースを提供する database/sql パッケージをインポートします。 "database/sql" // フォーマットされた入出力機能を提供する fmt パッケージをインポートします。 "fmt" // RDS for MySQL のデータベースに接続して管理するために、Go-MySQL-Driver をインポートします。 "github.com/go-sql-driver/mysql" // 時間関連の操作を処理するために、time パッケージをインポートします。 "time" )
2. 接続プールを初期化する
.go
ファイルの main メソッドを使用して、接続プールを初期化し、関連パラメータを構成します。 サンプルコード:
// データベース接続を確立します。
cfg := mysql.NewConfig()
cfg.User = "****" // RDS インスタンスへのログインに使用するアカウントのユーザー名で値を置き換えます。
cfg.Passwd = "****" // アカウントのパスワードで値を置き換えます。
cfg.Net = "tcp" // 接続タイプはデフォルトで TCP です。 値を変更する必要はありません。
cfg.Addr = "rm-2zefwjx1s8156******.mysql.rds.aliyuncs.com:3306" // RDS インスタンスのエンドポイントとポート番号で値を置き換えます。
cfg.DBName = "****" // RDS インスタンスの名前で値を置き換えます。
conn, err := mysql.NewConnector(cfg)
if err != nil {
panic(err.Error())
}
db := sql.OpenDB(conn)
defer func(db *sql.DB) {
err := db.Close()
if err != nil {
fmt.Printf("データベース接続を閉じるときにエラーが発生しました: %v\n", err)
}
}(db)
// 接続プールに関連するパラメータを構成します。
db.SetMaxOpenConns(20) // 接続プール内のオープン接続の最大数を指定します。 ビジネス要件に基づいて値を変更できます。
db.SetMaxIdleConns(2) // 接続プール内のアイドル接続の最大数を指定します。 ビジネス要件に基づいて値を変更できます。
db.SetConnMaxIdleTime(10 * time.Second) // 接続プール内の接続がアイドル状態を維持できる最大期間を指定します。 ビジネス要件に基づいて値を変更できます。
db.SetConnMaxLifetime(80 * time.Second) // 接続を再利用できる最大期間を指定します。 ビジネス要件に基づいて値を変更できます。
RDS インスタンスのエンドポイントとポート番号を表示する方法の詳細については、「インスタンスのエンドポイントとポートを表示および管理する」をご参照ください。
付録: 接続プールの主要パラメータ
データベースのランタイムリスクを軽減するために、推奨構成 の表のパラメータを構成することをお勧めします。 また、データベースのパフォーマンスを向上させるために、オプションの構成 の表のパラメータを構成することもできます。
潜在的なリスクと不確実性を最小限に抑え、システムの安定性と信頼性を確保するために、本番環境に新しいパラメータ値を適用する前に、完全な機能テストとパフォーマンステストを実行することをお勧めします。
推奨構成
パラメータ | 説明 | デフォルト値 | 推奨値 | 備考 |
maxIdleTime | 接続の最大アイドル期間。 単位: 分。 | 0 | 10~30 |
|
maxLifetime | 接続を再利用できる最大期間。 単位: 時間。 | 0 | 1~8 |
|
maxOpen | 接続プール内の接続の最大数。 | 0 | 100 |
|
maxIdleCount | 接続プール内のアイドル接続の最大数。 | 2 | 20 |
|
readTimeout | I/O 読み取りのタイムアウト期間。 ビジネス要件に基づいて、ミリ秒 (ms)、秒 (s)、または分 (m) で値を指定できます。 | 0 | 10000ms~60000ms |
|
writeTimeout | I/O 書き込みのタイムアウト期間。 ビジネス要件に基づいて、ミリ秒 (ms)、秒 (s)、または分 (m) で値を指定できます。 | 0 | 10000ms~60000ms |
|
オプションの構成
パラメータ | 説明 | デフォルト値 | 推奨値 | 備考 |
timeout | 接続を確立するためのタイムアウト期間。 ビジネス要件に基づいて、ミリ秒 (ms)、秒 (s)、または分 (m) で値を指定できます。 | デフォルト値は、オペレーティングシステムのデフォルト値によって異なります。 | 3000ms |
|
データベースで操作を実行する
テーブルを作成する
この例では、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("テーブルの作成エラー ", err)
return
}
テーブルにデータを書き込む
この例では、userinfo
テーブルにデータが書き込まれます。
方法 1: テーブルに直接データを書き込む
stmt, err := db.Prepare("INSERT userinfo SET username=?,departname=?,created=?")
res, err := stmt.Exec("James", "Research", "2016-06-17")
id, err := res.LastInsertId()
if err != nil {
panic(err)
}
fmt.Println(id)
方法 2: パラメータ化クエリを実行してテーブルにデータを書き込む
result, err := db.Exec("INSERT INTO userinfo (username, departname, created) VALUES (?, ?, ?)", "Linda", "Testing", "2016-06-21")
if err != nil {
panic(err)
}
ids, err := result.LastInsertId()
fmt.Println(ids)
データを更新する
この例では、userinfo
テーブルのデータが更新されます。
stmtUpdate, err := db.Prepare("UPDATE userinfo SET username=?, departname=?, created=? WHERE username=?")
if err != nil {
fmt.Println("更新ステートメントの準備エラー:", err)
return
}
resUpdate, err := stmtUpdate.Exec("Robert", "Sales", "2024-09-23", "Linda")
if err != nil {
fmt.Println(err)
}
rowCnt, _ := resUpdate.RowsAffected()
fmt.Println(rowCnt)
データをクエリする
この例では、userinfo
テーブルから、username フィールドの値が Robert
であるデータレコードがクエリされます。
方法 1: 直接データをクエリする
rows, err := db.Query("SELECT username,departname,created FROM userinfo WHERE username=?", "Robert")
if err != nil {
panic(err)
}
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)
}
defer func(rows *sql.Rows) {
err := rows.Close()
if err != nil {
fmt.Println(err)
}
}(rows)
方法 2: パラメータ化クエリを実行してデータをクエリする
stmtQuery, err := db.Prepare("SELECT username,departname,created FROM userinfo WHERE username=?")
if err != nil {
fmt.Println("準備エラー", err)
return
}
rowData, err := stmtQuery.Query("Robert")
if err != nil {
fmt.Println("データのクエリエラー", err)
return
}
for rowData.Next() {
var username, departname, created string
if err := rowData.Scan(&username, &departname, &created); err != nil {
fmt.Println(err)
}
fmt.Println("username:", username, "departname:", departname, "created:", created)
}
defer func(rows *sql.Rows) {
err := rows.Close()
if err != nil {
fmt.Println(err)
}
}(rowData)
データを削除する
この例では、userinfo
テーブルから、username フィールドの値が James
であるデータレコードが削除されます。
delStmt, _ := db.Prepare("DELETE FROM userinfo WHERE username=?")
resultDel, err := delStmt.Exec("James")
if err != nil {
panic(err)
}
rowAffect, _ := resultDel.RowsAffected()
fmt.Println("データの削除が完了しました。", rowAffect)
例
package main
import (
// 一般的な SQL インターフェースを提供する database/sql パッケージをインポートします。
"database/sql"
// フォーマットされた入出力機能を提供する fmt パッケージをインポートします。
"fmt"
// RDS for MySQL のデータベースに接続して管理するために、Go-MySQL-Driver をインポートします。
"github.com/go-sql-driver/mysql"
// 時間関連の操作を処理するために time パッケージをインポートします。
"time"
)
func main() {
// データベース接続を確立します。
cfg := mysql.NewConfig()
cfg.User = "****" /*RDS インスタンスにログインするために使用するアカウントのユーザー名で値を置き換えます。*/
cfg.Passwd = "****" /*アカウントのパスワードで値を置き換えます。*/
cfg.Net = "tcp" /*接続タイプはデフォルトで TCP です。値を変更する必要はありません。*/
cfg.Addr = "rm-2ze1vw17v542q6b****.mysql.pre.rds.aliyuncs.com:3306" /*RDS インスタンスのエンドポイントとポート番号で値を置き換えます。*/
cfg.DBName = "****" /*データベースの名前で値を置き換えます。*/
cfg.Timeout = 3 * time.Second /*データベースへの接続を確立するためのタイムアウト期間を指定します。ビジネス要件に基づいて、ミリ秒 (ms)、秒 (s)、または分 (m) で値を指定できます。*/
cfg.ReadTimeout = 60 * time.Second /*I/O 読み取りのタイムアウト期間。ビジネス要件に基づいて、ミリ秒 (ms)、秒 (s)、または分 (m) で値を指定できます。*/
cfg.WriteTimeout = 60 * time.Second /*I/O 書き込みのタイムアウト期間。ビジネス要件に基づいて、ミリ秒 (ms)、秒 (s)、または分 (m) で値を指定できます。*/
conn, err := mysql.NewConnector(cfg)
if err != nil {
panic(err.Error())
}
db := sql.OpenDB(conn)
defer func(db *sql.DB) {
err := db.Close()
if err != nil {
fmt.Printf("データベース接続を閉じるときにエラーが発生しました: %v\n", err)
}
}(db)
// 接続プールに関連するパラメータを構成します。
db.SetMaxOpenConns(100) /*接続プール内のオープン接続の最大数を指定します。*/
db.SetMaxIdleConns(20) /*接続プール内のアイドル接続の最大数を指定します。*/
db.SetConnMaxIdleTime(10 * time.Minute) /*接続プール内の接続がアイドル状態を維持できる最大期間を指定します。*/
db.SetConnMaxLifetime(8 * time.Hour) /*接続を再利用できる最大期間を指定します。*/
// 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("テーブルの作成エラー ", err)
return
}
// テーブルに直接データを書き込みます。
stmt, err := db.Prepare("INSERT userinfo SET username=?,departname=?,created=?")
res, err := stmt.Exec("James", "Research", "2016-06-17")
id, err := res.LastInsertId()
if err != nil {
panic(err)
}
fmt.Println(id)
// パラメータ化クエリを実行してテーブルにデータを書き込みます。
result, err := db.Exec("INSERT INTO userinfo (username, departname, created) VALUES (?, ?, ?)", "Linda", "Testing", "2016-06-21")
if err != nil {
panic(err)
}
ids, err := result.LastInsertId()
fmt.Println(ids)
// テーブルのデータを更新します。
stmtUpdate, err := db.Prepare("UPDATE userinfo SET username=?, departname=?, created=? WHERE username=?")
if err != nil {
fmt.Println("更新ステートメントの準備エラー:", err)
return
}
resUpdate, err := stmtUpdate.Exec("Robert", "Sales", "2024-09-23", "Linda")
if err != nil {
fmt.Println(err)
}
rowCnt, _ := resUpdate.RowsAffected()
fmt.Println(rowCnt)
// テーブルにデータを直接クエリします。
rows, err := db.Query("SELECT username,departname,created FROM userinfo WHERE username=?", "Robert")
if err != nil {
panic(err)
}
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)
}
defer func(rows *sql.Rows) {
err := rows.Close()
if err != nil {
fmt.Println(err)
}
}(rows)
// パラメータ化クエリを実行してテーブルにデータをクエリします。
stmtQuery, err := db.Prepare("SELECT username,departname,created FROM userinfo WHERE username=?")
if err != nil {
fmt.Println("準備エラー", err)
return
}
rowData, err := stmtQuery.Query("Robert")
if err != nil {
fmt.Println("データのクエリエラー", err)
return
}
for rowData.Next() {
var username, departname, created string
if err := rowData.Scan(&username, &departname, &created); err != nil {
fmt.Println(err)
}
fmt.Println("username:", username, "departname:", departname, " created:", created)
}
defer func(rows *sql.Rows) {
err := rows.Close()
if err != nil {
fmt.Println(err)
}
}(rowData)
// テーブルからデータを削除します。
delStmt, _ := db.Prepare("DELETE FROM userinfo WHERE username=?")
resultDel, err := delStmt.Exec("James")
if err != nil {
panic(err)
}
rowAffect, _ := resultDel.RowsAffected()
fmt.Println("データの削除が完了しました。", rowAffect)
}