SlideShare ist ein Scribd-Unternehmen logo
1 von 157
Downloaden Sie, um offline zu lesen
Laravelを用いた
ゲームサーバーのチューニング
TECH×GAME COLLEGE#21 2019/06/05
ナウプロダクション
本日の流れ
序章
はじめに
Laravelとは
第一章
Laravelで
100msec以下を
目指すには
高速なアプリケーションを組むために実施した工夫を紹介しま
す
第二章
Laravelで水平分
散するには
水平分散の実装を行うにあたり、
どのような方針で機能提供を行い、
どのような課題があって、解決してきたのかを紹介します
1
序章
はじめに
2
Laravelとは
1. PHPのフレームワーク
2. MITライセンス
3. Symfonyベース
4. 今最も勢いがある
3
序章 はじめに
Laravelは今最も勢いがある
Laravelは2016年11月頃からトレンドトップに
未だ衰える事を知らない
4
序章 はじめに Laravelの紹介
Laravelの強み
1. 自由度の高さ
2. DI機能の強さ
3. 洗練されたコード群
5
序章 はじめに Laravelの紹介
Laravelの強み
1. 自由度の高さ
2. DI機能の強さ
3. 洗練されたコード群
6
序章 はじめに Laravelの紹介
Laravelの強み
1. 自由度の高さ
2. DI機能の強さ
3. 洗練されたコード群
7
序章 はじめに Laravelの紹介
Laravelの強み
1. 自由度の高さ
2. DI機能の強さ
3. 洗練されたコード群
8
序章 はじめに Laravelの紹介
Laravelの強み
簡単に書けて
なんでもできる
9
序章 はじめに Laravelの紹介
Laravelの誤解
10
序章 はじめに Laravelの紹介
Laravelはフルスタック
↓
レールに則って開発すればOK
↓
レール通りやったけど遅い!
Laravelの誤解
11
序章 はじめに Laravelの紹介
class FlightController extends Controller
{
/**
* 新しいflightインスタンスの生成
*
* @param Request $request
* @return Response
*/
public function store(Request $request)
{
// リクエストのバリデート処理…
$flight = new Flight;
$flight->name = $request->name;
$flight->save();
}
}
Laravel公式ドキュメント
saveメソッドの解説より抜粋
単純なCRUDを管理するCMSっぽ
い書き方
複雑な処理とIOを扱うゲームで
このような書き方をすると
クエリ回数が増える
ゲームでORMと上手くつきあう
ためのレールは用意されていない
Laravelの誤解
12
序章 はじめに Laravelの紹介
レールに則って開発すればOK
レールは自分で考える必要がある
Laravelとは
13
序章 はじめに Laravelの紹介
自由で
書きやすい分
自分でレールを考えていく必要がある
第1章
Laravelで通信時間100msec以内を
達成するための工夫
14
第1章の流れ
1.「通信時間
100msec以内」の
定義
タイトルのキーワードについて
環境要因を含めての目線合わせを行います
2.Laravelの起動処
理を早くする
Laravelの初期起動に関わるServiceProviderについて
「遅延ServiceProvider」を用いた最適化を、事例も交えて紹介します
3.DBへのアクセス
回数を最小にする
Eloquentクエリビルダーの呼び出し回数≒DBアクセス回数
この問題に対応するためにRepositoryパターンを導入しました。
社内FWでも実装されている、具体的な機能を紹介します
15
第1章 Laravelで通信時間100msec以内を達成するための工夫
第1章の流れ
1.「通信時間
100msec以内」の
定義
タイトルのキーワードについて
環境要因を含めての目線合わせを行います
2.Laravelの起動処
理を早くする
Laravelの初期起動に関わるServiceProviderについて
「遅延ServiceProvider」を用いた最適化を、事例も交えて紹介します
3.DBへのアクセス
回数を最小にする
Eloquentクエリビルダーの呼び出し回数≒DBアクセス回数
この問題に対応するためにRepositoryパターンを導入しました。
社内FWでも実装されている、具体的な機能を紹介します
16
第1章 Laravelで通信時間100msec以内を達成するための工夫
第1章の流れ
1.「通信時間
100msec以内」の
定義
タイトルのキーワードについて
環境要因を含めての目線合わせを行います
2.Laravelの起動処
理を早くする
Laravelの初期起動に関わるServiceProviderについて
「遅延ServiceProvider」を用いた最適化を、事例も交えて紹介します
3.DBへのアクセス
回数を最小にする
Eloquentクエリビルダーの呼び出し回数≒DBアクセス回数
この問題に対応するためにRepositoryパターンを導入しました。
社内FWでも実装されている、具体的な機能を紹介します
17
第1章 Laravelで通信時間100msec以内を達成するための工夫
第1章の流れ
1.「通信時間
100msec以内」の
定義
タイトルのキーワードについて
環境要因を含めての目線合わせを行います
2.Laravelの起動処
理を早くする
Laravelの初期起動に関わるServiceProviderについて
「遅延ServiceProvider」を用いた最適化を、事例も交えて紹介します
3.DBへのアクセス
回数を最小にする
Eloquentクエリビルダーの呼び出し回数≒DBアクセス回数
この問題に対応するためにRepositoryパターンを導入しました。
社内FWでも実装されている、具体的な機能を紹介します
18
第1章 Laravelで通信時間100msec以内を達成するための工夫
第1章
1.「通信時間100msec以内」の定義
19
そもそも100msec以内を目指す理由
ユーザー体験に影響を与える
目安とされている数値
操作に対する応答が100msec以内
だとサクサク感じるとされる
20
第1章 Laravelで通信時間100msec以内を達成するための工夫 1.「通信時間100msec以内」の定義
「通信時間100msec」の定義
本番で100msecはテストし辛いので
「以下の条件で50msec以内」と定義する
1. AWSのオールインワン環境
2. 性能は本番APIサーバに準拠
3. 単一のリクエストを処理させる
4. リクエストが届いてから
レスポンスを返し終わるまで
21
第1章 Laravelで通信時間100msec以内を達成するための工夫 1.「通信時間100msec以内」の定義
「通信時間100msec」の定義
インフラ要件次第では
これでも本番で100msec切れない事
はある
マルチAZ構成だとかなり厳しい
22
第1章 Laravelで通信時間100msec以内を達成するための工夫 1.「通信時間100msec以内」の定義
第1章
2.Laravelの起動処理を早くする
23
DIとServiceProvider
1. DIとは「クラス間・およびオブジェクト間の
依存解決を外側から」する仕組みの事
2. ServiceProviderはこの「外側からの依存解
決」をするためのクラス
24
第1章 Laravelで通信時間100msec以内を達成するための工夫 2.Laravelの起動処理を早くする
DIとServiceProvider
1. DIとは「クラス間・およびオブジェクト間の
依存解決を外側から」する仕組みの事
2. ServiceProviderはこの「外側からの依存解
決」をするためのクラス
25
第1章 Laravelで通信時間100msec以内を達成するための工夫 2.Laravelの起動処理を早くする
DIとServiceProvider
1. DIとは「クラス間・およびオブジェクト間の
依存解決を外側から」する仕組みの事
2. ServiceProviderはこの「外側からの依存解
決」をするためのクラス
26
第1章 Laravelで通信時間100msec以内を達成するための工夫 2.Laravelの起動処理を早くする
様々なクラスKernelの動作順
DIのイメージ
27
ServiceProvider
Container
Controller
ClassA ClassBprovide
依存注入を行う
ClassAはClassBを利
用している
make
ClassAのオブジェクト
が欲しい
ServiceProviderによって
定義された依存関係
第1章 Laravelで通信時間100msec以内を達成するための工夫 2.Laravelの起動処理を早くする
様々なクラスKernelの動作順
DIのイメージ
28
ServiceProvider
Container
Controller
ClassA ClassBprovide
依存注入を行う
ClassAはClassBを利
用している ServiceProviderによって
定義された依存関係
第1章 Laravelで通信時間100msec以内を達成するための工夫 2.Laravelの起動処理を早くする
make
ClassAのオブジェクト
が欲しい
様々なクラスKernelの動作順
DIのイメージ
29
ServiceProvider
Container
Controller
ClassA ClassB
make
ClassAのオブジェクト
が欲しい
第1章 Laravelで通信時間100msec以内を達成するための工夫 2.Laravelの起動処理を早くする
ServiceProviderによって
定義された依存関係
provide
依存注入を行う
ClassAはClassBを利
用している
Newからの拡張点
new呼び出し~オブジェクト生成の間
に独自ルールを仕込める
30
第1章 Laravelで通信時間100msec以内を達成するための工夫 2.Laravelの起動処理を早くする
DIの強み
FWなど、外から提供されている
コードに手を加えやすい
31
第1章 Laravelで通信時間100msec以内を達成するための工夫 2.Laravelの起動処理を早くする
様々なクラスKernelの動作順
コードに手を加えやすい
32
Container
Controller
ClassA ClassB
make
ClassAのオブジェクト
が欲しい
第1章 Laravelで通信時間100msec以内を達成するための工夫 2.Laravelの起動処理を早くする
ClassB`
ServiceProvider
provide
依存注入を行う
ClassAはClassBの代わ
りに、ClassB`を使う
ServiceProviderによって
定義された依存関係
DIの強み
他にも
テストコード書く時にモックしやすい
複雑な依存関係をシンプルに記述できる
などメリット沢山
33
第1章 Laravelで通信時間100msec以内を達成するための工夫 2.Laravelの起動処理を早くする
DIを利用するのは基本
様々なクラスKernelの動作順
増え続けるServiceProviderの責務
34
ServiceProvider
Container
Controller
ClassA ClassB
provide
依存注入を行う
ClassA⇒ClassB
ClassB⇒ClassC
ClassB⇒ClassD
Class1⇒Class2
…
make
Class1のオブジェクトが
欲しいだけ
ClassC ClassD
依存
Class1 Class2
依存
第1章 Laravelで通信時間100msec以内を達成するための工夫 2.Laravelの起動処理を早くする
様々なクラスKernelの動作順
増え続けるServiceProviderの責務
35
Container
Controller
make
Class1のオブジェクトが
欲しいだけ
依存
Class1 Class2
第1章 Laravelで通信時間100msec以内を達成するための工夫 2.Laravelの起動処理を早くする
ServiceProvider
provide
依存注入を行う
ClassA⇒ClassB
ClassB⇒ClassC
ClassB⇒ClassD
Class1⇒Class2
…
ClassA ClassB
ClassC ClassD
依存
遅延サービスプロバイダ
起動条件を満たすまで、
そもそも起動すらしないサービスプロバイダ
36
第1章 Laravelで通信時間100msec以内を達成するための工夫 2.Laravelの起動処理を早くする
様々なクラスKernelの動作順
遅延ServiceProviderは遅れて実行される
37
Container
Controller ClassA ClassB
make
Class1のオブジェクトが
欲しいだけ
ClassC ClassD
依存
Class1 Class2
依存
Class1のオブジェクト解決
したいので
ServiceProvider1を起動
第1章 Laravelで通信時間100msec以内を達成するための工夫 2.Laravelの起動処理を早くする
ServiceProviderA
ServiceProvider1
Class1⇒Class2
ClassA⇒ClassB
ClassB⇒ClassC
ClassB⇒ClassD
様々なクラスKernelの動作順
遅延ServiceProviderは遅れて実行される
38
Container
Controller ClassA ClassB
make
Class1のオブジェクトが
欲しいだけ
ClassC ClassD
依存
Class1 Class2
依存
Class1のオブジェクト解決
したいので
ServiceProvider1を起動
第1章 Laravelで通信時間100msec以内を達成するための工夫 2.Laravelの起動処理を早くする
ServiceProviderA
ServiceProvider1
Class1⇒Class2
ClassA⇒ClassB
ClassB⇒ClassC
ClassB⇒ClassD
遅延サービスプロバイダ導入の効果
39
126ファイル改善
オールインワン環境で6msec改善
改善後
合計542ファイル
改善前
合計668ファイル
第1章 Laravelで通信時間100msec以内を達成するための工夫 2.Laravelの起動処理を早くする
対応してきたこと
3.DBへのアクセス回数を最小にする
40
Eloquentとは
1. DatabaseアクセスのORM
2. 1テーブルあたり1クラス
3. クエリビルダーとしての振る舞いとレコード
(Entity)としての振る舞いを持つ
41
第1章 Laravelで通信時間100msec以内を達成するための工夫 3.DBへのアクセス回数を最小にする
Eloquentとは
1. クエリビルダーとしての振る舞い
⇒MySQLのクエリ文を作成する
2. レコード(Entity)としての振る舞い
⇒結果セットはEloquentオブジェクトのCollection
42
第1章 Laravelで通信時間100msec以内を達成するための工夫 3.DBへのアクセス回数を最小にする
Entityについて補足
1. ドメイン駆動開発の「ドメイン層Entity」
とは異なる
2. 本講演でのEntityは1テーブルの1レコードを
表現する「インフラストラクチャ層Entity」
3. これからのお話はインフラストラクチャ層の
最適化にあたる
43
第1章 Laravelで通信時間100msec以内を達成するための工夫 3.DBへのアクセス回数を最小にする
クエリビルダーの危険な使い方
1. ループ中で使う
2. 使う場所を限定しない
3. コントローラー直書き
44
第1章 Laravelで通信時間100msec以内を達成するための工夫 3.DBへのアクセス回数を最小にする
Repositoryの導入
Eloquentのクエリビルダーとしての振る舞いを
アプリケーション層に露出させない役割
つまり
クエリ結果のキャッシュ
45
第1章 Laravelで通信時間100msec以内を達成するための工夫 3.DBへのアクセス回数を最小にする
Repositoryの導入
ただし、
CMSからの複雑な条件での問い合わせや、
batch処理などは
クエリビルダーの直接使用を認める
46
第1章 Laravelで通信時間100msec以内を達成するための工夫 3.DBへのアクセス回数を最小にする
Repositoryに求める役割
47
1. selectを最小限にする
2. コミット直前にまとめてupdateする
第1章 Laravelで通信時間100msec以内を達成するための工夫 3.DBへのアクセス回数を最小にする
Repositoryに求める役割
48
1. selectを最小限にする
2. コミット直前にまとめてupdateする
第1章 Laravelで通信時間100msec以内を達成するための工夫 3.DBへのアクセス回数を最小にする
selectを最小限にする
49
DB
Repository
プロパティ
id user_id card_id
1 1 101
2 1 201
3 1 301
4 1 401
5 1 501
6 2 101
7 2 201
8 2 301
9 2 401
10 3 101
EntityCollection
id user_id card_id
1 1 101
2 1 201
3 1 301
4 1 401
5 1 501
関数
fetch()
get/find/count()
DBから特定の範囲の
Entity一覧を取得
アプリケーションが求
めるEntity一覧を返す
第1章 Laravelで通信時間100msec以内を達成するための工夫 3.DBへのアクセス回数を最小にする
selectを最小限にする
50
DB
Repository
プロパティ
id user_id card_id
1 1 101
2 1 201
3 1 301
4 1 401
5 1 501
6 2 101
7 2 201
8 2 301
9 2 401
10 3 101
EntityCollection
id user_id card_id
1 1 101
2 1 201
3 1 301
4 1 401
5 1 501
関数
get/find/count()
アプリケーションが求
めるEntity一覧を返す
第1章 Laravelで通信時間100msec以内を達成するための工夫 3.DBへのアクセス回数を最小にする
fetch()
DBから特定の範囲の
Entity一覧を取得
selectを最小限にする
51
DB
Repository
プロパティ
id user_id card_id
1 1 101
2 1 201
3 1 301
4 1 401
5 1 501
6 2 101
7 2 201
8 2 301
9 2 401
10 3 101
関数
fetch()
get/find/count()
DBから特定の範囲の
Entity一覧を取得
アプリケーションが求
めるEntity一覧を返す
第1章 Laravelで通信時間100msec以内を達成するための工夫 3.DBへのアクセス回数を最小にする
EntityCollection
id user_id card_id
1 1 101
2 1 201
3 1 301
4 1 401
5 1 501
selectを最小限にする
52
DB
Repository
プロパティ
id user_id card_id
1 1 101
2 1 201
3 1 301
4 1 401
5 1 501
6 2 101
7 2 201
8 2 301
9 2 401
10 3 101
EntityCollection
id user_id card_id
1 1 101
2 1 201
3 1 301
4 1 401
5 1 501
関数
get/find/count()
アプリケーションが求
めるEntity一覧を返す
第1章 Laravelで通信時間100msec以内を達成するための工夫 3.DBへのアクセス回数を最小にする
fetch()
DBから特定の範囲の
Entity一覧を取得
selectを最小限にする
53
DB
Repository
プロパティ
id user_id card_id
1 1 101
2 1 201
3 1 301
4 1 401
5 1 501
6 2 101
7 2 201
8 2 301
9 2 401
10 3 101
EntityCollection
id user_id card_id
1 1 101
2 1 201
3 1 301
4 1 401
5 1 501
関数
fetch()
DBから特定の範囲の
Entity一覧を取得
第1章 Laravelで通信時間100msec以内を達成するための工夫 3.DBへのアクセス回数を最小にする
get/find/count()
アプリケーションが求
めるEntity一覧を返す
selectを最小限にする
54
DB
Repository
プロパティ
id user_id card_id
1 1 101
2 1 201
3 1 301
4 1 401
5 1 501
6 2 101
7 2 201
8 2 301
9 2 401
10 3 101
関数
fetch()
get/find/count()
DBから特定の範囲の
Entity一覧を取得
アプリケーションが求
めるEntity一覧を返す
第1章 Laravelで通信時間100msec以内を達成するための工夫 3.DBへのアクセス回数を最小にする
EntityCollection
id user_id card_id
1 1 101
2 1 201
3 1 301
4 1 401
5 1 501
インデックス機能付きなので、
card_idなどの条件で一度検索する
と、二度目からはO(1)になる
初回アクセス時の
インデックス生成のコストはO(n)
selectを最小限にする
55
DB
Repository
プロパティ
id user_id card_id
1 1 101
2 1 201
3 1 301
4 1 401
5 1 501
6 2 101
7 2 201
8 2 301
9 2 401
10 3 101
11 1 601
EntityCollection
id user_id card_id
1 1 101
2 1 201
3 1 301
4 1 401
5 1 501
11 1 601
関数
create($attributes)
multiCreate($values)
アプリケーションが配
列形式でinsertしたい
データを指定する
第1章 Laravelで通信時間100msec以内を達成するための工夫 3.DBへのアクセス回数を最小にする
DBとEntityCollectionに
反映
selectを最小限にする
56
DB
Repository
プロパティ
id user_id card_id
1 1 101
2 1 201
3 1 301
4 1 401
5 1 501
6 2 101
7 2 201
8 2 301
9 2 401
10 3 101
EntityCollection
id user_id card_id
1 1 101
2 1 201
3 1 301
4 1 401
5 1 501
関数
delete($entity)
multiDelete($entities)
アプリケーションが
deleteしたいデータを
指定する
DBとEntityCollectionに
反映
第1章 Laravelで通信時間100msec以内を達成するための工夫 3.DBへのアクセス回数を最小にする
Repositoryに求める役割
57
1. selectを最小限にする
2. コミット直前にまとめてupdateする
第1章 Laravelで通信時間100msec以内を達成するための工夫 3.DBへのアクセス回数を最小にする
コミット直前にまとめてupdateする
1. コミット直前にまとめてupdate
2. 変更はRepositoryにため込んでおく
3. 変更されたentityの一覧からbulkupdate?を
発行
58
第1章 Laravelで通信時間100msec以内を達成するための工夫 3.DBへのアクセス回数を最小にする
Bulkupdateなんて
MySQLに存在しない・・・
結局1行ずつupdateするしか
ないのか・・・?
59
第1章 Laravelで通信時間100msec以内を達成するための工夫 3.DBへのアクセス回数を最小にする
ELT文とFIELD文を組み合わせると
それっぽい事できますよ!
60
第1章 Laravelで通信時間100msec以内を達成するための工夫 3.DBへのアクセス回数を最小にする
Bulkupdate文の例
UPDATE `user_cards` SET
is_locked = ELT(FIELD(id,2,4,5),1,0,1)
WHERE id IN (2,4,5)
プレースホルダーに置き換えると
UPDATE `user_cards` SET
is_locked = ELT(FIELD(id,?,?,?),?,?,?)
WHERE id IN (?,?,?)
61
第1章 Laravelで通信時間100msec以内を達成するための工夫 3.DBへのアクセス回数を最小にする
updateが反映されるまでDBと不整合
62
DB
Repository
プロパティ
id user
_id
card_id Is_locke
d
1 1 101 0
2 1 201 0
3 1 301 0
4 1 401 0
5 1 501 0
6 2 101 0
7 2 201 0
8 2 301 0
9 2 401 0
10 3 101 0
EntityCollection
id user
_id
card_id Is_locke
d
1 1 101 1
2 1 201 1
3 1 301 0
4 1 401 0
5 1 501 0
関数
save($entity)
Entityの永続化を宣言する
実際に永続化されるのはト
ランザクションコミット直前
第1章 Laravelで通信時間100msec以内を達成するための工夫 3.DBへのアクセス回数を最小にする
updateが反映されるまでDBと不整合
1. コミット直前以外にも、クエリを投げる直前
もトリガーとする
2. 基本的にfetchしてからsaveする流れなので、
EntityCollectionの管理さえしっかりしてい
ればあまりこういった問題は起こらない。
63
第1章 Laravelで通信時間100msec以内を達成するための工夫 3.DBへのアクセス回数を最小にする
第1章
Laravelで通信時間100msec以内を
達成するための工夫
まとめ
64
Laravelで速度を出すには
65
DIとEloquentの利用方法(レール)
を決めるのが急務
そこが安定すれば早い!
第1章 Laravelで通信時間100msec以内を達成するための工夫 まとめ
第2章
Laravelで水平分散への対応
66
第2章の流れ
1. Laravelで水
平分散する際
の困難さ
「Laravelで水平分散する」というテーマが困難さを持っているのは
Eloquent実装上の大前提と、水平分散の相性が悪いためです。
この後の議論のために困難なポイントを整理します
2.困難を解決
するための実
装方針
その困難さに対抗するための実装方針を定めます
その後、「分散DBへのクエリを、各シングルDBへのクエリへと変換する」工程をどのように実装するかを説明します
3.その他課題
と解決
水平分散で発生する以下の課題に対し、
上記で定めた実装方針を前提に、どのように対応してきたか紹介します。
1.idの一意性担保
2.XAトランザクション・トランザクション発行
3.batchにおける分散並列処理
4.migration
67
第2章 Laravelで水平分散への対応
第2章の流れ
1. Laravelで水
平分散する際
の困難さ
「Laravelで水平分散する」というテーマが困難さを持っているのは
Eloquent実装上の大前提と、水平分散の相性が悪いためです。
この後の議論のために困難なポイントを整理します
2.困難を解決
するための実
装方針
その困難さに対抗するための実装方針を定めます
その後、「分散DBへのクエリを、各シングルDBへのクエリへと変換する」工程をどのように実装するかを説明します
3.その他課題
と解決
水平分散で発生する以下の課題に対し、
上記で定めた実装方針を前提に、どのように対応してきたか紹介します。
1.idの一意性担保
2.XAトランザクション・トランザクション発行
3.batchにおける分散並列処理
4.migration
68
第2章 Laravelで水平分散への対応
第2章の流れ
1. Laravelで水
平分散する際
の困難さ
「Laravelで水平分散する」というテーマが困難さを持っているのは
Eloquent実装上の大前提と、水平分散の相性が悪いためです。
この後の議論のために困難なポイントを整理します
2.困難を解決
するための実
装方針
その困難さに対抗するための実装方針を定めます
その後、「分散DBへのクエリを、各シングルDBへのクエリへと変換する」工程をどのように実装するかを説明します
3.その他課題
と解決
水平分散で発生する以下の課題に対し、
上記で定めた実装方針を前提に、どのように対応してきたか紹介します。
1.idの一意性担保
2.XAトランザクション・トランザクション発行
3.batchにおける分散並列処理
4.migration
69
第2章 Laravelで水平分散への対応
第2章の流れ
1. Laravelで水
平分散する際
の困難さ
「Laravelで水平分散する」というテーマが困難さを持っているのは
Eloquent実装上の大前提と、水平分散の相性が悪いためです。
この後の議論のために困難なポイントを整理します
2.困難を解決
するための実
装方針
その困難さに対抗するための実装方針を定めます
その後、「分散DBへのクエリを、各シングルDBへのクエリへと変換する」工程をどのように実装するかを説明します
3.その他課題
と解決
水平分散で発生する以下の課題に対し、
上記で定めた実装方針を前提に、どのように対応してきたか紹介します。
1.idの一意性担保
2.XAトランザクション・トランザクション発行
3.batchにおける分散並列処理
4.migration
70
第2章 Laravelで水平分散への対応
第2章
1. Laravelで水平分散する際の困難さ
71
Eloquentで水平分散は難しい?
1. 水平分散自体の問題
2. Eloquent特有の問題
1. 圧倒的に足りない情報
2. 実際に難しい
72
第2章 Laravelで水平分散への対応 1.Laravelで水平分散する際の困難さ
Eloquentで水平分散は難しい?
1. 水平分散自体の問題
2. Eloquent特有の問題
1. 圧倒的に足りない情報
2. 実際に難しい
73
第2章 Laravelで水平分散への対応 1.Laravelで水平分散する際の困難さ
Eloquentで水平分散は難しい?
1. 水平分散自体の問題
2. Eloquent特有の問題
1. 圧倒的に足りない情報
2. 実際に難しい
74
第2章 Laravelで水平分散への対応 1.Laravelで水平分散する際の困難さ
Eloquentで水平分散は難しい?
1. 水平分散自体の問題
2. Eloquent特有の問題
1. 圧倒的に足りない情報
2. 実際に難しい
75
第2章 Laravelで水平分散への対応 1.Laravelで水平分散する際の困難さ
水平分散自体はそこそこ見つかる
76
第2章 Laravelで水平分散への対応 1.Laravelで水平分散する際の困難さ
Laravelだと事例が激減
77
第2章 Laravelで水平分散への対応 1.Laravelで水平分散する際の困難さ
Eloquentで水平分散は難しい?
1. 水平分散自体の問題
2. Eloquent特有の問題
1. 圧倒的に足りない情報
2. 実際に難しい
78
第2章 Laravelで水平分散への対応 1.Laravelで水平分散する際の困難さ
Eloquentで水平分散は難しい
クエリビルダーは
1コネクション×1テーブル
79
第2章 Laravelで水平分散への対応 1.Laravelで水平分散する際の困難さ
Eloquentで水平分散は難しい
水平分散のモデルは
Nコネクション×1テーブル
80
第2章 Laravelで水平分散への対応 1.Laravelで水平分散する際の困難さ
根本的なところなので
作り直しに近い労力が
かかりそう
81
実際、LaravelのDatabaseパッケージに格納されている
多くのクラスに機能拡張が必要
第2章 Laravelで水平分散への対応 1.Laravelで水平分散する際の困難さ
第2章
2.困難を解決するための実装方針
82
実装方針
1. Nコネクションを1コネクションとし
て表現する
「仮想コネクション」の導入
既存の単一コネクションは
「物理コネクション」とする
2. 上記仮想コネクションを持つ
Eloquentビルダーを
「仮想ビルダー」として実装する
物理コネクションを持つビルダーを
「物理ビルダー」とする
83
第2章 Laravelで水平分散への対応 2.困難を解決するための実装方針
仮想・物理コネクションのイメージ
84
DB
仮想コネクション
物理コネクション(DB1) DB1
user_cards
user_items
DB2
user_cards
user_items
DB3
user_cards
user_items
物理コネクション(DB2)
物理コネクション(DB3)
仮想・物理コネクションのイメージ
85
DB
仮想コネクション
DB3物理コネクション(DB3)
物理コネクション(DB1) DB1
user_cards
user_items
DB2
user_cards
user_items
user_cards
user_items
物理コネクション(DB2)
仮想・物理コネクションのイメージ
86
DB
仮想コネクション
仮想ビルダーによって得られる事
Nコネクション×1テーブル
を表現できる
87
第2章 Laravelで水平分散への対応 2.困難を解決するための実装方針
仮想ビルダーの責任範囲は
どこまで?
物理ビルダーと同等の
Interfaceを備えるべき?
⇒クエリの発行できるべき?
(実は間違い)
88
第2章 Laravelで水平分散への対応 2.困難を解決するための実装方針
クエリの発行は
操作の分解が必要
89
第2章 Laravelで水平分散への対応 2.困難を解決するための実装方針
水平分散テーブルへのクエリ処理分解
水平分散したテーブルへのクエリは、
常に以下の変換処理が必要となる
1. 分解:対象DBの選択・分解
2. 実行:分解したDBそれぞれへ発行するクエリ作成
3. マージ:クエリ結果のマージ
この変換処理を「クエリ解決」と呼ぶ事にする
90
第2章 Laravelで水平分散への対応 2.困難を解決するための実装方針
クエリ解決の機能は
仮想ビルダーに
持たせるべき?
91
第2章 Laravelで水平分散への対応 2.困難を解決するための実装方針
仮想ビルダーにクエリ解決を持たせる場合
1. limit-offsetは無理
2. batch処理の効率化は見込めない
「分解・実行・マージ」の「実行」は並列させたい
が
クエリビルダー内で並列処理は責任範囲違反
92
第2章 Laravelで水平分散への対応 2.困難を解決するための実装方針
クエリ解決の機能は
仮想ビルダーに
持たせてはいけない
93
第2章 Laravelで水平分散への対応 2.困難を解決するための実装方針
仮想ビルダーの責任範囲は
どこまで?
↓
「物理ビルダー参照」
にのみ集中
94
第2章 Laravelで水平分散への対応 2.困難を解決するための実装方針
クエリ解決は
QueryPolicyとして
ユースケース化する
95
第2章 Laravelで水平分散への対応 2.困難を解決するための実装方針
「分解・操作・マージ」のユースケースを表現するQueryPolicy
96
QueryPolicyService
96
指定したEloquentBuilderを使って
user_id=1,2,3の所持カード一覧が欲しい
EloquentBuilder
第2章 Laravelで水平分散への対応 2.困難を解決するための実装方針
「分解・操作・マージ」のユースケースを表現するQueryPolicy
97
QueryPolicyService
97
EloquentBuilder
指定したEloquentBuilderを使って
user_id=1,2,3の所持カード一覧が欲しい
第2章 Laravelで水平分散への対応 2.困難を解決するための実装方針
「分解・操作・マージ」のユースケースを表現するQueryPolicy
98
QueryPolicyService
98
EloquentBuilder
指定したEloquentBuilderを使って
user_id=1,2,3の所持カード一覧が欲しい
user_id=1は採番1
user_id=2,3は採番2
第2章 Laravelで水平分散への対応 2.困難を解決するための実装方針
「分解・操作・マージ」のユースケースを表現するQueryPolicy
99
QueryPolicyService
99
EloquentBuilder
指定したEloquentBuilderを使って
user_id=1,2,3の所持カード一覧が欲しい
user_id=1は採番1
user_id=2,3は採番2
where user_id=1
where user_id in (2,3)
第2章 Laravelで水平分散への対応 2.困難を解決するための実装方針
「分解・操作・マージ」のユースケースを表現するQueryPolicy
100
QueryPolicyService
100
EloquentBuilder
指定したEloquentBuilderを使って
user_id=1,2,3の所持カード一覧が欲しい
user_id=1は採番1
user_id=2,3は採番2
where user_id=1
where user_id in (2,3)
各クエリの結果をマージ
第2章 Laravelで水平分散への対応 2.困難を解決するための実装方針
「分解・操作・マージ」のユースケースを表現するQueryPolicy
101
QueryPolicyService
101
EloquentBuilder
指定したEloquentBuilderを使って
user_id=1,2,3の所持カード一覧が欲しい
where user_id=1
where user_id in (2,3)
各クエリの結果をマージ
第2章 Laravelで水平分散への対応 2.困難を解決するための実装方針
user_id=1は採番1
user_id=2,3は採番2
採番サービスを利用
採番サービス
102
第2章 Laravelで水平分散への対応 2.困難を解決するための実装方針
採番番号は
user_id毎に
DBに保存する
⇒剰余に頼ると後から追加し辛い
採番サービスの機能
1. 分散キーに基づいて、採番番号を発行する
• 引数:4 (user_id)
• 戻り値:2 (採番番号)
2. 分散キー一覧に基づいて、採番毎に分割する
• 引数:[1,2,3,4](user_id[])
• 戻り値:[1=>[1], 2=>[2,3,4]] (user_id[][])
• 注意点:DBに負荷を与えないよう、memcached
など駆使する
103
第2章 Laravelで水平分散への対応 2.困難を解決するための実装方針
採番サービスの機能
1. 分散キーに基づいて、採番番号を発行する
• 引数:4 (user_id)
• 戻り値:2 (採番番号)
2. 分散キー一覧に基づいて、採番毎に分割する
• 引数:[1,2,3,4](user_id[])
• 戻り値:[1=>[1], 2=>[2,3,4]] (user_id[][])
• 注意点:DBに負荷を与えないよう、memcached
など駆使する
104
第2章 Laravelで水平分散への対応 2.困難を解決するための実装方針
採番サービスの機能
1. 分散キーに基づいて、採番番号を発行する
• 引数:4 (user_id)
• 戻り値:2 (採番番号)
2. 分散キー一覧に基づいて、採番毎に分割する
• 引数:[1,2,3,4](user_id[])
• 戻り値:[1=>[1], 2=>[2,3,4]] (user_id[][])
• 注意点:DBに負荷を与えないよう、memcached
など駆使する
105
第2章 Laravelで水平分散への対応 2.困難を解決するための実装方針
クエリ解決を行う
サービスは作った
ここで再びリポジトリに活躍
してもらう
106
第2章 Laravelで水平分散への対応 2.困難を解決するための実装方針
リポジトリ上の水平分散対応コード
protected function fetchWithUsersLocked($user_ids)
{
// 複数ユーザーからロック済みのカードを取得
// インメモリマッピング無し
return $this->selectQueryWithOwners($user_ids, function ($builder) {
return $builder
->where('is_locked', true)
->get();
});
}
第2章 Laravelで水平分散への対応 2.困難を解決するための実装方針
リポジトリ上の水平分散対応コード
protected function fetchWithUsersLocked($user_ids)
{
// 複数ユーザーからロック済みのカードを取得
// インメモリマッピング無し
return $this->selectQueryWithOwners($user_ids, function ($builder) {
return $builder
->where('is_locked', true)
->get();
});
}
第2章 Laravelで水平分散への対応 2.困難を解決するための実装方針
第2章
3.その他課題と解決
109
課題
1. idの一意性担保
2. XAトランザクション・トランザクション発行
3. batchにおける分散並列処理
4. migration
110
第2章 Laravelで水平分散への対応 3.その他課題と解決
課題
3-1.idの一意性担保
111
idの一意性は保つ
同一テーブルについては、
異なるコネクションであっても
idのバッティングはして欲しくない
112
第2章 Laravelで水平分散への対応 3-1.idの一意性担保
idの一意性は保つ
auto_increment_incrementと
auto_increment_offsetを
上手く使う
auto_increment_incrementを100
auto_increment_offsetを1~100
で構えておけば、理論上100台まで増やせる
113
第2章 Laravelで水平分散への対応 3-1.idの一意性担保
課題
3-2.XAトランザクション・トランザクション発行
114
XAトランザクションとは
分散したDBに
アトミックに
トランザクションの変更を反映させる
115
第2章 Laravelで水平分散への対応 3-2.XAトランザクション・トランザクション発行
MySQLのXAトランザクションは
ソーシャルゲームでの利用は難しい
分離レベルが「REPEATABLE-READ」または
「SERIALIZABLE」でないと利用できない
しかし使っているのは
「READ-COMMITTED」
116
第2章 Laravelで水平分散への対応 3-2.XAトランザクション・トランザクション発行
XAトランザクションしない
と、部分コミットが発生する
117
第2章 Laravelで水平分散への対応 3-2.XAトランザクション・トランザクション発行
コミットだけでなく
トランザクション発行ですら難しい
1. 1DBの頃は、beginTransaction~endTransaction
をコントローラーやService内で記述
2. 水平分散していると、ビジネスロジックの分岐に
よってどのDBが参照されるか変わってくる
3. かといって、水平分散の全DBにトランザクション
するわけにはいかない
118
第2章 Laravelで水平分散への対応 3-2.XAトランザクション・トランザクション発行
トランザクション発行問題は
物理コネクションに遅延解決させる事で解決
1. 物理コネクションは一旦トランザクションの命令を
保留して、何らかのクエリ発行の際に初めてトラン
ザクションを実施する
2. 仮想コネクションへのトランザクション発行は、愚
直に配下の物理コネクションにトランザクション発
行するよう命令する
119
第2章 Laravelで水平分散への対応 3-2.XAトランザクション・トランザクション発行
トランザクション発行問題は
物理コネクションに遅延解決させる事で解決
1. 物理コネクションは一旦トランザクションの命令を
保留して、何らかのクエリ発行の際に初めてトラン
ザクションを実施する
2. 仮想コネクションへのトランザクション発行は、愚
直に配下の物理コネクションにトランザクション発
行するよう命令する
120
第2章 Laravelで水平分散への対応 3-2.XAトランザクション・トランザクション発行
トランザクション発行問題は
物理コネクションに遅延解決させる事で解決
1. 物理コネクションは一旦トランザクションの命令を
保留して、何らかのクエリ発行の際に初めてトラン
ザクションを実施する
2. 仮想コネクションへのトランザクション発行は、愚
直に配下の物理コネクションにトランザクション発
行するよう命令する
121
第2章 Laravelで水平分散への対応 3-2.XAトランザクション・トランザクション発行
部分コミット問題は
処理の最後に自動コミットさせる事で解決
1. 水平、垂直問わず、全てのトランザクション発行済
みDBをコミットするcommitAll()関数を用意
2. コミット直前のbulkupdateを全DBに流した後で順
次コミットする
122
第2章 Laravelで水平分散への対応 3-2.XAトランザクション・トランザクション発行
コミット直前のbulkupdateを全DBに流した
後で順次コミットする
123
commitAll関数内の実行順序
DB1
bulkupdate
DB2 DB2
bulkupdate
bulkupdate
commit
commit
commit
第2章 Laravelで水平分散への対応 3-2.XAトランザクション・トランザクション発行
部分コミットの危険は少ないながらある
124
第2章 Laravelで水平分散への対応 3-2.XAトランザクション・トランザクション発行
commitAll関数内の実行順序
DB1
bulkupdate
DB2 DB2
bulkupdate
bulkupdate
commit
commit
commit
ここで接続エラー
DB1だけコミットされる
失敗したらログに必ず残す
1. 発生したら内容によって手動修正
2. 弊社の運用上で発生したことは
未だない
125
第2章 Laravelで水平分散への対応 3-2.XAトランザクション・トランザクション発行
課題
3-3. batchにおける分散並列処理
126
batch処理における要件
1. DB側の負荷が大きいので、負荷分散したい
2. シングルプロセスだと直列になってしまうので、
DB毎にプロセス分けて並列化したい
127
第2章 Laravelで水平分散への対応 3-3. batchにおける分散並列処理
分散処理を行うためのライブラリを導入
ナウプロダクションでは以下のパッケージを導入
qxsch/worker-pool
https://packagist.org/packages/qxsch/worker-pool
128
第2章 Laravelで水平分散への対応 3-3. batchにおける分散並列処理
具体的な実装
129
第2章 Laravelで水平分散への対応 3-3. batchにおける分散並列処理
具体的な実装
public function handle()
{
// ● WorkerPool作成
// 1プロセスはWorkerというクラス単位で表され
/// Workerの集合は、WorkerPoolで分散制御される
$this->worker_pool = $this->makeWorkerPool();
// ● 分散タスク実施
$this->runWorkers();
// ● 分散タスク完了待ち
$this->worker_pool->waitForAllWorkers();
// ● 各実行結果解析&出力
// 省略 子プロセスのエラーなど取得できる。
// https://packagist.org/packages/qxsch/worker-pool を参照
}
第2章 Laravelで水平分散への対応 3-3. batchにおける分散並列処理
具体的な実装
private function makeWorkerPool()
{
return app()->make(WorkerPool::class)
// TemplateMethod:getWorkerPoolSizeは「何プロセス同時に動くか」を定義
->setWorkerPoolSize($this->getWorkerPoolSize())
// TemplateMethod:makeWorkerは「Worker役としてどのオブジェクトを使うか」を定義
->create($this->makeWorker());
}
private function runWorkers()
{
// taskGeneratorは、Workerに実行して欲しいタスクの生成を行う
// タスクはオブジェクトではなく、[1]のようにパラメーターの配列で、解釈はWorker側で行う
foreach ($this->taskGenerator() as $task) {
$this->worker_pool->run($task);
}
}
第2章 Laravelで水平分散への対応 3-3. batchにおける分散並列処理
具体的な実装
private function makeWorkerPool()
{
return app()->make(WorkerPool::class)
// TemplateMethod:getWorkerPoolSizeは「何プロセス同時に動くか」を定義
->setWorkerPoolSize($this->getWorkerPoolSize())
// TemplateMethod:makeWorkerは「Worker役としてどのオブジェクトを使うか」を定義
->create($this->makeWorker());
}
private function runWorkers()
{
// taskGeneratorは、Workerに実行して欲しいタスクの生成を行う
// タスクはオブジェクトではなく、[1]のようにパラメーターの配列で、解釈はWorker側で行う
foreach ($this->taskGenerator() as $task) {
$this->worker_pool->run($task);
}
}
第2章 Laravelで水平分散への対応 3-3. batchにおける分散並列処理
具体的な実装
private function makeWorkerPool()
{
return app()->make(WorkerPool::class)
// TemplateMethod:getWorkerPoolSizeは「何プロセス同時に動くか」を定義
->setWorkerPoolSize($this->getWorkerPoolSize())
// TemplateMethod:makeWorkerは「Worker役としてどのオブジェクトを使うか」を定義
->create($this->makeWorker());
}
private function runWorkers()
{
// taskGeneratorは、Workerに実行して欲しいタスクの生成を行う
// タスクはオブジェクトではなく、[1]のようにパラメーターの配列で、解釈はWorker側で行う
foreach ($this->taskGenerator() as $task) {
$this->worker_pool->run($task);
}
}
第2章 Laravelで水平分散への対応 3-3. batchにおける分散並列処理
具体的な実装
private function makeWorkerPool()
{
return app()->make(WorkerPool::class)
// TemplateMethod:getWorkerPoolSizeは「何プロセス同時に動くか」を定義
->setWorkerPoolSize($this->getWorkerPoolSize())
// TemplateMethod:makeWorkerは「Worker役としてどのオブジェクトを使うか」を定義
->create($this->makeWorker());
}
private function runWorkers()
{
// taskGeneratorは、Workerに実行して欲しいタスクの生成を行う
// タスクはオブジェクトではなく、[1]のようにパラメーターの配列で、解釈はWorker側で行う
foreach ($this->taskGenerator() as $task) {
$this->worker_pool->run($task);
}
}
第2章 Laravelで水平分散への対応 3-3. batchにおける分散並列処理
具体的な実装
public function taskGenerator()
{
$day = $this->argument(‘day’);
// 物理コネクションを返すIterator
// 分散処理用に用意しておくと便利
$iterator = DbBuilderIterator::instance()
->addTable('user_presentboxes');
foreach ($iterator->getIterator() as $builder) {
yield [
// 子プロセスにリソース型は渡せないので、リソース型を含まないクラスに変換
$builder->toBuilderSerial(),
$day
];
// Worker側はこのように展開する
//list (
// $builder_serial,
// $day,
// ) = $input;
//$builder = $builder_serial->connect();
}
}
第2章 Laravelで水平分散への対応 3-3. batchにおける分散並列処理
具体的な実装
public function taskGenerator()
{
$day = $this->argument(‘day’);
// 物理コネクションを返すIterator
// 分散処理用に用意しておくと便利
$iterator = DbBuilderIterator::instance()
->addTable('user_presentboxes');
foreach ($iterator->getIterator() as $builder) {
yield [
// 子プロセスにリソース型は渡せないので、リソース型を含まないクラスに変換
$builder->toBuilderSerial(),
$day
];
// Worker側はこのように展開する
//list (
// $builder_serial,
// $day,
// ) = $input;
//$builder = $builder_serial->connect();
}
}
第2章 Laravelで水平分散への対応 3-3. batchにおける分散並列処理
具体的な実装
public function taskGenerator()
{
$day = $this->argument(‘day’);
// 物理コネクションを返すIterator
// 分散処理用に用意しておくと便利
$iterator = DbBuilderIterator::instance()
->addTable('user_presentboxes');
foreach ($iterator->getIterator() as $builder) {
yield [
// 子プロセスにリソース型は渡せないので、リソース型を含まないクラスに変換
$builder->toBuilderSerial(),
$day
];
// Worker側はこのように展開する
//list (
// $builder_serial,
// $day,
// ) = $input;
//$builder = $builder_serial->connect();
}
}
第2章 Laravelで水平分散への対応 3-3. batchにおける分散並列処理
課題
3-4.migration
138
通常のmigrationで水平分散に対応?
139
$schema_connection_iterator = DbSchemaConnectionIterator::instance()
->addConnection($this->connection);
// 水平分散台数だけループする
foreach ($schema_connection_iterator->getIterator() as $schema_connection) {
$schema_connection->create($this->table, function (Blueprint $table) {
$table->bigIncrements('id');
$table->bigInteger('user_id')->unsigned();
$table->bigInteger('card_id')->unsigned();
$table->timestamps();
$table->index('user_id');
});
}
第2章 Laravelで水平分散への対応 3-4.migration
通常のmigrationでは問題がある
1. 水平分散の台数が増えたときに、マイグレーション
済みのテーブルは入れてくれない
2. 直列処理なので遅い。alter-tableは絶望的
140
第2章 Laravelで水平分散への対応 3-4.migration
通常のmigrationでは問題がある
1. 水平分散の台数が増えたときに、マイグレーション
済みのテーブルは入れてくれない
2. 直列処理なので遅い。alter-tableは絶望的
141
第2章 Laravelで水平分散への対応 3-4.migration
通常のmigrationでは問題がある
1. 水平分散の台数が増えたときに、マイグレーション
済みのテーブルは入れてくれない
2. 直列処理なので遅い。alter-tableは絶望的。
142
第2章 Laravelで水平分散への対応 3-4.migration
自前のmigrationを用意する
1. migrationのバージョン管理は、各DB毎に行う
2. migrationは並列実行させる
143
第2章 Laravelで水平分散への対応 3-4.migration
自前のmigrationを用意する
1. migrationのバージョン管理は、各DB毎に行う
2. migrationは並列実行させる
144
第2章 Laravelで水平分散への対応 3-4.migration
後から追加されたDBだけマイグレーションできる
自前のmigrationを用意する
1. migrationのバージョン管理は、各DB毎に行う
2. migrationは並列実行させる
145
第2章 Laravelで水平分散への対応 3-4.migration
alter-table直列実行問題を解決
フォルダ構成
146
第2章 Laravelで水平分散への対応 3-4.migration
migrationファイルの実装
147
class Ver1_0_0_0_UserDecks extends Migration
{
/**
* テーブル名
*
* @var string
*/
protected $table = 'user_decks';
public function up()
{
$this->createTable(function (Blueprint $table) {
$table->bigIncrements('id');
$table->bigInteger('user_id')->unsigned();
$table->integer('deck_number')->unsigned();
$table->integer('position_number')->unsigned();
$table->bigInteger('user_card_id')->unsigned();
$table->softDeletes();
$table->timestamps();
$table->index('user_id');
});
}
}
protected function createTable(Closure $callback)
{
// テーブル作成 => リビジョンを記録
// という流れのため、冪統制担保のために存在チェックする
$this->getSchemaBuilder()->dropIfExists($this->table);
// create実行
$this->getSchemaBuilder()->create($this->table, $callback);
// autoincrement初期設定
$this->getConnection()->unprepared(
sprintf(
'alter table %s auto_increment = %d',
$this->table,
$this->getConnection()->getAutoIncrementOffset()
)
);
}
第2章 Laravelで水平分散への対応 3-4.migration
migrationファイルの実装
148
class Ver1_0_0_0_UserDecks extends Migration
{
/**
* テーブル名
*
* @var string
*/
protected $table = 'user_decks';
public function up()
{
$this->createTable(function (Blueprint $table) {
$table->bigIncrements('id');
$table->bigInteger('user_id')->unsigned();
$table->integer('deck_number')->unsigned();
$table->integer('position_number')->unsigned();
$table->bigInteger('user_card_id')->unsigned();
$table->softDeletes();
$table->timestamps();
$table->index('user_id');
});
}
}
protected function createTable(Closure $callback)
{
// テーブル作成 => リビジョンを記録
// という流れのため、冪統制担保のために存在チェックする
$this->getSchemaBuilder()->dropIfExists($this->table);
// create実行
$this->getSchemaBuilder()->create($this->table, $callback);
// autoincrement初期設定
$this->getConnection()->unprepared(
sprintf(
'alter table %s auto_increment = %d',
$this->table,
$this->getConnection()->getAutoIncrementOffset()
)
);
}
第2章 Laravelで水平分散への対応 3-4.migration
migrationファイルの実装
149
class Ver1_0_0_0_UserDecks extends Migration
{
/**
* テーブル名
*
* @var string
*/
protected $table = 'user_decks';
public function up()
{
$this->createTable(function (Blueprint $table) {
$table->bigIncrements('id');
$table->bigInteger('user_id')->unsigned();
$table->integer('deck_number')->unsigned();
$table->integer('position_number')->unsigned();
$table->bigInteger('user_card_id')->unsigned();
$table->softDeletes();
$table->timestamps();
$table->index('user_id');
});
}
}
protected function createTable(Closure $callback)
{
// テーブル作成 => リビジョンを記録
// という流れのため、冪統制担保のために存在チェックする
$this->getSchemaBuilder()->dropIfExists($this->table);
// create実行
$this->getSchemaBuilder()->create($this->table, $callback);
// autoincrement初期設定
$this->getConnection()->unprepared(
sprintf(
'alter table %s auto_increment = %d',
$this->table,
$this->getConnection()->getAutoIncrementOffset()
)
);
}
第2章 Laravelで水平分散への対応 3-4.migration
migration分散実行部
150
$worker_pool = new WorkerPool();
$worker_pool->setWorkerPoolSize(64)
->create(new MigratorWorker());
$models = $this->model_finder->all();
$connection_configs = config(‘database.connections’);
foreach ($connection_configs as $connection_name => $connection_config) {
$hd_numbers = $this->calcHdNumbers($connection_config);
foreach ($models as $model) {
if ($connection_name != $model->getConnectionName()) {
continue;
}
foreach ($hd_numbers as $hd_number) {
$this->createMigrationRevisionRepository($connection_name, $hd_number);
// 1workerあたり
// 該当テーブルのファイルを全てフォルダから探し出し、リビジョン番号順に実行
// というロジックが走る
$worker_pool->run([
$model->getTable(),
$connection_name,
$hd_number
]);
}
}
}
$worker_pool->waitForAllWorkers();
第2章 Laravelで水平分散への対応 3-4.migration
migration分散実行部
151
$worker_pool = new WorkerPool();
$worker_pool->setWorkerPoolSize(64)
->create(new MigratorWorker());
$models = $this->model_finder->all();
$connection_configs = config(‘database.connections’);
foreach ($connection_configs as $connection_name => $connection_config) {
$hd_numbers = $this->calcHdNumbers($connection_config);
foreach ($models as $model) {
if ($connection_name != $model->getConnectionName()) {
continue;
}
foreach ($hd_numbers as $hd_number) {
$this->createMigrationRevisionRepository($connection_name, $hd_number);
// 1workerあたり
// 該当テーブルのファイルを全てフォルダから探し出し、リビジョン番号順に実行
// というロジックが走る
$worker_pool->run([
$model->getTable(),
$connection_name,
$hd_number
]);
}
}
}
$worker_pool->waitForAllWorkers();
第2章 Laravelで水平分散への対応 3-4.migration
第2章
Laravelで水平分散への対応
まとめ
152
まとめ
153
Laravelは
自分でレールを考えていく必要がある分
自由で
何でも作れる
まとめ
154
DIや、各種サービスとの連動など
コーディングのインフラとなる部分の
機能は豊富
まとめ
155
DBさえなんとかすれば
プロダクト固有の関心に
集中できる
ご清聴ありがとうございました
156

