第13回 DBとトランザクション

第 13 回の目次
前回: Web サーバー
今回:
RDB (MySQL プログラミング)
トランザクション処理
DB 高速化 (memcached,その他)
【参考書 1: MySQL 全機能バイブル (他にも沢山ある)】
【参考書 2: 伊藤ほか: サーバ/インフラを支える技術, 技術評論社】
【参考書 3: 西田ほか: Google を支える技術, 技術評論社】
1 / 23
本日のソースコード
testmysql.rb: mysql を ruby で呼び出す
testmem.rb: memcached を ruby で呼び出す
2 / 23
なぜデータベースが必要か?
複雑な処理には状態が必要
利用者登録,利用者課金,サービス状態
Web サーバーには状態は残せない/残すべきではない
3 層アーキテクチャが一般的
ブラウザ (プレゼンテーション)
Web サーバー s (ビジネスロジック)
データベース
有名なデータベースソフト
MySQL
PostgreSQL
SQLite
MariaDB (MySQL 互換)
3 / 23
MySQL の概要
Relational Data Base (SQL)
Oracle が買収
一つの独立したシステム.つまり,
各サーバに一つだけ存在
セキュリティ管理は独自
ユーザ管理も自前
Web サーバと連携させるには言語から呼び出す (→次頁)
ストレージエンジンを InnoDB にすればトランザクション可
(デフォルトでは MyISAM)
4 / 23
Web サーバから RDB を使う
MySQL/Ruby を使った例【→ testmysql.rb】
5 / 23
testmysql.rb の出力例
"5.1.53"
["information_schema", "demo_development", "demo_test",
"mydb_development", "mydb_test", "mysql",
"nchak_development", "nchak_test", "sampledb", "test"]
["engtable", "mathtable"]
"id"
"name"
"score"
["1", "yamazaki", "50"]
["2", "shibaura", "70"]
---result of join--["yamazaki", "50", "60"]
["shibaura", "70", "80"]
6 / 23
SQL injection
RDB をプログラムする時は要注意
重要なデータはデータベースに入っており,まず間違いなく
SQL でアクセスされる
システムを柔軟に作ろうとすると SQL を実行時に合成せざ
るを得ない
利用者の入力から SQL 文を作ることが多い
ありがちな例:
q="select name, score from engtable where name="+username+";"
result=ms.query(q)
username は,”’yamazaki’”などであることを想定しているが,も
し次のような値だったら?
username="’dummy’ OR true"
7 / 23
SQL injection 対策
(SQL だけでなく一般の injection 対策)
入力が危険なのでなく出力が危険 (×サニタイズ )
入力対象は意外と広い (ヘッダーや Cookie も)
出力文字列中にメタ文字がないようにする (htmlspecialchars)
出力もいろいろある (HTML 出力,DB への出力,DB へのコ
マンド)
入力文字列はその目的が決まったところでチェックをする
ファイルパス名,ユーザ名,…
8 / 23
MySQL でのトランザクション処理
トランザクションとはデータベースに対する一連の操作
START TRANSACTION;
∼
COMMIT; または ROLLBACK;
トランザクションが満たすべき性質: ACID
Atomicity: トランザクションは all-or-nothing で動作
Consistency: DB が矛盾した状態にならない
Isolation: 2 つのトランザクションは独立して動く
Durability: Commit されたデータは永続する
Isolation のレベルは TRANSACTION ISOLATION LEVEL コマンド
で設定可能
9 / 23
MySQL の Isolation Level
以下の 4 つのレベルに設定可能
Serializable: 全部なし
Repeatable Read: phantom
Read Committed: non-repeatable, phantom
Read Uncommitted: dirty, non-repeatable, phantom
それぞれの read の意味
dirty read: まだ commit されてないデータが見える
non-repeatable read: 別 Tx が書いて commit した値が見える
(2 回読んだら違う値だった)
phantom read: 新しいデータの挿入が見える
(2 回同じ SELECT 文を実行したら結果が違う)
10 / 23
Snapshot Isolation
H.Berenson: A critique of ANSI SQL isolation levels, SIGMOD 95
MVCC (Multi-Version Concurrency Control) の考え方:
値にバージョンを導入する
ある Tx 内の read は,その Tx 開始時点のバージョンを read
することにする
First-Committer-Wins
利点: read も write もブロックしないので高速
Read Skew: r1[x]...w2[x]...w2[y]...c2...r1[y]...(c1 or a1)
Write Skew: r1[x]...r2[y]...w1[y]...w2[x]...(c1 and c2 occur)
SI: Read Skew は起きないが,Write Skew は起きる.
RR: 両方起きない.
RC:両方起きる.
ゆえに RR > SI > RC
Write Skew は,Serializable Snapshot Isolation (2008 年) で解決
11 / 23
CAP 定理
E.Brewer が 1999 年に発表 ∗
分散データシステムは,高々次の 2 つしか満たせない:
Consistency: 全ノードが同じデータを参照する
Availability: 操作は必ず (成功か失敗で) 終了する
Partition tolerance: ネットワーク分割しても動作する
つまり,
矛盾状態を許すなら,分割状態での操作は可能
分割状態での矛盾を許さないなら,操作を遅延するしかない
分割を考えなくて良いなら,矛盾ない操作が可能
Partition Tolerance は大規模分散では必須なので,Consistency と
Availability の間で解を見つけるしかない
(*) ここでは E.Brewer: CAP Twelve Years Later, Computer 2012 を参考
12 / 23
ACID vs BASE
Consistency first, Availability second: ACID
Atomicity: トランザクションは all-or-nothing で動作
Consistency: DB が矛盾した状態にならない
Isolation: 2 つのトランザクションは独立して動く
Durability: Commit されたデータは永続する
Availability first, Consistency second: BASE∗
Basically Available: 正しい答えでなくても,すぐ返事が返っ
てくることの方が重要.
Soft-State: 状態は,(コストが高い) 別の手段で復旧できるの
であれば,失われてしまっても良い.
Eventual Consistency: 一時的に古いデータが見えてしまって
も良い.それほど長くない時間内に最新になれば良い.
(*) A. Fox: Cluster-Based Scalable Network Services, ACM SOSP 97.
13 / 23
BASE の考え方
例えば一つのトランザクションの中で送金と銀行のもつ総額を数
えるとする.
送金と銀行の総額カウントの増減:
総額カウントは概算値だと考えてしまう
(eventual consistency の例であり,soft sate の例でもある)
送金と受金:
送金中という概念を入れる (persistent message)
送金メッセージの喪失:
再送可能なようにメッセージ ID 付与 (idempotent update)
「BASE」的設計方針であり ACID のように厳密な話ではない
Serializable Snapshot Isolation ができたので BASE は古いという人も
BASE でよければ,いろいろなやり方が → 次のページ
14 / 23
memcached
背景:
多くのデータは,write は少なく,read は多い
read さえ早く実行できれば,かなり楽になる
汎用のキャッシュサーバ
memcached:
名前と値の組みを登録できるサーバ
すべてをメモリ上で行うので高速
使い方は自由 (普通は,Web サーバーと DB の間に挟む)
クライアントが頑張る
シャーディング
冗長化
15 / 23
簡単な実験
$ telnet XXXX 11211
set name 12345 0 9
testvalue
STORED
get name
VALUE name 12345 9
testvalue
END
←
←
←
→
←
→
→
memcached サーバに接続
キーが name の長さ 9 の値を保存
長さ 9 文字の値
memcached の出力
キーが name の値を取得
memcached の出力
memcached の出力
16 / 23
幾つかのコマンド
保存:
set キー フラグ 有効時間 バイト長
そのバイト長のデータ
取得:
get キー
取得 (CAS 付き):
gets キー
削除:
delete キー
前回 get してから誰も set してないことを check して set:
cas キー フラグ 有効時間 バイト長 cas 値
そのバイト長のデータ
不可分での 1 増加,1 減少: incr や decr
17 / 23
memcache クライアントの例 (ruby)
memcache-client の例 【→ testmem.rb】
(memcache-client は非推奨で後継は Dalli)
次のように生成すると適当に分散してくれる:
MemCache.new(ホスト 1, ホスト 2, ホスト 3)
また次のように書けるのは
mc["foo"]="foo’s value"
p mc[”foo”]
ruby では次のように演算子も再定義可能だから:
def []=(key, value)
∼
end
def [](key)
∼
end
18 / 23
testmem.rb の実行結果
"foo’s value"
1234
"this is a pen"
"value"
"this is a pen"
19 / 23
ごく簡単なベンチマーク
ruby でリモートサーバにアクセスし測定
時間は 1 回当たりの elapse time
チューニング等は何もやってない
MySQL:
insert into engtable values (I, ’y’+I.to_s, I);
→ 0.48 ミリ秒 (I=1..10 万)
select name, score from engtable where name=’y0’;
→ 0.47 ミリ秒 (I=1..10 万)
select name, score from engtable where name=’y’+I.to_s;
→ 1 回目 13.4 ミリ秒 (I=1..1 万)
→ 2 回目 0.47 ミリ秒 (I=1..1 万)
memcached:
mc["y"+I.to_s]=I
→ 0.5 ミリ秒 (I=1..10 万)
x = mc["y"+I.to_s]
→ 0.49 ミリ秒 (I=1..10 万)
たぶん通信 (+パース) がほとんど
MySQL もキャッシュ的な効果が効く範囲ではかなり早い
20 / 23
Twitter サーバのアーキテクチャ
引用元: http://www.infoq.com/news/2009/06/Twitter-Architecture
21 / 23
近年の動向 1: NoSQL とは?
これまで: データベース = RDBMS (つまり SQL)
とにかくでかい (ペタバイト級)
非定形データ
多様な処理
SQL じゃ駄目 → NoSQL (Not Only SQL)
代表的な NoSQL 技術
巨大データ + Key-Value
Hadoop → これについては次回
BigTable
Cassandra
その他: MongoDB, CouchDB
22 / 23
近年の動向 2: 高速化
インメモリ DB
主記憶上にデータベースを構築 (最大数十 GB 程度)
高速 (応答時間μ秒のレベル)
ハードディスク上に (非同期で) ログを取ることで永続性を担保
Oracle TimesTen など
フラッシュメモリの利用
カラム型 DB
トランザクショナルメモリ
IBM PowerPC A2, Intel Haswell
トランザクション技術の進展
Serializable Snapshot Isolation
SI を Serializable にする (ただしわずかに false-positive あり)
Google Spanner
世界規模のトランザクションに全順序 (=物理時刻) .通信因果関係
でないので論理時間ではない.GPS と原子時計を使う.
23 / 23