HandlerSocket plugin for MySQL

35,661 views

Published on

Published in: Technology
2 Comments
63 Likes
Statistics
Notes
No Downloads
Views
Total views
35,661
On SlideShare
0
From Embeds
0
Number of Embeds
3,984
Actions
Shares
0
Downloads
321
Comments
2
Likes
63
Embeds 0
No embeds

No notes for slide

HandlerSocket plugin for MySQL

  1. 1. handlersocket plugin for mysql 2010/06/29 Tech セミナー @ 代々木 株式会社 DeNA システム統括本部 IT 基盤部 樋口 証 <higuchi dot akira at dena dot jp>
  2. 2. Who am I? <ul><li>DeNA IT 基盤部 </li></ul><ul><ul><li>システムのパフォーマンス最適化 </li></ul></ul><ul><ul><li>障害の分析 </li></ul></ul><ul><ul><li>ミドルウェア開発 </li></ul></ul><ul><li>IPA 未踏 スーパークリエータ (2005 年 ) </li></ul><ul><li>1993 年ころから GNU/Linux 利用 </li></ul><ul><ul><li>Fedora: yum install KoboDeluxe </li></ul></ul><ul><ul><li>Debian: apt-get install kobodeluxe </li></ul></ul><ul><li>サーバソフトウェアを多数開発 </li></ul>
  3. 3. handlersocket plugin について
  4. 4. handlersocket plugin とは <ul><li>MySQL の非 SQL なインタフェース </li></ul>
  5. 5. ねらい <ul><li>単純な CRUD 処理を高速に実行したい。 </li></ul><ul><ul><li>SQL 処理を省略 </li></ul></ul><ul><ul><li>単純な処理に適用可能な最適化 </li></ul></ul><ul><li>しかも同じデータを MySQL でも処理できるようにしたい。 </li></ul><ul><ul><li>単純かつ速度が必要な部分だけを非 SQL に置き換えたい </li></ul></ul><ul><ul><li>SQL から少しずつ移行したい </li></ul></ul>
  6. 6. handlersocket plugin の概要 <ul><li>InnoDB 等のストレージエンジンへの非 SQL インタフェースを提供 </li></ul><ul><li>TCP/IP でリクエストを受け、ストレージエンジンを直接叩く </li></ul><ul><li>独自プロトコルを喋る </li></ul><ul><li>C++ と Perl のクライアントライブラリを用意 </li></ul><ul><li>Linux 専用 </li></ul><ul><li>配布場所は DeNA 技術ブログで案内します </li></ul><ul><ul><li>http://engineer.dena.jp/ </li></ul></ul>
  7. 7. 構成 mysqld client app Handler Interface Innodb MyISAM Other storage engines … SQL Layer Handlersocket Plugin Listener for libmysql libmysql libhsclient Applications
  8. 8. 参考 : 非 SQL for MySQL <ul><li>mycached </li></ul><ul><ul><li>http://developer.cybozu.co.jp/kazuho/2009/08/mycached-memcac.html </li></ul></ul><ul><ul><li>handlersocket と同様に handler インタフェースを叩く </li></ul></ul><ul><ul><li>memcached プロトコルを喋る </li></ul></ul><ul><li>NDB API </li></ul><ul><ul><li>http://dev.mysql.com/doc/ndbapi/en/index.html </li></ul></ul><ul><ul><li>ndbcluster 専用 </li></ul></ul><ul><ul><li>handlersocket より下の層 ( ストレージエンジン内部 ) を叩く </li></ul></ul>
  9. 9. 性能
  10. 10. 性能比較 ( 参照クエリ ) <ul><li>単純な参照クエリで数倍~ 10 倍程度 </li></ul><ul><li>取得する列数が多い場合に特に有効 </li></ul>
  11. 11. handlersocket の機能 ( 参照系 ) <ul><li>擬似 SQL で書くと… </li></ul><ul><ul><li>SELECT f1,..,fn FROM db.table </li></ul></ul><ul><ul><li>WHERE k1,…,km = v1,…,vm </li></ul></ul><ul><ul><li>ORDER BY index_i LIMIT offset, limit </li></ul></ul><ul><ul><li>(k1,…,km) は index_i のキー列 ( 又はその prefix) </li></ul></ul><ul><ul><li>WHERE 句で索引に入っていない列についての条件でフィルタすることはできない </li></ul></ul><ul><ul><ul><li>いずれサポートしたい </li></ul></ul></ul><ul><ul><li>比較条件に使える演算子は =, >=, >, <=, < </li></ul></ul><ul><li>索引を使った検索のみサポートする </li></ul><ul><ul><li>MySQL の HANDLER クエリに近い </li></ul></ul>
  12. 12. handlersocket の機能 ( 更新系 ) <ul><li>参照クエリで得た行の UPDATE と DELETE </li></ul><ul><li>行の INSERT </li></ul><ul><li>トランザクションはサポートしない </li></ul><ul><li>更新系クエリは row-based の形式でバイナリログに記録される </li></ul><ul><li>書き込みは durable </li></ul>
  13. 13. 実行例 <ul><li>create table db1.table1 (k int key, v char(20)) </li></ul><ul><li>insert into db1.table1 values (234, 'foo'), (678, ‘bar’) </li></ul>$ telnet localhost 9998 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. P 0 db1 table1 PRIMARY k,v 0 1 0 = 1 234 0 2 234 foo 0 = 1 678 0 2 678 bar db1.table1 の PK を開く k = 234 を検索 k = 678 を検索
  14. 14. 何故速くなるのか <ul><li>SQL パース処理をしていない </li></ul><ul><ul><li>-> CPU 消費が少ない </li></ul></ul><ul><li>リクエストを集約実行している </li></ul><ul><ul><li>-> CPU 消費やディスク IO が少ない </li></ul></ul><ul><li>独自プロトコルで通信する </li></ul><ul><ul><li>-> ネットワーク消費が少ない </li></ul></ul>
  15. 15. CPU 消費の削減
  16. 16. oprofile – libmysql/mysqld <ul><li>SELECT v from table where k = ?  を大量に実行し、 oprofile で CPU 消費量をしらべる </li></ul>samples| %| ------------------ 9669940 53.1574 mysqld 4438098 24.3970 vmlinux 1835976 10.0927 libpthread-2.5.so 1680656 9.2389 libc-2.5.so 397970 2.1877 e1000e 89136 0.4900 oprofiled 42881 0.2357 oprofile
  17. 17. oprofile – libmysql/mysqld <ul><li>mysqld 内の CPU 消費 </li></ul>samples % symbol name 748022 7.7355 MYSQLparse(void*) 219702 2.2720 my_pthread_fastmutex_lock 205606 2.1262 make_join_statistics(JOIN*, TABLE_LIST*, 198234 2.0500 btr_search_guess_on_hash 180731 1.8690 JOIN::optimize() 177120 1.8317 row_search_for_mysql 171185 1.7703 lex_one_token(void*, void*) 162683 1.6824 alloc_root 131823 1.3632 read_view_open_now 122795 1.2699 mysql_select(THD*, Item***, TABLE_LIST*, 100276 1.0370 open_table(THD*, TABLE_LIST*, st_mem_root*, 99575 1.0297 mem_pool_fill_free_list 96434 0.9973 build_template(row_prebuilt_struct*, THD*, 86349 0.8930 get_hash_symbol(char const*, unsigned int,
  18. 18. oprofile – libmysql/mysqld <ul><li>カーネル内の CPU 消費 </li></ul>samples % symbol name 204393 4.6054 schedule 118648 2.6734 tcp_sendmsg 115832 2.6099 tcp_recvmsg 106537 2.4005 tcp_v4_rcv 103915 2.3414 tcp_ack 103534 2.3328 system_call 93864 2.1150 dev_queue_xmit 86831 1.9565 __mod_timer 85891 1.9353 tcp_rcv_established 84083 1.8946 .text.task_rq_lock
  19. 19. oprofile – libmysql/mysqld <ul><li>libmysql/mysqld 使用時の CPU 消費 : </li></ul><ul><ul><li>mysqld 内で多くの CPU 時間を使用 </li></ul></ul><ul><ul><li>特に SQL パース処理周辺が CPU 使用 </li></ul></ul><ul><ul><li>カーネル内ではタスク切り替えに CPU 使用 </li></ul></ul>
  20. 20. oprofile – handlersocket <ul><li>先の SQL クエリと同等の処理を handlersocket で実行し、 oprofile で CPU 消費量をしらべる </li></ul>samples| %| ------------------ 1919039 51.0453 vmlinux 811998 21.5987 mysqld 421215 11.2041 libpthread-2.5.so 207166 5.5105 e1000e 191566 5.0955 handlersocket.so 188618 5.0171 libc-2.5.so 13622 0.3623 oprofiled 5707 0.1518 oprofile
  21. 21. oprofile – handlersocket <ul><li>mysqld 内の CPU 消費 </li></ul>samples % symbol name 119684 14.7394 btr_search_guess_on_hash 58202 7.1678 row_search_for_mysql 46946 5.7815 mutex_delay 38617 4.7558 my_pthread_fastmutex_lock 37707 4.6437 buf_page_get_known_nowait 36528 4.4985 rec_get_offsets_func 34625 4.2642 build_template(row_prebuilt_struct*, THD*, TABLE*, 20024 2.4660 row_sel_store_mysql_rec 19347 2.3826 btr_cur_search_to_nth_level 16701 2.0568 row_sel_convert_mysql_key_to_innobase 13343 1.6432 cmp_dtuple_rec_with_match 11381 1.4016 ha_innobase::index_read(unsigned char*, 11176 1.3764 dict_index_copy_types 10762 1.3254 mtr_memo_slot_release 10734 1.3219 ha_innobase::init_table_handle_for_HANDLER()
  22. 22. oprofile – handlersocket <ul><li>カーネル内の CPU 消費 </li></ul>samples % symbol name 129038 6.7241 tcp_sendmsg 80080 4.1729 tcp_v4_rcv 69658 3.6298 dev_queue_xmit 66171 3.4481 .text.skb_release_data 63316 3.2994 __qdisc_run 60279 3.1411 tcp_recvmsg 59703 3.1111 ip_output 58462 3.0464 .text.skb_release_head_state 48876 2.5469 tcp_ack 48733 2.5394 __alloc_skb 45660 2.3793 ip_queue_xmit 44671 2.3278 tcp_transmit_skb
  23. 23. oprofile – handlersocket <ul><li>handlersocket 使用時の CPU 消費 : </li></ul><ul><ul><li>カーネルが最も多く CPU を使用 </li></ul></ul><ul><ul><li>カーネル内では network 周りで CPU 使用 </li></ul></ul><ul><ul><li>mysqld 内では innodb の中が使用量多い </li></ul></ul><ul><ul><li>特に adaptive hash index 検索処理 </li></ul></ul>
  24. 24. 並列処理の最適化
  25. 25. 並列処理モデル比較 <ul><li>mysqld の並列処理モデル : </li></ul><ul><ul><li>MySQL 5 では接続あたり 1 スレッド </li></ul></ul><ul><ul><li>MySQL 6 でスレッドプールもサポート </li></ul></ul><ul><ul><ul><li>SQL 実行を少数スレッドで実行 </li></ul></ul></ul><ul><ul><ul><li>メモリ消費の上限が抑えられる </li></ul></ul></ul>
  26. 26. 並列処理モデル比較 <ul><li>handlersocket の並列処理モデル : </li></ul><ul><ul><li>少数のスレッド </li></ul></ul><ul><ul><li>各スレッドは複数の接続を処理 </li></ul></ul><ul><ul><ul><li>既定では epoll 使用 </li></ul></ul></ul><ul><ul><ul><li>同時接続数は事実上無制限 </li></ul></ul></ul><ul><ul><ul><li>メモリ消費が少ない </li></ul></ul></ul>
  27. 27. handlersocket スレッドの動作 各接続からリクエストを読む DB ロック、 readview 取得 各接続からのリクエストを実行 DB ロック解除 各接続へ結果を返す ここの処理が (1/ 接続数 ) 回 程度で済む handlersocket の参照系ワーカースレッド の動作
  28. 28. handlersocket スレッドの動作 各接続からリクエストを読む DB ロック、トランザクション開始 各接続からのリクエストを実行 コミット、 DB ロック解除 各接続へ結果を返す handlersocket の更新系ワーカースレッド の動作 複数のリクエストを一つの トランザクション内で実行
  29. 29. 更新処理の性能について <ul><li>前提 : </li></ul><ul><ul><li>同期書き込み (durable) </li></ul></ul><ul><ul><ul><li>sync_binlog = 1 </li></ul></ul></ul><ul><ul><ul><li>innodb_flush_log_at_trx_commit = 1 </li></ul></ul></ul><ul><ul><ul><li>innodb_support_xa = 1 </li></ul></ul></ul><ul><ul><li>バッテリ付き write-back cache 又は SSD </li></ul></ul><ul><li>性能 : </li></ul><ul><ul><li>mysql: ~ 1000 qps </li></ul></ul><ul><ul><ul><li>innodb plugin で sync_binlog = 0 だと group commit が効いてさらに速くなるが、スレーブが追いつかない </li></ul></ul></ul><ul><ul><li>handlersocket: ~ 30000 qps </li></ul></ul><ul><ul><ul><li>書き込みはシリアライズされるので、常にスレーブが追いつく </li></ul></ul></ul>
  30. 30. handlersocket の排他制御 <ul><li>MyISAM ならテーブルロックがかかる </li></ul><ul><ul><li>shared-exclusive lock </li></ul></ul><ul><li>InnoDB ならテーブルロックはかからない。ただし更新系トランザクションは一度に一つだけ実行されるよう排他制御される。 </li></ul><ul><ul><li>そうしないとレコードロックを持ち合ってデッドロックするから </li></ul></ul><ul><ul><li>更新系スレッドは参照系スレッドをブロックしない </li></ul></ul><ul><li>handlersocket のリクエスト自体はデッドロックフリー </li></ul><ul><ul><li>そもそも単純なリクエストしかサポートしていないので。 </li></ul></ul>
  31. 31. 通信の最適化
  32. 32. C/S プロトコル比較 <ul><li>write(3, &quot;L3select column0,column1,column2,column3,column4 from db_1.table_1 where k=15&quot;, 80) = 80 </li></ul><ul><li>read(3, &quot;1105623def4 db_1 7 table_1 7 table_1 7 column0 7 column0 f <37520000633def4 db_1 7 table_1 7 table_1 7 column1 7 column1 f <37520000643def4 db_1 7 table_1 7 table_1 7 column2 7 column2 f <37520000653def4 db_1 7 table_1 7 table_1 7 column3 7 column3 f <37520000663def4 db_1 7 table_1 7 table_1 7 column4 7 column4 f <37520057376&quot; 1001 0 01 1 01 2 01 3 01 4 5 376&quot;&quot;, 16384) = 327 </li></ul>libmysql/mysqld でこのクエリを実行すると… SELECT column0, column1, column2, column3, column4 FROM db_1.table_1 where k = 15
  33. 33. C/S プロトコル比較 write(3, &quot;1 = 1 15 &quot;, 9) = 9 read(3, &quot;0 5 0 1 2 3 4 &quot;, 8192) = 14 handlersocket で同等のクエリを実行すると… 14 bytes 327 bytes response 9 bytes 80 bytes request handlersocket libmysql
  34. 34. mysqld/libmysql のプロトコル <ul><li>結果セットのメタデータが大きい </li></ul><ul><ul><li>各列について、 DB 名 、 テーブル名 、 テーブル別名 、 列名 、 列の別名 がメタデータに含まれる </li></ul></ul><ul><ul><li>http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol#Field_Packet </li></ul></ul><ul><ul><li>mysql 4.0 までのプロトコルではメタデータが少し小さい。 4.1 以降で大きくなった。 </li></ul></ul><ul><li>列が多く行が少ないとき相対的に大きい </li></ul><ul><ul><li>メタデータは結果セットに一つだけ付くから </li></ul></ul><ul><li>HANDLER クエリでも同様にメタデータが付く </li></ul><ul><li>server-side prepared statement を使っても同様 </li></ul><ul><li>プロトコル圧縮を使うと 1/3 程度になる。ただし CPU を喰う。 </li></ul>
  35. 35. クライアントライブラリ
  36. 36. libhsclient <ul><li>handlersocket 用クライアントライブラリ </li></ul><ul><ul><li>C++ </li></ul></ul><ul><ul><li>低水準インタフェース </li></ul></ul>
  37. 37. DB::HandlerSocket <ul><li>Perl 用クライアントライブラリ </li></ul><ul><ul><li>xs 経由で libhsclient を呼んでいる </li></ul></ul>my $cli = new DB::HandlerSocket( {host => ‘localhost’, port => 9999}); $cli->open_index(1, ‘db1’, ‘table1’, ‘PRIMARY’, ‘k,v’); my $res = $cli->exec_multi([ [ 1, ‘=‘, [ ’33’ ], 1, 0 ], [ 1, ‘=‘, [ ’44’ ], 1, 0, ‘U’, [ ’44’, ‘hoge’ ] ], [ 1, ‘>=‘, [ ’55’ ], 10, 20 ], ]);
  38. 38. 設定ガイド
  39. 39. handlersocket の設定 <ul><li>handlersocket_threads = 16 </li></ul><ul><ul><li>参照系ワーカスレッド数 </li></ul></ul><ul><ul><li>CPU コア数の 2 倍程度を推奨 </li></ul></ul><ul><li>handlersocket_thread_wr = 1 </li></ul><ul><ul><li>更新系ワーカスレッド数 </li></ul></ul><ul><ul><li>増やしても余りメリットなさそう </li></ul></ul><ul><li>handlersocket_port = 9998 </li></ul><ul><ul><li>参照系ワーカスレッド用ポート </li></ul></ul><ul><li>handlersocket_port_wr = 9999 </li></ul><ul><ul><li>更新系ワーカスレッド用ポート </li></ul></ul>
  40. 40. その他の設定 <ul><li>innodb_buffer_pool_size を限界まで大きく </li></ul><ul><ul><li>それでも参照が Disk ネックになるならデータ分割 </li></ul></ul><ul><li>innodb_log_file_size, innodb_log_files_in_group </li></ul><ul><ul><li>大きいとテーブルスペースへの書き戻しが減る </li></ul></ul><ul><ul><li>更新系を速くしたいなら限界まで大きく </li></ul></ul><ul><li>innodb_thread_concurrency = 0 </li></ul><ul><ul><li>innodb 内の並列度の上限。 0 にすると無効。 </li></ul></ul><ul><li>open_files_limit = 65535 </li></ul><ul><ul><li>mysql が開けるファイルディスクリプタ数 </li></ul></ul><ul><ul><li>handlersocket 接続一本あたり 1 つ消費するので大きく </li></ul></ul>
  41. 41. その他の設定 <ul><li>innodb_adaptive_hash_index = 1 </li></ul><ul><ul><li>B-Tree に加えて Hash 索引を on-demand に付ける機能 </li></ul></ul><ul><ul><li>完全に Hash に乗れば handlersocket 経由の参照が~ 30% 程度速くなる </li></ul></ul><ul><ul><li>Hash 索引作成中は一時的に参照が遅くなる </li></ul></ul><ul><ul><li>メモリを喰うのが嫌なら無効にしたほうがよい </li></ul></ul>
  42. 42. durability に関連する設定 <ul><li>sync_binlog = 1 </li></ul><ul><ul><li>binlog を同期書き込み </li></ul></ul><ul><li>innodb_flush_log_at_trx_commit = 1 </li></ul><ul><ul><li>innodb の WAL を同期書き込み </li></ul></ul><ul><li>innodb_support_xa = 1 </li></ul><ul><ul><li>内部的に XA を使って binlog と innodb を同期 </li></ul></ul>
  43. 43. ベンチマーク
  44. 44. ベンチマーク <ul><li>サーバ </li></ul><ul><ul><li>Core2Quad Q6600 </li></ul></ul><ul><ul><li>CentOS 5.4 </li></ul></ul><ul><ul><li>EXPI9301CT(e1000e) </li></ul></ul><ul><ul><li>Intel X25-E (write-back cache disabled) </li></ul></ul><ul><li>クライアントとは 1000base 接続 </li></ul><ul><ul><li>クライアントのほうが高速のため、全てのワークロードでクライアント側はボトルネックになっていない </li></ul></ul><ul><li>スキーマ : </li></ul><ul><ul><li>CREATE TABLE table1 (k varchar(32) KEY, v varchar(32)) engine = INNODB; </li></ul></ul><ul><li>read テスト : </li></ul><ul><ul><li>1000 万レコード </li></ul></ul><ul><ul><li>SELECT v from table1 where k = ? </li></ul></ul><ul><ul><li>ランダムアクセスするようキーは乱数で生成 </li></ul></ul><ul><li>write テスト : </li></ul><ul><ul><li>1000 万レコード </li></ul></ul><ul><ul><li>UPDATE table SET v = ? where k = ? </li></ul></ul><ul><ul><li>ランダムアクセスするようキーは乱数で生成 </li></ul></ul><ul><ul><li>binlog 有効 </li></ul></ul><ul><ul><li>動機的 (durable) 書き込み </li></ul></ul>
  45. 45. スループット
  46. 46. スループット ( 書き込み )
  47. 47. 最大レスポンス時間
  48. 48. 平均レスポンス時間
  49. 49. 今後の予定 (?) と課題
  50. 50. 課題 <ul><li>ビルドが多少面倒 </li></ul><ul><ul><li>mysql のソースコードがビルド時に必要 </li></ul></ul><ul><li>mysql バイナリ互換性問題 </li></ul><ul><ul><li>mysql のバージョンやビルドオプションの違いによって plugin のバイナリ互換性が無くなる </li></ul></ul>
  51. 51. 今後の予定 (?) <ul><li>where 句のようなフィルタのサポート </li></ul><ul><li>不可分な read-modify-write 操作のサポート </li></ul><ul><li>SQL 実行機能? (stateless なクエリのみサポート ) </li></ul><ul><li>InnoDB 以外のエンジンでの検証 </li></ul><ul><li>クライアントライブラリの整備 </li></ul>

×