Weitere ähnliche Inhalte

Was ist angesagt?

目grep入門 +解説
目grep入門 +解説目grep入門 +解説
目grep入門 +解説murachue
 
メタプログラミングって何だろう
メタプログラミングって何だろうメタプログラミングって何だろう
メタプログラミングって何だろうKota Mizushima
 
導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について
導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について
導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来についてshinjiigarashi
 
ゲームエンジニアのためのデータベース設計
ゲームエンジニアのためのデータベース設計ゲームエンジニアのためのデータベース設計
ゲームエンジニアのためのデータベース設計sairoutine
 
テスト文字列に「うんこ」と入れるな
テスト文字列に「うんこ」と入れるなテスト文字列に「うんこ」と入れるな
テスト文字列に「うんこ」と入れるなKentaro Matsui
 
世界一わかりやすいClean Architecture
世界一わかりやすいClean Architecture世界一わかりやすいClean Architecture
世界一わかりやすいClean ArchitectureAtsushi Nakamura
 
テストコードの DRY と DAMP
テストコードの DRY と DAMPテストコードの DRY と DAMP
テストコードの DRY と DAMPYusuke Kagata
 
「のどが渇いた」というユーザーに何を出す? ユーザーの「欲しい」に惑わされない、本当のインサイトを見つけるUXデザイン・UXリサーチ
「のどが渇いた」というユーザーに何を出す? ユーザーの「欲しい」に惑わされない、本当のインサイトを見つけるUXデザイン・UXリサーチ「のどが渇いた」というユーザーに何を出す? ユーザーの「欲しい」に惑わされない、本当のインサイトを見つけるUXデザイン・UXリサーチ
「のどが渇いた」というユーザーに何を出す? ユーザーの「欲しい」に惑わされない、本当のインサイトを見つけるUXデザイン・UXリサーチYoshiki Hayama
 
