800 likes | 937 Views
はてな流大規模データ処理. アジェンダ. 大規模なデータ OS のキャッシュ MySQL の運用 大規模データアプリケーションの開発. 大規模なデータ. 大規模データ. はてなブックマーク. mysql> select count(*) from relword; +----------+ | count(*) | +----------+ | 51780147 | +----------+ 1 row in set (0.00 sec). はてなブックマークのデータ規模. レコ ― ド数 1,073 万エントリー 3,134 万ブックマーク
E N D
アジェンダ • 大規模なデータ • OS のキャッシュ • MySQLの運用 • 大規模データアプリケーションの開発
大規模データ • はてなブックマーク mysql> select count(*) from relword; +----------+ | count(*) | +----------+ | 51780147 | +----------+ 1 row in set (0.00 sec)
はてなブックマークのデータ規模 • レコ―ド数 • 1,073万エントリー • 3,134万ブックマーク • 4,743万タグ • データサイズ • エントリー 2.5GB • ブックマーク 4GB • タグ 3.4GB • HTML 100GB超
大規模データへのクエリ mysql> select url from entry use index(hoge) where eid = 9615899; ... 200秒待っても結果が返って来ない
大規模データの難しい所 • メモリ内で計算できない
メモリとディスクの速度差 • メモリはディスクの100倍以上高速 • メモリ 7 .5 GB/sec • ディスク 58MB/sec % sudo /sbin/hdparm -tT /dev/sda /dev/sda: Timing cached reads: 15012 MB in 1.99 seconds = 7525.03 MB/sec Timing buffered disk reads: 176 MB in 3.02 seconds = 58.37 MB/sec
スケーリングの要所 • CPU 負荷のスケーリングは簡単 • 同じ構成のサーバーを増やす、LB で分散 • Web, APサ―バ, クローラー • I/O 負荷のスケーリングは難しい • 大規模データ • データベース
大規模データを扱うコツ • いかにしてメモリで済ませるか • 局所性を活かした分散 • データ量の増加に強いアルゴリズム、データ構造 • 例: 線形探索 → 二分探索 • O (n) → O (log n) • 圧縮、情報検索技術
大規模データを前に知っておくべき事 • OS のキャッシュ層 • 分散を考慮した RDBMS の運用 • アルゴリズムとデータ構造
メモリとディスク • メモリとディスクの速度は 150倍 • メモリを使ってディスクアクセスを減らす
OS のキャッシュ • Linux のページキャッシュの特性
Linux (x86) のページング機構 • 仮想メモリ機構の基盤 • 論理的なリニアアドレスを物理的な物理アドレスへ変換 0xbffff444 リニアアドレス ページング機構 (MMU) 物理アドレス 0x00002123
Linux (x86) のページ • フラットメモリモデル • ページ = 仮想メモリの最小単位 • 4kb の構造体 • ページキャッシュ = カーネルバッファに残った page構造体
Linux のページキャッシュとディスク • ディスクの内容をメモリに読み込む • ページが作成される • 作成したページは破棄せずに残す = ページキャッシュ • 例外を除きすべての I/O に透過的に作用する • ディスクのキャッシュを担う箇所 ... VFS
VFS vfs ext2 ext3 ext4 xfs tmpfs デバイスドライバ
VFSの役割 • (1) ファイルシステム実装の抽象化 • (2) パフォーマンス • ページキャッシュ
VFS データ構造関係図 superblock 1 inode (1) inode番号 inode * inode dentry file 1 1 1 1 1 1 address_space 1 (2) offset * page page page
Linux はページ単位でディスクをキャッシュ • ファイルオフセット + iノード番号がインデックス • ファイルの一部をキャッシュできる • address_space → page(s) は Radix Tree • 検索コストはファイルの大きさにほとんど依存しない
キャッシュの単位 • ページ = 仮想メモリの最小単位 • ページキャッシュ ≠ ファイルキャッシュ • ページキャッシュ = カーネルバッファに残った page構造体
メモリが空いていればキャッシュ • 制限なし • sar –r で確認 % sar -r 1 10000 Linux 2.6.11-co-0.6.4 (colinux) 05/28/07 19:50:32 kbmemfree kbmemused %memused kbbuffers kbcached kbswpfree kbswpused %swpused kbswpcad 19:50:33 5800 1005888 99.43 28244 694088 262132 4 0.00 0 19:50:34 5800 1005888 99.43 28244 694088 262132 4 0.00 0 19:50:35 5800 1005888 99.43 28244 694088 262132 4 0.00 0 19:50:36 5800 1005888 99.43 28244 694088 262132 4 0.00 0
メモリを増やすことで I/O 負荷軽減 メモリ 4GB 14:10:01 CPU %user %nice %system %iowait %idle 14:20:01 all 8.58 0.00 5.84 16.58 69.00 14:30:01 all 7.41 0.00 5.14 17.81 69.63 14:40:01 all 7.74 0.00 4.97 18.56 68.73 14:50:01 all 7.02 0.00 5.01 16.24 71.72 メモリ 8GB 14:10:01 CPU %user %nice %system %iowait %idle 14:10:01 all 18.16 0.00 11.56 0.80 69.49 14:20:01 all 12.48 0.00 9.47 0.88 77.17 14:30:01 all 14.20 0.00 10.17 0.91 74.72 14:40:01 all 13.25 0.00 9.74 0.75 76.25
透過的に作用する OS起動直後に数GBのファイルを read した結果 18:20:01 kbmemfree kbmemused %memused kbbuffers kbcached kbswpfree kbswpused %swpused kbswpcad 18:30:01 3566992 157272 4.22 11224 50136 2048276 0 0.00 0 18:40:01 3546264 178000 4.78 12752 66548 2048276 0 0.00 0 18:50:01 112628 3611636 96.98 4312 3499144 2048232 44 0.00 44
キャッシュを前提にした I/O 軽減策 • データ規模 < 物理メモリなら全てキャッシュできる • 経済的コストとのバランスを考慮 • 現状のコモディティ: 8GB ~ 16GB
キャッシュ仕切れない規模になったら • 複数サーバーにスケールさせる • ただし単純に増やさない • CPU 負荷分散では単純に増やす • I/O分散では局所性を考慮する • 自前でインデックスを作る
単純に台数を増やす場合 • キャッシュできない割合は相変わらずそのまま • すぐに再度ボトルネックに コピー
局所性を考慮した分散 • アクセスパターンを考慮した分散 • キャッシュできない箇所がなくなる • メモリはディスクの 150倍 アクセスパターンB アクセスパターンA
具体的には • RDBMS のテーブル単位での分割 • パーティショニング • 検索のインデクスを辞書の途中で分割する • A ~ E まではサーバ A • F ~ I まではサーバB • ... • 用途ごとにシステムを「島」に分ける
リクエストパターンで「島」に分割 proxy proxy 画像API etc. bot / feed 通常のリクエスト AP DB
ページキャッシュを考慮した運用 • OS 起動後すぐにサーバを投入しない • 性能評価はキャッシュが最適化された時に • 分散は局所性を考慮して • データ規模に合わせて搭載メモリを調整する • メモリ増設で対応しきれないなら分散
MySQL 運用のポイント • OS のキャッシュを活かす • インデックスを適切に設定する • スケーリングを前提とした設計
OS のキャッシュを活かす • 全データサイズに気を配る • データ量 < 物理メモリ を維持 • メモリが足りない場合は増設 etc.
インデックス重要 • インデックス = 索引 • B木 • O(n) → O(log n)
インデックスの効果 • 例 : 4,000万件のタグデーブルからの検索 • インデックスなし = 線形探索 → O(n) → 最大 4,000 万回の探索 • インデックスあり = B木で二分探索 → O(log n) → log24000万 = 最大 25.25 回
インデックスの効果の例 mysql> select url from entry where eid = 9615899; +------------------------------------------------------------------------------+ | url | +------------------------------------------------------------------------------+ | http://builder.japan.zdnet.com/member/u87200/blog/2008/08/10/entry_27012867/ | +------------------------------------------------------------------------------+ 1 row in set (0.00 sec) mysql> select url from entry use index(hoge) where eid = 9615899; ... 200秒待っても結果が返って来ない
インデックスの作用 • where、order by、group by の条件 • プライマリキー、UNIQUE 制約 • 明示的に追加したインデックス • 罠 • 複数のカラムに同時にインデックスを効かせたい場合は複合インデックス • select * from entry where url like 'http://d.hatena.nejp/%' order by timestamp
インデックスが効くかどうかの確認 • explain mysql> explain select url from entry where eid = 9615899; +-------+------+---------------+------+---------+-------+------+-------------+ | table | type | possible_keys | key | key_len | ref | rows | Extra | +-------+------+---------------+------+---------+-------+------+-------------+ | entry | ref | eid | eid | 4 | const | 1 | Using where | +-------+------+---------------+------+---------+-------+------+-------------+ 1 row in set (0.04 sec) mysql> explain select url from entry use index(cname) where eid = 9615899; +-------+------+---------------+------+---------+------+---------+-------------+ | table | type | possible_keys | key | key_len | ref | rows | Extra | +-------+------+---------------+------+---------+------+---------+-------------+ | entry | ALL | NULL | NULL | NULL | NULL | 9620451 | Using where | +-------+------+---------------+------+---------+------+---------+-------------+ 1 row in set (0.01 sec)
MySQL の分散 • マスタ・スレーブ • 参照系はスレーブへ、更新はマスタへ • ORマッパで制御する アプリケーション サーバー アプリケーション サーバー アプリケーション サーバー アプリケーション サーバー ロードバランサ DBスレーブ DBスレーブ DBスレーブ DBマスタ
マスタ・スレーブの特徴 • 参照系クエリはスケール • サーバーを増やすだけで良い • ただし、台数を稼ぐことよりもメモリにフィットさせることが重要 • マスタはスケールしない • 更新系クエリが増えると厳しい • ただし、Web アプリは多くの場合 90%以上が参照クエリ • マスタ負荷はテーブル分割で凌ぐのが現状のセオリー
MySQL のスケールアウト戦略 • データがメモリに載るサイズ? • YES → メモリに載せる • NO • メモリ増設 • メモリ増設が不可能ならパーティショニング
パーティショニング (テーブル分割) • テーブルA とテーブルB を別のサーバーに置いて分散する方法
パーティショニング • テーブル単位での分割 • 特定のアルゴリズムでの分割 • 例1. 頭文字 a-d が A、頭文字 e-h が B ... • 例2. ハッシュ関数 A B A B
パーティショニングはなぜ効果的か • 局所性
パーティショニングを前提にした設計 • JOIN を使わない • RDBMS 屋には叱られるがしょうがない
INNER JOIN している SQL • entry has many bookmarks mysql> select url from entry INNER JOIN bookmark on entry.eid = bookmark.eid -> where bookmark.uid = 169848 limit 5; +-------------------------------------------------------------------+ | url | +-------------------------------------------------------------------+ | http://blog.bulknews.net/mt/archives/001537.html | | http://www.wrightthisway.com/Articles/000154.html | | http://internet.watch.impress.co.jp/cda/news/2005/02/10/6438.html | | http://headlines.yahoo.co.jp/hl?a=20050210-00000136-kyodo-bus_all | | http://headlines.yahoo.co.jp/hl?a=20050210-00000015-maip-soci | +-------------------------------------------------------------------+
JOIN を排除 where ... in ... を利用 mysql> select eid from bookmark where uid = 169848 limit 5; +-----+ | eid | +-----+ | 0 | | 4 | | 5 | | 6 | | 7 | +-----+ 5 rows in set (0.01 sec) mysql> select url from entry where eid in (0, 4, 5, 6, 7); +-------------------------------------------------------------------+ | url | +-------------------------------------------------------------------+ | http://blog.bulknews.net/mt/archives/001537.html | | http://www.wrightthisway.com/Articles/000154.html | | http://internet.watch.impress.co.jp/cda/news/2005/02/10/6438.html | | http://headlines.yahoo.co.jp/hl?a=20050210-00000136-kyodo-bus_all | +-------------------------------------------------------------------+ 4 rows in set (0.12 sec)