手っ取り早く結論
- Laravel で明示的に Write コネクションを指定する方法はざっくり 3 つある
- Eloquent Model で
protected $connection
を指定する -
onWriteConnection()
を使う - トランザクションを切る
- Eloquent Model で
前提となる背景
- Laravel1 でデータベースのコネクション設定を Read/Write 構成にすると、参照系のクエリは暗黙的に Read コネクションを使う
- 参照系のクエリで Write コネクションを利用したいときがある
- Read コネクションがリードレプリカに向いていて、書き込み遅延がある
- 更新直後に異なるリクエストによる参照が発生することがあり、書き込み遅延が間に合わず最新のレコードを取得できない
- 更新直後に最新のレコードを取得したいため、一部の参照系で Write コネクションを利用したい
根本的には設計のミスだと思っています。
しかし一旦、この設計は変えられない前提で、小手先のテクニックでなんとかする方法を紹介します。
確認バージョン
- Laravel 5.5
- Laravel 6.x
データベース設定
このような設定のとき、参照系のクエリのコネクションに DB_HOST_WRITE
を使いたい。
'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()
を使う - トランザクションを切る
- Eloquent Model で
参考
- Laravel Read/Write Connection - Specify Explicitly - Stack Overflow
- Laravel の DB のマスター/スレーブでマスターで SELECT したりスレーブでトランザクションしたり - Qiita
- Database: Getting Started - Laravel - The PHP Framework For Web Artisans