PHP+MySQLを使ったスケーラブルなソーシャルゲーム開発
PHP+MySQLを使ったスケーラブルなソーシャルゲーム開発PHP+MySQLを使ったスケーラブルなソーシャルゲーム開発
PHP+MySQLを使ったスケーラブルなソーシャルゲーム開発infinite_loop
 
オブジェクト指向プログラミングのためのモデリング入門
オブジェクト指向プログラミングのためのモデリング入門オブジェクト指向プログラミングのためのモデリング入門
オブジェクト指向プログラミングのためのモデリング入門増田 亨
 
php-src の歩き方
php-src の歩き方php-src の歩き方
php-src の歩き方do_aki
 
Redisの特徴と活用方法について
Redisの特徴と活用方法についてRedisの特徴と活用方法について
Redisの特徴と活用方法についてYuji Otani
 
PostgreSQLアンチパターン
PostgreSQLアンチパターンPostgreSQLアンチパターン
PostgreSQLアンチパターンSoudai Sone
 
Redis勉強会資料(2015/06 update)
Redis勉強会資料(2015/06 update)Redis勉強会資料(2015/06 update)
Redis勉強会資料(2015/06 update)Yuji Otani
 
ホットペッパービューティーにおけるモバイルアプリ向けAPIのBFF/Backend分割
ホットペッパービューティーにおけるモバイルアプリ向けAPIのBFF/Backend分割ホットペッパービューティーにおけるモバイルアプリ向けAPIのBFF/Backend分割
ホットペッパービューティーにおけるモバイルアプリ向けAPIのBFF/Backend分割Recruit Lifestyle Co., Ltd.
 
