33
27

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Laravel で Read/Write 構成のデータベースに対して明示的に Write コネクションを指定する方法

Last updated at Posted at 2020-02-26

手っ取り早く結論

  • Laravel で明示的に Write コネクションを指定する方法はざっくり 3 つある
    • Eloquent Model で protected $connection を指定する
    • onWriteConnection() を使う
    • トランザクションを切る

前提となる背景

  • Laravel1 でデータベースのコネクション設定を Read/Write 構成にすると、参照系のクエリは暗黙的に Read コネクションを使う
  • 参照系のクエリで Write コネクションを利用したいときがある
    • Read コネクションがリードレプリカに向いていて、書き込み遅延がある
    • 更新直後に異なるリクエストによる参照が発生することがあり、書き込み遅延が間に合わず最新のレコードを取得できない
    • 更新直後に最新のレコードを取得したいため、一部の参照系で Write コネクションを利用したい

根本的には設計のミスだと思っています。
しかし一旦、この設計は変えられない前提で、小手先のテクニックでなんとかする方法を紹介します。

確認バージョン

  • Laravel 5.5
  • Laravel 6.x

データベース設定

このような設定のとき、参照系のクエリのコネクションに DB_HOST_WRITE を使いたい。

config/database.php
'mysql' => [
    'driver'    => 'mysql',
    'read' => [
        'host' => env('DB_HOST_READ', env('DB_HOST', 'localhost')),
    ],
    'write' => [
        'host' => env('DB_HOST_WRITE', env('DB_HOST', 'localhost')),
    ],

protected $connection

Eloquent Model の protected $connection'コネクション名::write' を入れましょう。
特定のテーブルや Eloquent Model で常に Write を参照したい場合に有効です。

class User extends Model implements AuthenticatableContract, AuthorizableContract
{
    use Authenticatable, Authorizable;

    protected $connection = 'mysql::write';

このプロパティがどういう働きをするのかは、このあたりのコードを読むと理解できます。
https://github.com/laravel/framework/blob/5.5/src/Illuminate/Database/DatabaseManager.php#L87

Pros
常に最新のレコードを取得できる
Cons
Write コネクションへの負荷が増える

総評

  • 社内管理画面など、一部のユーザーが利用するような低トラフィックなアプリケーション&セッションストアにデータベースを採用している場合に有効だと思います
  • 高トラフィックで常に最新のレコードを取りたい、という場合には性能的な厳しさに直面すると思います
    • 更新時に Redis にキャッシュするなど、他の高速なデータストアの利用を検討したほうが良さそう

onWriteConnection()

Eloquent Model の特定クエリで Write を参照したいときに便利です。

$user = User::onWriteConnection()->first();

Pros
Write コネクションの負荷を最小限に抑えられる
特定のユースケースで最新のレコードを取得できる
Cons
onWriteConnection() を書くべきところで書き忘れるとバグる
遅延によるのでたまにしか再現しなかったりする

総評

  • 小手先テクニックとしては個人的にオススメです
  • 書き忘れが怖いなら protected $connection を検討しましょう
  • このやり方がツラいときは設計から見直したほうが良いでしょう

トランザクションを切る

Laravel はトランザクションを切ると暗黙的に Write コネクションを利用します。
これはトランザクション本来の用途ではありません2が、1リクエスト中の複数の参照クエリで読み取り一貫性をもたせたいという要求もセットで叶えるならこれです。
Laravel でトランザクションの切り方はドキュメントを見てください。

Pros
1リクエスト中の複数の参照クエリで読み取り一貫性をもたせられる(※)
特定のユースケースで最新のレコードを取得できる
Cons
暗黙的に Write コネクションを利用しているため、知らない人はコードを読んでも分からない
トランザクションを切っている理由をコメントに書いてあげると親切ですね

(※)トランザクション中の実際の読み取り一貫性はデータベースエンジンやその設定によって左右されます。設定がどうなっているか、実際にトランザクションを切ってどういう振る舞いをするかは確認してください。

総評

  • 複数の参照クエリで読み取り一貫性を持たせたい場合はこれしかないです
  • 単に Write コネクションを利用したいだけなら onWriteConnection()protected $connection を最初に検討しましょう

まとめ

  • Laravel で明示的に Write コネクションを指定する方法はざっくり 3 つある
    • Eloquent Model で protected $connection を指定する
    • onWriteConnection() を使う
    • トランザクションを切る

参考

  1. Lumen でも同様

  2. Laravel が明示的なトランザクションで Write コネクションを使うようにしているだけ

33
27
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
33
27

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?