第
13
回の目次
前回: Webサーバー 今回: RDB (MySQLプログラミング) トランザクション処理 DB高速化(memcached,その他) 【参考書1: MySQL全機能バイブル(他にも沢山ある)】 【参考書2:伊藤ほか: サーバ/インフラを支える技術,技術評論社】 【参考書3:西田ほか: Googleを支える技術,技術評論社】本日のソースコード
testmysql.rb: mysqlをrubyで呼び出す testmem.rb: memcachedをrubyで呼び出す
なぜデータベースが必要か
?
複雑な処理には状態が必要 利用者登録,利用者課金,サービス状態 Webサーバーには状態は残せない/残すべきではない 3層アーキテクチャが一般的 ブラウザ(プレゼンテーション) Webサーバーs (ビジネスロジック) データベース 有名なデータベースソフト MySQL PostgreSQL SQLite MariaDB (MySQL互換)MySQL
の概要
Relational Data Base (SQL) Oracleが買収 一つの独立したシステム.つまり, 各サーバに一つだけ存在 セキュリティ管理は独自 ユーザ管理も自前 Webサーバと連携させるには言語から呼び出す(→次頁) ストレージエンジンをInnoDBにすればトランザクション可 (デフォルトではMyISAM)
Web
サーバから
RDB
を使う
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"]
SQL injection
RDBをプログラムする時は要注意 重要なデータはデータベースに入っており,まず間違いなく SQLでアクセスされる システムを柔軟に作ろうとするとSQLを実行時に合成せざ るを得ない 利用者の入力からSQL文を作ることが多い ありがちな例:q="select name, score from engtable where name="+username+";" result=ms.query(q)
usernameは,”’yamazaki’”などであることを想定しているが,も
し次のような値だったら?
SQL injection
対策
(SQLだけでなく一般のinjection対策) 入力が危険なのでなく出力が危険(×サニタイズ) 入力対象は意外と広い(ヘッダーやCookieも) 出力文字列中にメタ文字がないようにする(htmlspecialchars) 出力もいろいろある(HTML出力,DBへの出力,DBへのコ マンド) 入力文字列はその目的が決まったところでチェックをする ファイルパス名,ユーザ名,…MySQL
でのトランザクション処理
トランザクションとはデータベースに対する一連の操作START TRANSACTION;
∼COMMIT;
またはROLLBACK;
トランザクションが満たすべき性質: ACID Atomicity: トランザクションはall-or-nothingで動作 Consistency: DBが矛盾した状態にならない Isolation: 2つのトランザクションは独立して動く Durability: Commitされたデータは永続するIsolationのレベルはTRANSACTION ISOLATION LEVELコマンド で設定可能
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文を実行したら結果が違う)
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>
RCCAP
定理
E.Brewerが1999年に発表∗ 分散データシステムは,高々次の2つしか満たせない: Consistency: 全ノードが同じデータを参照する Availability:操作は必ず(成功か失敗で)終了する Partition tolerance: ネットワーク分割しても動作する つまり, 矛盾状態を許すなら,分割状態での操作は可能 分割状態での矛盾を許さないなら,操作を遅延するしかない 分割を考えなくて良いなら,矛盾ない操作が可能Partition Toleranceは大規模分散では必須なので,Consistencyと Availabilityの間で解を見つけるしかない
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: 一時的に古いデータが見えてしまって も良い.それほど長くない時間内に最新になれば良い.
BASE
の考え方
例えば一つのトランザクションの中で送金と銀行のもつ総額を数 えるとする.
送金と銀行の総額カウントの増減:
総額カウントは概算値だと考えてしまう
(eventual consistencyの例であり,soft sateの例でもある)
送金と受金:
送金中という概念を入れる(persistent message)
送金メッセージの喪失:
再送可能なようにメッセージID付与(idempotent update)
「BASE」的設計方針でありACIDのように厳密な話ではない
memcached
背景: 多くのデータは,writeは少なく,readは多い readさえ早く実行できれば,かなり楽になる 汎用のキャッシュサーバ memcached: 名前と値の組みを登録できるサーバ すべてをメモリ上で行うので高速 使い方は自由(普通は,WebサーバーとDBの間に挟む) クライアントが頑張る シャーディング 冗長化簡単な実験
$ telnet XXXX 11211
←memcached
サーバに接続set name 12345 0 9
← キーがname
の長さ9
の値を保存testvalue
← 長さ9
文字の値STORED
→memcached
の出力get name
← キーがname
の値を取得VALUE name 12345 9
→memcached
の出力testvalue
→memcached
の出力END
幾つかのコマンド
保存:
set
キー フラグ 有効時間 バイト長 そのバイト長のデータ 取得:
get
キー 取得(CAS
付き):
gets
キー 削除:
delete
キー前回
get
してから誰もset
してないことをcheck
してset:
cas
キー フラグ 有効時間 バイト長cas
値そのバイト長のデータ
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)
∼testmem.rb
の実行結果
"foo’s value"
1234
"this is a pen"
"value"
"this is a pen"
ごく簡単なベンチマーク
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万) たぶん通信(+パース)がほとんど
サーバのアーキテクチャ
近年の動向
1: NoSQL
とは
?
これまで: データベース= RDBMS (つまりSQL)
とにかくでかい(ペタバイト級)
非定形データ 多様な処理
SQLじゃ駄目 →NoSQL (Not Only SQL) 代表的なNoSQL技術 巨大データ+ Key-Value Hadoop→ これについては次回 BigTable Cassandra その他: MongoDB, CouchDB
近年の動向
2:
高速化
インメモリDB 主記憶上にデータベースを構築(最大数十GB程度) 高速(応答時間μ秒のレベル) ハードディスク上に(非同期で)ログを取ることで永続性を担保 Oracle TimesTenなど フラッシュメモリの利用 カラム型DB トランザクショナルメモリIBM PowerPC A2, Intel Haswell
トランザクション技術の進展
Serializable Snapshot Isolation
SIをSerializableにする(ただしわずかにfalse-positiveあり) Google Spanner