ソーシャルゲームのためのデータベース設計
ソーシャルゲームのためのデータベース設計ソーシャルゲームのためのデータベース設計
ソーシャルゲームのためのデータベース設計Yoshinori Matsunobu
 
エンジニアの個人ブランディングと技術組織
エンジニアの個人ブランディングと技術組織エンジニアの個人ブランディングと技術組織
エンジニアの個人ブランディングと技術組織Takafumi ONAKA
 
心理的安全性を 0から80ぐらいに上げた話
心理的安全性を 0から80ぐらいに上げた話心理的安全性を 0から80ぐらいに上げた話
心理的安全性を 0から80ぐらいに上げた話Yusuke Hisatsu
 
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)Takuto Wada
 

Was ist angesagt? (20)

目grep入門 +解説
目grep入門 +解説目grep入門 +解説
目grep入門 +解説
 
メタプログラミングって何だろう
メタプログラミングって何だろうメタプログラミングって何だろう
メタプログラミングって何だろう
 
導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について
導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について
導入から 10 年、PHP の trait は滅びるべきなのか その適切な使いどころと弱点、将来について
 
ゲームエンジニアのためのデータベース設計
ゲームエンジニアのためのデータベース設計ゲームエンジニアのためのデータベース設計
ゲームエンジニアのためのデータベース設計
 
