このトピックでは、Go が提供する database/sql パッケージと SQL 文を使用して LindormTable ベースのアプリケーションを開発する方法について説明し、例を示します。
前提条件
Go がインストールされていること。Go 1.17 以降をインストールすることを推奨します。詳細については、「Go」をご参照ください。
インスタンスの MySQL 互換機能が有効になっていること。詳細については、「MySQL 互換機能を有効にする」をご参照ください。
クライアントの IP アドレスが Lindorm インスタンスのホワイトリストに追加されていること。詳細については、「ホワイトリストの設定」をご参照ください。
考慮事項
Lindorm SQL フロントエンドアクセスノードは、Server Load Balancer (SLB) を使用してクライアントリクエストをさまざまなフロントエンドノードに分散します。クライアントリクエストがすべてのフロントエンドノードに均等に分散されるようにするため、
長時間の接続キープアライブ時間を設定しないことを推奨します。SetConnMaxLifetime パラメーターを設定して、定期的に接続をリフレッシュできます。
複雑なネットワーク環境で、ゲートウェイのパフォーマンスがボトルネックに達した場合、ネットワークリンクが長い場合、またはネットワークジッター、再送率、パケット損失率が高い場合、接続が中断されることがあります。適切な接続プール構成を確保し、必要に応じてビジネスコードにリトライメカニズムを実装することを推奨します。
サーバーがスペックアップされ再起動されると、接続が一時的に中断されることがあります。接続プールを使用していても、ビジネスで例外が検出される可能性があります。例外をキャッチしてリトライを実装することを推奨します。
必要に応じて接続プールの構成を調整し、構成が有効になることを確認してください。接続プール内の接続数がビジネスニーズを満たしていることを確認し、接続不足による接続待機を回避してください。これにより、応答時間 (RT) が増加する可能性があります。接続プールのステータス (
fmt.Printf("%+v\n", db.Stats())) を出力することで問題を診断できます。
手順
Go プロジェクトの
go.modファイルに、Go MySQL Driver の依存関係を追加します。require github.com/go-sql-driver/mysql v1.7.1接続パラメーターを設定します。
const ( user = "user" password = "test" host = "ld-uf6k8yqb741t3****-proxy-sql-lindorm.lindorm.rds.aliyuncs.com" port = 33060 database = "default" connectTimeout = "10s" )パラメーター
パラメーター
説明
user
パスワードを忘れた場合は、LindormTable のクラスター管理システムでパスワードを変更できます。詳細については、「ユーザーの管理」をご参照ください。
password
host
LindormTable Endpoint For MySQL。エンドポイントの取得方法の詳細については、「エンドポイントの表示」をご参照ください。
重要アプリケーションが ECS インスタンスにデプロイされている場合は、virtual private cloud (VPC) を使用して Lindorm インスタンスに接続し、より高いセキュリティと低いネットワーク遅延を確保することを推奨します。
アプリケーションがローカルサーバーにデプロイされており、インターネット経由で Lindorm インスタンスに接続する必要がある場合は、次の手順を実行して Lindorm コンソールでインスタンスのインターネットエンドポイントを有効にできます。コンソールで、 を選択し、[ワイドテーブルエンジン] タブで [パブリックエンドポイントを有効にする] をクリックします。
VPC 経由で Lindorm インスタンスにアクセスする場合は、host パラメーターを MySQL 互換の [VPC] アドレスに設定します。インターネット経由で Lindorm インスタンスにアクセスする場合は、host パラメーターを MySQL 互換の [インターネット] アドレスに設定します。
port
MySQL を使用して LindormTable にアクセスするためのポート。このパラメーターの値は 33060 に固定されています。
database
接続するデータベースの名前。デフォルトでは、クライアントは default という名前のデータベースに接続されます。
connectTimeout
データベース接続のタイムアウト期間。単位: 秒 (s)。
接続を確立し、LindormTable SQL を使用して LindormTable で操作を実行します。次のコードブロックは、LindormTable SQL を使用してすべてのデータベースをクエリする方法の例を示しています。
url := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?timeout=%s", user, password, host, port, database, connectTimeout) db, err := sql.Open("mysql", url) if err != nil { panic(err.Error()) } // 確立できる接続の最大数を指定します。デフォルト値は 0 で、接続の最大数に制限がないことを示します。接続不足による接続待機を避けるために、必要に応じてこれを設定します。 db.SetMaxOpenConns(20) // アイドル接続の最大数を指定します。デフォルト値は 2 です。SetMaxIdleConns を SetMaxOpenConns と同じ値に設定することを推奨します。 db.SetMaxIdleConns(20) // 接続の最大アイドル期間を指定します。デフォルト値は 0 で、接続がタイムアウトしないことを示します。これを 8 分に設定することを推奨します。 db.SetConnMaxIdleTime(8 * time.Minute) // 持続的接続の長期使用による接続の不均衡を避けるために、接続の最大有効期間を指定します。これを 30 分に設定することを推奨します。 db.SetConnMaxLifetime(30 * time.Minute) defer db.Close() // すべてのデータベースをクエリ { rows, err := db.Query("show databases") if err != nil { panic(err.Error()) } defer rows.Close() for rows.Next() { var dbName string err := rows.Scan(&dbName) if err != nil { panic(err.Error()) } fmt.Println(dbName) } }
サンプルコード
完全なサンプルコードは次のとおりです。
package main
import (
"database/sql"
"fmt"
"time"
_ "github.com/go-sql-driver/mysql"
)
const (
// user を LindormTable へのアクセスに使用するユーザー名に設定します
user = "user"
// password を LindormTable へのアクセスに使用するパスワードに設定します
password = "test"
// host を MySQL 用の LindormTable エンドポイントに設定します
host = "ld-uf6k8yqb741t3****-proxy-sql-lindorm-public.lindorm.rds.aliyuncs.com"
// MySQL を使用して LindormTable にアクセスするためのポートを指定します。ポート番号は 33060 に固定されています
port = 33060
// database を接続したいデータベースの名前に設定します
database = "default"
// データベース接続のタイムアウト期間を指定します
connectTimeout = "20s"
)
func main() {
// データベース接続を確立します
url := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?timeout=%s", user, password, host, port, database, connectTimeout)
db, err := sql.Open("mysql", url)
if err != nil {
panic(err.Error())
}
// 確立できる接続の最大数を指定します。デフォルト値は 0 で、接続の最大数に制限がないことを示します。接続不足による接続待機を避けるために、必要に応じてこれを設定します。
db.SetMaxOpenConns(20)
// アイドル接続の最大数を指定します。デフォルト値は 2 です。SetMaxIdleConns を SetMaxOpenConns と同じ値に設定することを推奨します。
db.SetMaxIdleConns(20)
// 接続の最大アイドル期間を指定します。デフォルト値は 0 で、接続がタイムアウトしないことを示します。これを 8 分に設定することを推奨します。
db.SetConnMaxIdleTime(8 * time.Minute)
// 持続的接続の長期使用による接続の不均衡を避けるために、接続の最大有効期間を指定します。これを 30 分に設定することを推奨します。
db.SetConnMaxLifetime(30 * time.Minute)
defer db.Close()
// 接続が十分にあるか、待機中の接続があるか、いくつの接続が閉じられたかを診断するために、接続プールのステータスを定期的に出力します
go func() {
for {
fmt.Printf("%+v\n", db.Stats())
time.Sleep(10 * time.Second)
}
}()
// すべてのデータベースをクエリします
{
rows, err := db.Query("show databases")
if err != nil {
panic(err.Error())
}
defer rows.Close()
for rows.Next() {
var dbName string
err := rows.Scan(&dbName)
if err != nil {
panic(err.Error())
}
fmt.Println(dbName)
}
}
// テーブルを作成します
{
_, err := db.Exec("create table if not exists user_test(id int, name varchar,age int, primary key(id))")
if err != nil {
fmt.Println("create table error ", err)
return
}
}
// テーブルにデータを書き込みます
// 方法 1: テーブルに直接データを書き込みます
{
_, err = db.Exec("upsert into user_test(id,name,age) values(1,'zhangsan',17)")
if err != nil {
fmt.Println("insert data error", err)
return
}
}
// 方法 2: パラメーターを指定してテーブルにデータを書き込みます
{
stmt, err := db.Prepare("upsert into user_test(id,name,age) values(?,?,?)")
if err != nil {
fmt.Println("prepare error", err)
return
}
defer stmt.Close()
_, err = stmt.Exec(2, "lisi", 18)
if err != nil {
fmt.Println("upsert error", err)
return
}
}
// テーブル内のデータをクエリします
// 方法 1: データを直接クエリします
{
rows, err := db.Query("select * from user_test")
if err != nil {
fmt.Println("query data error", err)
return
}
defer rows.Close()
var id int
var name string
var age int
for rows.Next() {
err = rows.Scan(&id, &name, &age)
if err != nil {
fmt.Println("scan data error", err)
return
}
fmt.Println("id:", id, "name:", name, "age:", age)
}
}
// 方法 2: パラメーターを指定してパラメーター化クエリを実行します
{
stmt, err := db.Prepare("select * from user_test where id=?")
if err != nil {
fmt.Println("prepare error", err)
return
}
defer stmt.Close()
rows, err := stmt.Query(1)
if err != nil {
fmt.Println("query data error", err)
return
}
defer rows.Close()
var id int
var name string
var age int
for rows.Next() {
err = rows.Scan(&id, &name, &age)
if err != nil {
fmt.Println("scan data error", err)
return
}
fmt.Println("id:", id, "name:", name, "age:", age)
}
}
// テーブルからデータを削除します
{
_, err = db.Exec("delete from user_test where id=1")
if err != nil {
fmt.Println("delete data error", err)
return
}
}
}使用後に Close を呼び出して行を閉じる必要があります。
パフォーマンス専有型シナリオでは、Stmt を再利用できます。
QueryContext や ExecContext などの Context 関連のインターフェイスを使用する場合、タイムアウト期間が短すぎると、クライアントのガベージコレクション (GC) ジッターによって Context タイムアウトが発生し、接続がリセットされ、システムパフォーマンスに影響を与える可能性があります。
現在のインスタンスに default という名前のデータベースのみが含まれている場合、次の結果が返されます。
default
information_schema
id: 1 name: zhangsan age: 17
id: 2 name: lisi age: 18
id: 1 name: zhangsan age: 17