サーバーのおしごとWebサービスにおけるサーバー構成とその目的
自己紹介清水 佑吾 @yamionp
id:yamionp
株式会社 gumi 勤務
Python歴約2年
サーバーさわりはじめて約10年
略歴
高校:CGIやホームページを作成して小遣い稼ぎ
卒業後:ISPでネットワーク&サーバー構築のバイト
ちょっと前:PHPでウェブサービス開発
今:ソーシャルゲーム開発
サーバーの話
使用言語・フレームワーク
使用ミドルウェア
最近のサーバー構成
Application
MessageQueue
EC2
JobEC2
ElasticCache
Horizontal Partitioning
RDS
Database
ELBS3
今日話す範囲
最小限構成
Service
Request
Response DATA
一番シンプルな形
問題は?
サーバーが壊れるとデータが消える
サービスも止まる
書き込み途中で壊れると中途半端な書き込みが残る
アクセスが増えたら今のサーバー1台では処理しきれない時
負荷への対処スケールアップ
スペックの良いサーバーに変更して処理能力UP!
スケールアウト
台数を増やして処理能力UP!
今日はこちらの話をします
Service
DATA
Request
Response
Service
DATA
DATA
DATA
A
B
C
単にサーバーを増やした
特定のサーバーに負荷が集中すると対処出来ない
全データを見るには個別にアクセスする必要がある
可用性、整合性の問題は解決していない
データベース
Service
DATA
DATA
DATA
A
B
C
Application
ServiceDATA
SQL
AB
CSQL
データの格納にRDBを使用した
データの扱い方が統一される
(リレーショナルモデル&SQL)
どのApplicationサーバーでも同じデータを扱える
ユーザーはどれか一台にアクセスすれば良い
アクセス先はユーザーが決める
ロードバランサー
Application
ServiceDATA
A
B
C
助けて!
暇…
暇…
Elastic Load Balancing
Application
ServiceDATA
SQL
AB
C
Application
Service DATA
ELBLoadBalancer
AB
C
ユーザーがアクセスする先を選ぶ必要が無くなった
特定のApplicationサーバーに負荷が集中しない
=> 台数さえ増やせば処理可能
レプリケーション
Application
Service
ELB
DATA
Application
Service
ELB
読み込み書き込み
Master Slave
メリット
既存のロジックに大きな変更無しに導入可能
自動でデータが更新される
再起動しても消えない
デメリット
分散可能なのは読み込みのみ
非同期の場合古いデータが最新のデータと限らない
同期の場合、Masterのパフォーマンスが悪化する
KeyValueStore (KVS)
KVSとは
KeyとValueのペアでデータを管理するDB
Memcache, DynamoDB, Riak, Redis…etc
それぞれ全て特徴・使い方・用途が違う
KEY VALUE
A_AGE 21
A_AGE 21
Memcached
Application
Service
ELB
DATA
Application
Service
ELB
DATA
GET
SELECT
特徴
オンメモリなので非常に高速 (RDBの十倍程度)
シンプル
注意点キャッシュロジックをアプリケーションに実装が必要
更新時に削除を忘れると古いデータが返る
キャッシュのデータをもとにRDBを更新してはならない
TTL以前に消える可能性
キャッシュ間のサイズが大きく違うとメモリ効率悪化
永続化は出来ない(データはメモリ上にのみ存在)
Redis
特徴インメモリ型KVS
永続化可能
レプリケーション可能
データ型 (LIST, SET, SORTED SET, HASH) を持つ
複雑な操作をアトミックに実行可能
LIST
A B C D
LPOP
LIST
B C D E
RPUSH
アトミックに実行とは
原子性
コマンド結果が全て成功or全て失敗しかない事
操作の途中段階にならない。
データベース分割
Application
Service
ELB書き込み
Master Slave
暇…助けて!
書込み性能の限界
垂直分割
ID 体力 やくそう ジョブ
Player A 100 2 戦士
Player B 98 8 格闘家
Player C 20 0 魔法使い
Player D 0 10 僧侶
DB 1 DB 2 DB 3
Application
Service
ELB
アイテム カード
メリット
分割しても集計が容易
ユニーク制約が維持される
デメリット
アイテムに負荷が集中したら対処できない
実際は分割をまたいだ処理が多い
水平分割
体力 やくそう ジョブ
Player A 100 2 戦士
Player B 98 8 格闘家
Player C 20 0 魔法使い
Player D 0 10 僧侶
DB 1
DB 2
DB 3
Application
Service
ELB Player1
Player2
メリット
ほぼ均等に負荷が分散される
プレイヤーに閉じた処理はDBをまたがなくてすむ
デメリット
集計が難しい
技術的難易度(フレームワークがサポートしないetc
ユニーク制約をかけれない
ID1のデータが分割した数だけ存在する
データが壊れる時
GET A
3
SET A 5a = a + 2
a = 3
set(“a”, a)
A 3
A 5
DBユーザー1
DBユーザー1
GET A
3
SET A 5
a = a + 2a = 3
set(“a”, a)
A 3
A 5
ユーザー2
GET A
3
SET A 5a = a + 2
a = 3
set(“a”, a)A 5
A 3
3+2+2=5
ロック
DBユーザー1GET A & LOCK
3
SET A 5a = a + 2a = 3
A 3
A 5
ユーザー2
GET A & LOCK
5
SET A 7 a = a + 2a = 5
A 7
A 3
RDBでは標準的
SELECT FOR UPDATE
デッドロックの危険性
ロック順番を統一する
CAS
DBユーザー1
GET A
3, ver02
SET A 5, ver02
a = a + 2a = 3
A 3
A 3
ユーザー2
GET A
3, ver02
SET A 5, ver02
a = a + 2a = 3
A 5
A 3
ver02
ver02
ver02
ver03
A 5ver03
失敗
DB ユーザー2
GET A
5, ver03
SET A 7, ver03
a = a + 2a = 5
A 5
A 5ver03
ver03A 7
ver04
失敗したので最初からリトライ
Memcache, Redisなどで使用可能
ロックが無いので処理が止まらない
繰り返すロジックを自分で実装する必要が有る
永遠に失敗し続ける可能性がある
リトライ回数に上限をつける
まとめ
負荷対策にはスケールアップとスケールアウトがある
データ読み込みに関しては手段が多い
KVSはそれぞれのソフトで目的・使い方が違う
書き込みの分散は整合性との戦い
ご清聴ありがとうございました