標準パッケージ sql は一般的な SQL のインタフェースを提供しています。
使用する際は各ベンダーの用意するドライバーと共に使うことが必要で、諸々の使い方は go の wiki にも書いてあります。
今回はこの sql パッケージで注意が必要な点(と自分が今認識してる範囲)についてメモしておきます。
他にも気をつけるべき点があればぜひ教えてください。
[目次]
## 扱わないこと ## 登場人物 ## sql パッケージ利用時に気をつけたい点 ### sql.Open はコネクションを確立しない ### コネクションを確立するには ### コネクションプールの制御 ## (おまけ)db 側からコネクション数確認(postgres 編) ## おわりに
扱わないこと
以下内容については今回は扱いません。
- sqlboiler や gorm といった ORM がどのような扱いをしているのかについて
登場人物
- sql.DB
- コネクションプールを管理する親玉
- sql.driverConn
- sql.DB の freeConn が持っている
- sql/driver.Conn
- driverConn の ci として持っているインタフェース
- 各ドライバーが実装
- sql.DB が、コネクションプールの管理をゴルーチンセーフにしてくれているため、このコネクションはその辺の考慮不要
sql パッケージ利用時に気をつけたい
sql.Open はコネクションを確立しない
wiki の Connecting to a database にも書いてありますが、sql.Open では sql.DB の初期化を行うのみで実際のコネクションは確立しません。
そのため、ポートが間違っていたり db が起動してなかったりしてもエラーになりません。
以下のように、db.Stats()
で取得できる sql.DBStats でコネクションに関する情報が取得できます。
package main import ( "database/sql" "fmt" "log" _ "github.com/lib/pq" ) func main() { source := fmt.Sprintf( "host=%s port=%s user=%s password=%s dbname=%s sslmode=%s", "localhost", "4646", "ubuntu", "ubuntu", "testdb", "disable", ) db, err := sql.Open("postgres", source) if err != nil { log.Fatal(err) } stats := db.Stats() // 0: アイドル状態のコネクション数。 fmt.Printf("stats.Idle: %v\t", stats.Idle) // 0: アクティブ状態のコネクション数。 fmt.Printf("stats.InUse: %v\n", stats.InUse) }
この辺の記載は 最近発売された『Go言語 100Tips』にも記載されてますので、気になる方はぜひ読んでみることをお勧めします。
Go言語 100Tips ありがちなミスを把握し、実装を最適化する (impress top gear) 新品価格 |
コネクションを確立するには
ではどのようにコネクションを確立すれば良いかというと、sql.DB に対し db.Exec や db.Query を発行することで勝手にコネクションが貼られます。 ユーザーが意識することはありません。
特に Ping のコメントに
// PingContext verifies a connection to the database is still alive, // establishing a connection if necessary.
と丁寧にあるように、Ping でもコネクションが貼られることが分かります。
func checkDBStatus(db *sql.DB) { stats := db.Stats() fmt.Printf("stats.Idle: %v\t", stats.Idle) fmt.Printf("stats.InUse: %v\n", stats.InUse) } func main() { source := fmt.Sprintf( "host=%s port=%s user=%s password=%s dbname=%s sslmode=%s", "localhost", "4646", "ubuntu", "ubuntu", "testdb", "disable", ) db, err := sql.Open("postgres", source) if err != nil { log.Fatal(err) } // sql.Open は実際にはコネクションをオープンしない!! // stats.Idle: 0 stats.InUse: 0 checkDBStatus(db) defer func() { if closeErr := db.Close(); err != nil { log.Print(closeErr) } }() if err := db.Ping(); err != nil { log.Fatal(err) } // Ping 等で、必要になった場合にコネクションをオープンする。 // stats.Idle: 1 stats.InUse: 0 checkDBStatus(db) }
コネクションプールの制御
sql.DB に生えているメソッドを通して『コネクションプールの最大数』や『コネクションがアイドルになってから切断するまでの時間』の設定等が可能です。 デフォルトではどちらも 0 に設定されており、これは無制限を意味するため注意が必要です。
func main() { source := fmt.Sprintf( "host=%s port=%s user=%s password=%s dbname=%s sslmode=%s", "localhost", "4646", "ubuntu", "ubuntu", "testdb", "disable", ) db, err := sql.Open("postgres", source) if err != nil { log.Fatal(err) } // オープンするコネクションの最大数を設定する。 // デフォルトでは 0 で無制限!! db.SetMaxOpenConns(1) // 1 時間アイドル状態が続いたコネクションは閉じる! // デフォルトでは 0 で無制限!! db.SetConnMaxIdleTime(1 * time.Hour) }
(おまけ)db 側からコネクション数確認(postgres 編)
pg_stat_activity
テーブルを確認することで、現在のコネクション情報が取得できるらしいです。
SELECT pid, usename, application_name, state, query_start FROM pg_stat_activity;
testdb=# \d pg_stat_activity; View "pg_catalog.pg_stat_activity" Column | Type | Collation | Nullable | Default ------------------+--------------------------+-----------+----------+--------- datid | oid | | | datname | name | | | pid | integer | | | leader_pid | integer | | | usesysid | oid | | | usename | name | | | application_name | text | | | client_addr | inet | | | client_hostname | text | | | client_port | integer | | | backend_start | timestamp with time zone | | | xact_start | timestamp with time zone | | | query_start | timestamp with time zone | | | state_change | timestamp with time zone | | | wait_event_type | text | | | wait_event | text | | | state | text | | | backend_xid | xid | | | backend_xmin | xid | | | query_id | bigint | | | query | text | | | backend_type | text | | | testdb=# SELECT pid, usename, application_name, state, query_start FROM pg_stat_activity; pid | usename | application_name | state | query_start -----+---------+------------------+--------+------------------------------- 28 | ubuntu | | | 27 | | | | 36 | ubuntu | psql | active | 2023-09-16 15:40:27.098589+00 24 | | | | 23 | | | | 26 | | | | (6 rows)
おわりに
net.Conn
の抽象化もエグいので、いつか使えるようになりたい!