標準パッケージ 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 の抽象化もエグいので、いつか使えるようになりたい!