テスト文字列に「うんこ」と入れるな
テスト文字列に「うんこ」と入れるなテスト文字列に「うんこ」と入れるな
テスト文字列に「うんこ」と入れるな
 
世界一わかりやすいClean Architecture
世界一わかりやすいClean Architecture世界一わかりやすいClean Architecture
世界一わかりやすいClean Architecture
 
テストコードの DRY と DAMP
テストコードの DRY と DAMPテストコードの DRY と DAMP
テストコードの DRY と DAMP
 
「のどが渇いた」というユーザーに何を出す? ユーザーの「欲しい」に惑わされない、本当のインサイトを見つけるUXデザイン・UXリサーチ
「のどが渇いた」というユーザーに何を出す? ユーザーの「欲しい」に惑わされない、本当のインサイトを見つけるUXデザイン・UXリサーチ「のどが渇いた」というユーザーに何を出す? ユーザーの「欲しい」に惑わされない、本当のインサイトを見つけるUXデザイン・UXリサーチ
「のどが渇いた」というユーザーに何を出す? ユーザーの「欲しい」に惑わされない、本当のインサイトを見つけるUXデザイン・UXリサーチ
 
PHP+MySQLを使ったスケーラブルなソーシャルゲーム開発
PHP+MySQLを使ったスケーラブルなソーシャルゲーム開発PHP+MySQLを使ったスケーラブルなソーシャルゲーム開発
PHP+MySQLを使ったスケーラブルなソーシャルゲーム開発
 
