Skip to content

golang Database programing

foolmacky edited this page Sep 11, 2021 · 10 revisions

■database/sqlパッケージの特性とpooling

  • database/sqlパッケージにはpooling機能が実装されていて、意識しなくても利用される
  • なので1つのデータベース上で、2つの連続したステートメントを実行すると、2つのコネクションをオープンし、別々に実行する可能性がある。
  • コネクションに制限はないが、アイドル状態のコネクションを制御することは可能。長い時間アイドル状態になり、エラーが発生する場合はdb.SetMaxIdleConns(0)を試してみる。
  • データベースのコネクションを開いたり閉じたりすると、リソース枯渇の原因となる可能性がある。
  • Failing to read all rows or user rows.Close() reserves connections from the pool.
  • Using Query() for a statement that doesn't return rows will reserve a connection from the pool.
  • Failing to be aware of how prepared statements work can lead to a lot of extra database activity.
  • 単一の接続で実行されていると思っていても、実際には複数の接続で実行されている場合がある。
  • コネクションを変更すると、そのコネクションはpoolに戻り、他のコードの状態に悪影響を及ぼす可能性がある。
  • begin, commitを直接発行してはいけない。
  • コネクションでエラーが発生すると、Goは新しいコネクションのオープンを最大10回リトライする。
  • 大きなUnit64で上位ビットが設定されている場合、ステートメントのパラメータとして渡せない。uint64を使う場合、最初は小さな数値でエラーが発生しないかもしれないが、時間が立つにつれて数値が増えていくと、エラーが発生する。
  • どうしてもNULLがリターンされる場合は、NULL許可のbool, string, int, floatの型を使う。
  • database/sqlでのほぼすべての操作では、最後の返り値としてエラーが返却される。それらのエラーをチェックし、無視しない。
  • 各Databaseの特定のエラーは、定義されているエラーの番号の定数を使って判定する。

■参考になるページ

■Goのドライバ

  • Goのドライバは、単一のクエリによる複数の結果セットをサポートしていないので、複数の結果セットを返却するようなストアドプロシージャはうまく動作しない。
  • 明示的には複数ステートメントサポートをしていない。
DELETE FROM tbl1; DELETE FROM tbl2
  • Mysqlドライバでは、ストアドが実行できない。MySQLがコネクションが複数ステートメントモードに設定されることを期待するため。
  • 通常はドライバーパッケージを直接使わないで、database/sqlで定義された型のみを使う。そうすることで、特定のドライバに依存しないようになる。

■DBについて

  • 直感に反しているかもしれないが、sql.Open()はデータベースへの接続を確立しないし、ドライバーの接続パラメータの検証もしない。後で使用されるabstructされたデータベースの準備をするだけ。
  • 実際のデータベースへのコネクションは、必要になったときに初めて、遅延して確立される。
  • データベースが利用可能でアクセス可能かどうか、すぐに確認したい場合は、sql.Ping()を使う。
  • sql.DBオブジェクトは長期間使われるように設計されているので、Opne()とClose()を頻繁には行わない。
  • それぞれのデータストアごとに単一のsql.DBオブジェクトを作り、必要に応じてそのsql.DBを引き回すか、あるいはグローバルに利用可能にしたり、関数に引数として渡す。

■Prepared Statement

  • prepared statementのときの動き ** SQL文をプリペアすると、pool内のコネクション上でprepare ** Stmtオブジェクトはどのコネクションが使われたかを記憶 ** そのStmtを実行すると、そのコネクションをしようとするが、もしもクローズされていたり他の処理をしていて使用できない場合は、プールから他のコネクションを取得し、データベースの他のコネクションで再度SQL文をプリペア ** もとのコネクションがビジー状態のとき、SQL文は必要に応じて再度prepareされるため、大量のprepared Stmtを生成し、ステートメントのリークに結びつく可能性がある。
  • もしprepared statementを使いたくない場合は、fmt.Sprint()などを使ってSQLを組み立て、そのSQLをdb.Query(), db.QueryRow()へ唯一の引数として渡す必要があるが、これはドライバのサポートに依存している。
  • Txで作成されたprepared statementはそのTx専用に紐付けられているため、Txオブジェクトの操作をするとき、アクションはTxとそれに紐付けられた単一のコネクションに直接マッピングされる。
  • Tx外部でprepareされたstatementをTx内で使うには、Tx.Stmt()を使うことができるが、実装が良くないので基本使わない。
  • prepared statementでのプレースホルダーパラメータの構文は、データベースにより異なる。

■更新系とトランザクションについて

  • Goでは基本的にトランザクションはデータストアへのコネクションを予約するオブジェクト
  • Begin()やCommit()等のトランザクション関連の関数を使い、begin, commitのようなSQL文を組み合わせて使わない。実際に使うと予期しない結果が起こる。

Clone this wiki locally