HandlerSocket plugin for MySQL
- 1. handlersocket plugin for mysql 2010/06/29 Tech セミナー @ 代々木 株式会社 DeNA システム統括本部 IT 基盤部 樋口 証 <higuchi dot akira at dena dot jp>
- 2. Who am I? DeNA IT 基盤部 システムのパフォーマンス最適化 障害の分析 ミドルウェア開発 IPA 未踏 スーパークリエータ (2005 年 ) 1993 年ころから GNU/Linux 利用 Fedora: yum install KoboDeluxe Debian: apt-get install kobodeluxe サーバソフトウェアを多数開発
- 5. ねらい 単純な CRUD 処理を高速に実行したい。 SQL 処理を省略 単純な処理に適用可能な最適化 しかも同じデータを MySQL でも処理できるようにしたい。 単純かつ速度が必要な部分だけを非 SQL に置き換えたい SQL から少しずつ移行したい
- 6. handlersocket plugin の概要 InnoDB 等のストレージエンジンへの非 SQL インタフェースを提供 TCP/IP でリクエストを受け、ストレージエンジンを直接叩く 独自プロトコルを喋る C++ と Perl のクライアントライブラリを用意 Linux 専用 配布場所は DeNA 技術ブログで案内します http://engineer.dena.jp/
- 7. 構成 mysqld client app Handler Interface Innodb MyISAM Other storage engines … SQL Layer Handlersocket Plugin Listener for libmysql libmysql libhsclient Applications
- 8. 参考 : 非 SQL for MySQL mycached http://developer.cybozu.co.jp/kazuho/2009/08/mycached-memcac.html handlersocket と同様に handler インタフェースを叩く memcached プロトコルを喋る NDB API http://dev.mysql.com/doc/ndbapi/en/index.html ndbcluster 専用 handlersocket より下の層 ( ストレージエンジン内部 ) を叩く
- 11. handlersocket の機能 ( 参照系 ) 擬似 SQL で書くと… SELECT f1,..,fn FROM db.table WHERE k1,…,km = v1,…,vm ORDER BY index_i LIMIT offset, limit (k1,…,km) は index_i のキー列 ( 又はその prefix) WHERE 句で索引に入っていない列についての条件でフィルタすることはできない いずれサポートしたい 比較条件に使える演算子は =, >=, >, <=, < 索引を使った検索のみサポートする MySQL の HANDLER クエリに近い
- 12. handlersocket の機能 ( 更新系 ) 参照クエリで得た行の UPDATE と DELETE 行の INSERT トランザクションはサポートしない 更新系クエリは row-based の形式でバイナリログに記録される 書き込みは durable
- 13. 実行例 create table db1.table1 (k int key, v char(20)) insert into db1.table1 values (234, 'foo'), (678, ‘bar’) $ 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 を検索
- 16. oprofile – libmysql/mysqld SELECT v from table where k = ? を大量に実行し、 oprofile で CPU 消費量をしらべる 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. oprofile – libmysql/mysqld mysqld 内の CPU 消費 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. oprofile – libmysql/mysqld カーネル内の CPU 消費 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
- 20. oprofile – handlersocket 先の SQL クエリと同等の処理を handlersocket で実行し、 oprofile で CPU 消費量をしらべる 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. oprofile – handlersocket mysqld 内の CPU 消費 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. oprofile – handlersocket カーネル内の CPU 消費 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. oprofile – handlersocket handlersocket 使用時の CPU 消費 : カーネルが最も多く CPU を使用 カーネル内では network 周りで CPU 使用 mysqld 内では innodb の中が使用量多い 特に adaptive hash index 検索処理
- 29. 更新処理の性能について 前提 : 同期書き込み (durable) sync_binlog = 1 innodb_flush_log_at_trx_commit = 1 innodb_support_xa = 1 バッテリ付き write-back cache 又は SSD 性能 : mysql: ~ 1000 qps innodb plugin で sync_binlog = 0 だと group commit が効いてさらに速くなるが、スレーブが追いつかない handlersocket: ~ 30000 qps 書き込みはシリアライズされるので、常にスレーブが追いつく
- 30. handlersocket の排他制御 MyISAM ならテーブルロックがかかる shared-exclusive lock InnoDB ならテーブルロックはかからない。ただし更新系トランザクションは一度に一つだけ実行されるよう排他制御される。 そうしないとレコードロックを持ち合ってデッドロックするから 更新系スレッドは参照系スレッドをブロックしない handlersocket のリクエスト自体はデッドロックフリー そもそも単純なリクエストしかサポートしていないので。
- 32. C/S プロトコル比較 write(3, "Lselect column0,column1,column2,column3,column4 from db_1.table_1 where k=15", 80) = 80 read(3, "056def db_1 table_1 table_1 column0 column0 <7500006def db_1 table_1 table_1 column1 column1 <7500006def db_1 table_1 table_1 column2 column2 <7500006def db_1 table_1 table_1 column3 column3 <7500006def db_1 table_1 table_1 column4 column4 <750076amp;quot;001 0 01 1 01 2 01 3 01 4 76amp;quot;", 16384) = 327 libmysql/mysqld でこのクエリを実行すると… SELECT column0, column1, column2, column3, column4 FROM db_1.table_1 where k = 15
- 33. C/S プロトコル比較 write(3, "1=115", 9) = 9 read(3, "0501234", 8192) = 14 handlersocket で同等のクエリを実行すると… 14 bytes 327 bytes response 9 bytes 80 bytes request handlersocket libmysql
- 34. mysqld/libmysql のプロトコル 結果セットのメタデータが大きい 各列について、 DB 名 、 テーブル名 、 テーブル別名 、 列名 、 列の別名 がメタデータに含まれる http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol#Field_Packet mysql 4.0 までのプロトコルではメタデータが少し小さい。 4.1 以降で大きくなった。 列が多く行が少ないとき相対的に大きい メタデータは結果セットに一つだけ付くから HANDLER クエリでも同様にメタデータが付く server-side prepared statement を使っても同様 プロトコル圧縮を使うと 1/3 程度になる。ただし CPU を喰う。
- 37. DB::HandlerSocket Perl 用クライアントライブラリ xs 経由で libhsclient を呼んでいる 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 ], ]);
- 39. handlersocket の設定 handlersocket_threads = 16 参照系ワーカスレッド数 CPU コア数の 2 倍程度を推奨 handlersocket_thread_wr = 1 更新系ワーカスレッド数 増やしても余りメリットなさそう handlersocket_port = 9998 参照系ワーカスレッド用ポート handlersocket_port_wr = 9999 更新系ワーカスレッド用ポート
- 40. その他の設定 innodb_buffer_pool_size を限界まで大きく それでも参照が Disk ネックになるならデータ分割 innodb_log_file_size, innodb_log_files_in_group 大きいとテーブルスペースへの書き戻しが減る 更新系を速くしたいなら限界まで大きく innodb_thread_concurrency = 0 innodb 内の並列度の上限。 0 にすると無効。 open_files_limit = 65535 mysql が開けるファイルディスクリプタ数 handlersocket 接続一本あたり 1 つ消費するので大きく
- 42. durability に関連する設定 sync_binlog = 1 binlog を同期書き込み innodb_flush_log_at_trx_commit = 1 innodb の WAL を同期書き込み innodb_support_xa = 1 内部的に XA を使って binlog と innodb を同期
- 44. ベンチマーク サーバ Core2Quad Q6600 CentOS 5.4 EXPI9301CT(e1000e) Intel X25-E (write-back cache disabled) クライアントとは 1000base 接続 クライアントのほうが高速のため、全てのワークロードでクライアント側はボトルネックになっていない スキーマ : CREATE TABLE table1 (k varchar(32) KEY, v varchar(32)) engine = INNODB; read テスト : 1000 万レコード SELECT v from table1 where k = ? ランダムアクセスするようキーは乱数で生成 write テスト : 1000 万レコード UPDATE table SET v = ? where k = ? ランダムアクセスするようキーは乱数で生成 binlog 有効 動機的 (durable) 書き込み
- 50. 課題 ビルドが多少面倒 mysql のソースコードがビルド時に必要 mysql バイナリ互換性問題 mysql のバージョンやビルドオプションの違いによって plugin のバイナリ互換性が無くなる
- 51. 今後の予定 (?) where 句のようなフィルタのサポート 不可分な read-modify-write 操作のサポート SQL 実行機能? (stateless なクエリのみサポート ) InnoDB 以外のエンジンでの検証 クライアントライブラリの整備