Closed4

プリペアードステートメントについてさっくり整理

tamaco489tamaco489

概要

  • SQL文を「構文解析(パース)」した状態で一度サーバーに送信し、その後は値のバインドだけで繰り返し実行できる仕組み。

  • ざっくりな流れ:

    1. DBを利用するクライアントから、まず変数を埋め込み可能な形式のSQLを発行。
    2. DB側でそのSQLを受け取り、解析した上でキャッシュに乗せる。
    3. クライアントからは変数のみを送ってSQLを実行する。
  • 利点:

    • 同じSQLを何度も実行する場合、実行計画にキャッシュさせておくことでDB効率よくなる。
    • クエリをパラメータを分離させることで、SQLインジェクション対策にもなる。
  • 使用例 (goの場合):

    stmt = db.prepare("INSERT INTO users (name, age) VALUES (?, ?)")
    stmt.execute("Alice", 25)
    
tamaco489tamaco489

補足事項

  • webアプリケーションでは発行するクエリの種類が多く、作成したプリペアードステートメントのキャッシュが効率よく利用されないケースが多い。
  • 結果として、SQLを発行する度にプリペアードステートメントを準備する、 PREPARE クエリと、作成したステートメントを開放する CLOSE のクエリが必要になり、通信回数が増えることで効率が低下してしまいがち。
  • Goの場合は、go-sql-driver/mysql ではプリペアードステートメントはデフォルトで有効になってるため、無効化したい場合は明示的に指定する必要がある。
tamaco489tamaco489
func NewDatabaseConnection() (*sql.DB, error) {
	c := mysqldriver.Config{
		User:                 cfg.Get().DB.User,
		Passwd:               cfg.Get().DB.Pass,
		Addr:                 fmt.Sprintf("%s:%s", cfg.Get().DB.Host, cfg.Get().DB.Port),
		DBName:               cfg.Get().DB.Name,
		ParseTime:            true,
		Net:                  "tcp",
		AllowNativePasswords: true,
		Params: map[string]string{
			"interpolateParams": "true", // ここで設定
		},
	}

	db, err := sql.Open("mysql", c.FormatDSN())
	if err != nil {
		return nil, err
	}
	return db, nil
}
このスクラップは4ヶ月前にクローズされました