オブジェクト指向プログラミングのためのモデリング入門
オブジェクト指向プログラミングのためのモデリング入門オブジェクト指向プログラミングのためのモデリング入門
オブジェクト指向プログラミングのためのモデリング入門
 
php-src の歩き方
php-src の歩き方php-src の歩き方
php-src の歩き方
 
Redisの特徴と活用方法について
Redisの特徴と活用方法についてRedisの特徴と活用方法について
Redisの特徴と活用方法について
 
PostgreSQLアンチパターン
PostgreSQLアンチパターンPostgreSQLアンチパターン
PostgreSQLアンチパターン
 
Redis勉強会資料(2015/06 update)
Redis勉強会資料(2015/06 update)Redis勉強会資料(2015/06 update)
Redis勉強会資料(2015/06 update)
 
ホットペッパービューティーにおけるモバイルアプリ向けAPIのBFF/Backend分割
ホットペッパービューティーにおけるモバイルアプリ向けAPIのBFF/Backend分割ホットペッパービューティーにおけるモバイルアプリ向けAPIのBFF/Backend分割
ホットペッパービューティーにおけるモバイルアプリ向けAPIのBFF/Backend分割
 
ソーシャルゲームのためのデータベース設計
ソーシャルゲームのためのデータベース設計ソーシャルゲームのためのデータベース設計
ソーシャルゲームのためのデータベース設計
 
エンジニアの個人ブランディングと技術組織
エンジニアの個人ブランディングと技術組織エンジニアの個人ブランディングと技術組織
エンジニアの個人ブランディングと技術組織
 
Docker Compose 徹底解説
Docker Compose 徹底解説Docker Compose 徹底解説
Docker Compose 徹底解説
 
心理的安全性を 0から80ぐらいに上げた話
心理的安全性を 0から80ぐらいに上げた話心理的安全性を 0から80ぐらいに上げた話
心理的安全性を 0から80ぐらいに上げた話
 
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
 

Ähnlich wie Laravelを用いたゲームサーバーのチューニング

Dockerでデプロイ
DockerでデプロイDockerでデプロイ
Dockerでデプロイoshiro_seiya
 
Open Shift v3 主要機能と内部構造のご紹介
Open Shift v3 主要機能と内部構造のご紹介Open Shift v3 主要機能と内部構造のご紹介
Open Shift v3 主要機能と内部構造のご紹介Etsuji Nakai
 
Lumen使ってみたレポ
Lumen使ってみたレポLumen使ってみたレポ
Lumen使ってみたレポmikakane
 
ドメインロジックの実装方法とドメイン駆動設計
ドメインロジックの実装方法とドメイン駆動設計ドメインロジックの実装方法とドメイン駆動設計
ドメインロジックの実装方法とドメイン駆動設計Tadayoshi Sato
 
Web技術勉強会 第33回
Web技術勉強会 第33回Web技術勉強会 第33回
Web技術勉強会 第33回龍一 田中
 
Ruby on Rails 入門
Ruby on Rails 入門Ruby on Rails 入門
Ruby on Rails 入門Yasuko Ohba
 
夏サミ2014 クラウドとCIツールで変わるエンタープライズJava開発
夏サミ2014 クラウドとCIツールで変わるエンタープライズJava開発 夏サミ2014 クラウドとCIツールで変わるエンタープライズJava開発
夏サミ2014 クラウドとCIツールで変わるエンタープライズJava開発 Takakiyo Tanaka
 
Rails初心者レッスン lesson1 3rd edition
Rails初心者レッスン lesson1 3rd editionRails初心者レッスン lesson1 3rd edition
Rails初心者レッスン lesson1 3rd editionGoh Matsumoto
 
PHPカンファレンス関西2012 Silex
PHPカンファレンス関西2012 SilexPHPカンファレンス関西2012 Silex
PHPカンファレンス関西2012 SilexMasao Maeda
 
OpsWorks aws-cli#11
OpsWorks aws-cli#11OpsWorks aws-cli#11
OpsWorks aws-cli#11Yuta Shimada
 
Pry による repl 駆動開発について
Pry による repl 駆動開発についてPry による repl 駆動開発について
Pry による repl 駆動開発についてTomoya Kawanishi
 
ステップ・バイ・ステップで学ぶラムダ式・Stream api入門 #jjug ccc #ccc h2
ステップ・バイ・ステップで学ぶラムダ式・Stream api入門 #jjug ccc #ccc h2ステップ・バイ・ステップで学ぶラムダ式・Stream api入門 #jjug ccc #ccc h2
ステップ・バイ・ステップで学ぶラムダ式・Stream api入門 #jjug ccc #ccc h2Masatoshi Tada
 
Docker friendly PHP / Laravel
Docker friendlyPHP / LaravelDocker friendlyPHP / Laravel
Docker friendly PHP / LaravelKentarou Takeda
 
Silverlight 5でぶり返すWPF不要論
Silverlight 5でぶり返すWPF不要論Silverlight 5でぶり返すWPF不要論
Silverlight 5でぶり返すWPF不要論Yuya Yamaki
 
Trema の紹介とネットワーク仮想化への応用
Trema の紹介とネットワーク仮想化への応用Trema の紹介とネットワーク仮想化への応用
Trema の紹介とネットワーク仮想化への応用kazuyas
 
Kubernetes 導入から始める DevOps について
Kubernetes 導入から始める DevOps についてKubernetes 導入から始める DevOps について
Kubernetes 導入から始める DevOps についてShigeru Tatsuta
 

Ähnlich wie Laravelを用いたゲームサーバーのチューニング (20)

Dockerでデプロイ
DockerでデプロイDockerでデプロイ
Dockerでデプロイ
 
ATN No.2 Scala事始め
ATN No.2 Scala事始めATN No.2 Scala事始め
ATN No.2 Scala事始め
 
Open Shift v3 主要機能と内部構造のご紹介
Open Shift v3 主要機能と内部構造のご紹介Open Shift v3 主要機能と内部構造のご紹介
Open Shift v3 主要機能と内部構造のご紹介
 
Lumen使ってみたレポ
Lumen使ってみたレポLumen使ってみたレポ
Lumen使ってみたレポ
 
ドメインロジックの実装方法とドメイン駆動設計
ドメインロジックの実装方法とドメイン駆動設計ドメインロジックの実装方法とドメイン駆動設計
ドメインロジックの実装方法とドメイン駆動設計
 
solr勉強会資料
solr勉強会資料solr勉強会資料
solr勉強会資料
 
Web技術勉強会 第33回
Web技術勉強会 第33回Web技術勉強会 第33回
Web技術勉強会 第33回
 
Ruby on Rails 入門
Ruby on Rails 入門Ruby on Rails 入門
Ruby on Rails 入門
 
夏サミ2014 クラウドとCIツールで変わるエンタープライズJava開発
夏サミ2014 クラウドとCIツールで変わるエンタープライズJava開発 夏サミ2014 クラウドとCIツールで変わるエンタープライズJava開発
夏サミ2014 クラウドとCIツールで変わるエンタープライズJava開発
 
Rails初心者レッスン lesson1 3rd edition
Rails初心者レッスン lesson1 3rd editionRails初心者レッスン lesson1 3rd edition
Rails初心者レッスン lesson1 3rd edition
 
PHPカンファレンス関西2012 Silex
PHPカンファレンス関西2012 SilexPHPカンファレンス関西2012 Silex
PHPカンファレンス関西2012 Silex
 
OpsWorks aws-cli#11
OpsWorks aws-cli#11OpsWorks aws-cli#11
OpsWorks aws-cli#11
 
Pry による repl 駆動開発について
Pry による repl 駆動開発についてPry による repl 駆動開発について
Pry による repl 駆動開発について
 
ステップ・バイ・ステップで学ぶラムダ式・Stream api入門 #jjug ccc #ccc h2
ステップ・バイ・ステップで学ぶラムダ式・Stream api入門 #jjug ccc #ccc h2ステップ・バイ・ステップで学ぶラムダ式・Stream api入門 #jjug ccc #ccc h2
ステップ・バイ・ステップで学ぶラムダ式・Stream api入門 #jjug ccc #ccc h2
 
Docker friendly PHP / Laravel
Docker friendlyPHP / LaravelDocker friendlyPHP / Laravel
Docker friendly PHP / Laravel
 
Silverlight 5でぶり返すWPF不要論
Silverlight 5でぶり返すWPF不要論Silverlight 5でぶり返すWPF不要論
Silverlight 5でぶり返すWPF不要論
 
Trema の紹介とネットワーク仮想化への応用
Trema の紹介とネットワーク仮想化への応用Trema の紹介とネットワーク仮想化への応用
Trema の紹介とネットワーク仮想化への応用
 
Docker Tokyo
Docker TokyoDocker Tokyo
Docker Tokyo
 
Groovyコンファレンス
GroovyコンファレンスGroovyコンファレンス
Groovyコンファレンス
 
Kubernetes 導入から始める DevOps について
Kubernetes 導入から始める DevOps についてKubernetes 導入から始める DevOps について
Kubernetes 導入から始める DevOps について
 

Laravelを用いたゲームサーバーのチューニング