2.1 QueryCache(クエリーキャッシュ)とは |
「実行されたSELECT文の結果をキャッシュ(query_cache)に保存しておき、同じSELECT文が要求された場合は(検索を行わずに)キャッシュに保存されている結果を返す機能」のこと。
詳しくは次のURLを参照のこと。
クエリーキャッシュ
2.2 動機 |
2.3 結果 |
成功は
私見: 今はLWLockのQueryCacheLocKがかなり足枷(あしかせ)になっているのは事実。 querycache領域についてもっと粒度の細かいLockをかけないと、 せっかくの同時実行性が大きく損なわれている。
しかし、そこを改良していくより、buffer関連のアルゴリズムを改良し、 querycacheに使うメモリをbufferにまわすほうがよいはずだ。
ところで、今回の実装にはいくつか制約がある。
2.4 実行例 |
"sample=# set query_cache_check_level = 4;"で
サーバがQueryCacheの各種情報を標準エラーに表示するようになる。
$ /usr/local/pgsql/bin/psql sample Welcome to psql 8.1.0, the PostgreSQL interactive terminal. Type: \copyright for distribution terms \h for help with SQL commands \? for help with psql commands \g or terminate with semicolon to execute query \q to quit sample=# set query_cache_check_level = 4; SET |
sample=# select * from accounts where aid < 3; aid | bid | abalance | filler -----+-----+----------+---------------------------------------------------------- 1 | 1 | 10 | 2 | 1 | 10 | (2 rows) sample=# select * from accounts where aid < 3; aid | bid | abalance | filler -----+-----+----------+---------------------------------------------------------- 1 | 1 | 10 | 2 | 1 | 10 | (2 rows) |
******************************************* LOG: ******************************************* ============ check_table blocks =========== oid oid/qid (p)prev (p)next qid_num qid_next ------------------------------------------- 1(TABLE) 16389 0 2 2(QUERY) 1 0 0 ============ check_query blocks =========== qid prev next hashkey next last query/data(len) ------------------------------------------- 1(QID) 0 0 369 2 2 select * from accounts where aid < 3; 2(DID) 0 (337) =============== check_header ============== table_block_num = 2, max_table_block_num = 4092 query_block_num = 2, max_query_block_num = 5424 table_free = 3 query_free = 3 -- qc_oid_space ----------------------------- (101) 1 1 -- qc_query_space ----------------------------- (369) 1 1 =============== statistics ============== all queries = 2 store = 1 hits = 0 not_cached = 0 |
2回目のSELECT文では、キャッシュされたデータが返されたことがわかる(statisticsパートのhits = 1の部分)
******************************************* LOG: ******************************************* ============ check_table blocks =========== oid oid/qid (p)prev (p)next qid_num qid_next ------------------------------------------- 1(TABLE) 16389 0 2 2(QUERY) 1 0 0 ============ check_query blocks =========== qid prev next hashkey next last query/data(len) ------------------------------------------- 1(QID) 0 0 369 2 2 select * from accounts where aid < 3; 2(DID) 0 (337) =============== check_header ============== table_block_num = 2, max_table_block_num = 4092 query_block_num = 2, max_query_block_num = 5424 table_free = 3 query_free = 3 -- qc_oid_space ----------------------------- (101) 1 1 -- qc_query_space ----------------------------- (369) 1 1 =============== statistics ============== all queries = 3 store = 1 hits = 1 not_cached = 0 |
次、
UPDATE文を実行する。
sample=# update accounts set abalance = 0 where aid < 3; UPDATE 2 |
すると、いままでキャッシュされていたデータがきれいに(共有)メモリ上から消えていることがわかる。
******************************************* LOG: ******************************************* ============ check_table blocks =========== oid oid/qid (p)prev (p)next qid_num qid_next ------------------------------------------- ============ check_query blocks =========== qid prev next hashkey next last query/data(len) ------------------------------------------- =============== check_header ============== table_block_num = 0, max_table_block_num = 4092 query_block_num = 0, max_query_block_num = 5424 table_free = 1 query_free = 1 -- qc_oid_space ----------------------------- -- qc_query_space ----------------------------- =============== statistics ============== all queries = 4 store = 1 hits = 1 not_cached = 0 |
2.5 実行環境 |
2.5.1 ソース+パッチ
対応バージョンはPostgreSQL 8.1.0。
パッチは必然的にBSDライセンス。
querycache.hはライセンスフリー(煮るなり焼くなり、お好きに)。
querycache.cはインスパイヤライセンス。思い付きで書き殴ったソースで、汚いし未完成だが(正直恥ずかしい)、好きにインスパイヤしてください。
特にelog()挿入は大歓迎。
/*------------------------------------------------------------------------- * * querycache.c * Query Cache for PostgreSQL * * NOTES * * インスパイヤ・ライセンス * * 配布条件: * * 以下に示す条件の下、各自が自由にインスパイヤできる。 * * (1)このヘッダーファイルを必ず添付し、直接的にインスパイヤされたリスペクトファイルと、 * 以下に示すインスパイヤ度を新たに更新する。 * * リスペクト・ファイル www.buena-idea.net/postgresql/querycache/querycache.c * * インスパイヤ度 = max(0, org - dif) / org * * ここで * org => リスペクトファイルの行数 - このヘッダー部分の行数 * dif => 次のスクリプトで得た、追加による変更箇所の行数 * "diff -uibw querycache.c.orig querycache.c | grep "^+[^+]" | wc -l" * * * Ex: 関数名をstore_data からdata_storeに変更すると、以下に示すように5行が変更されたことになる。 * * # diff -uibw querycache.c querycache.c.orig | grep "^+[^+]" * +static bool store_data (void); * + create_qc_query_space (); * + ret = store_data (); * +store_data (void) * + * This function is called from only store_data (). * # diff -uibw querycache.c.orig querycache.c | grep "^+[^+]" | wc -l * 5 * * dif = 5。 * リスペクトファイルの総行数は3149で、このヘッダーは77行なのでorg = 3149-77 = 3072、 * インスパイヤ度は (3072 - 5)/ 3072 = 0.9983 => 99.8372396%である。 * * * * (2)Aが作ったファイルをリスペクトしたBは、Aのファイルをリスペクトファイルと明記しなければならない。 * BがAをリスペクトして作ったファイルを、Cがさらにリスペクトする場合は、CはBのファイルのみリスペクトすればよい。 * * (3)インスパイヤ度が0となった時点で、そのファイルのインスパイヤ・ライセンスは消失する。 * * * ライセンスの適用地域: * カタカナ文化圏のみ有効。 * 英語を含む他の言語圏へ輸出する場合は、このヘッダファイルを削除した上で、ライセンスフリーとする。 * 理由は、"インスパイヤ"に相当する用語への翻訳が不可能なため。 * * ライセンスの適用における問題点と、現時点での対処: * diff による変更行数の検出には問題があり、例えばindentでファイル整形するとか、 * 関数定義を並び換えるだけで、インスパイヤ度が0%になってしまう。 * このような巧妙なインスパイヤに対して、Ave×ライセンスを策定準備中であるが、商標の問題から * 実現には長い時間がかかると予想されるため、上に示したインスパイヤ方法も、許容することとする。 * * リミックス・ライセンスとの関係: * インスパイヤ・ライセンスは、リミックス・ライセンスに含まれるので、 * リミックス・ライセンスの下でソースをサンプリングするのは自由。 * ここでリミックス・ライセンスとは、適当に部分部分を摘んで新しいファイルにカット&ペイストしたものを * 新たに作品と呼ぶことを保証するライセンス。カット&ペイストは条文中ではサンプリングといわれる。 * * /\ /\ * ( ^ A^) * ( ) * | | | * (___)__) * インスパイヤ・ライセンスのキャラクターは、"モナー"をインスパイヤしたものです。 * リスペクト先 : 2ch.net * インスパイヤ度 = 99% *------------------------------------------------------------------------- */ |
パラメータ設定(guc.c)、メモリコンテキスト関連、
共有メモリ上での領域確保とアクセス、LOCKの使い方など、
これからPostgreSQLをHackしようという優秀な人の参考になれば、
恥ずかしいソースでも少しは世の為になるかも、とWEB上に公開する。そのうちGoogleさんがCacheしてくれるだろう。
ちなみに開発はRedHat9 Linuxで行ない、動作確認もそれでしか行なっていない。
2.5.2 postgresql.conf
query_cache = true # デフォルトはfalseで、querycache機能が無効になっている query_cache_oblock_num = 4092 # query_cache_qblock_num = 5424 # query_cache_max_store_block_num = 28 # |
2.5.3 ベンチマーク
あと、最悪なことに、ときどきクライアントとの接続が切れている。DB失格。
$ /usr/local/pgsql/bin/pgbench -S sample starting vacuum...end. transaction type: SELECT only scaling factor: 1 number of clients: 1 number of transactions per client: 10 number of transactions actually processed: 10/10 tps = 245.074012 (including connections establishing) tps = 356.011250 (excluding connections establishing) $ /usr/local/pgsql/bin/pgbench -S -c 10 sample starting vacuum...end. transaction type: SELECT only scaling factor: 1 number of clients: 10 number of transactions per client: 10 number of transactions actually processed: 100/100 tps = 228.147465 (including connections establishing) tps = 317.170333 (excluding connections establishing) $ /usr/local/pgsql/bin/pgbench -S -c 10 -t 20 sample starting vacuum...end. Client 4 aborted in state 1: Client 1 aborted in state 1: Client 8 aborted in state 1: transaction type: SELECT only scaling factor: 1 number of clients: 10 number of transactions per client: 20 number of transactions actually processed: 166/200 tps = 217.282891 (including connections establishing) tps = 262.587180 (excluding connections establishing) $ /usr/local/pgsql/bin/pgbench -S -c 10 -t 20 sample starting vacuum...end. Client 9 aborted in state 1: Client 1 aborted in state 1: Client 0 aborted in state 1: transaction type: SELECT only scaling factor: 1 number of clients: 10 number of transactions per client: 20 number of transactions actually processed: 165/200 tps = 212.956808 (including connections establishing) tps = 257.021757 (excluding connections establishing) |
ここで統計情報をみると、530件ちかくのSELECT文を処理し、わずかに7件しかキャッシュにヒットしていない。
******************************************* LOG: ******************************************* =============== check_header ============== table_block_num = 521, max_table_block_num = 4092 query_block_num = 1038, max_query_block_num = 5424 table_free = 522 query_free = 1039 =============== statistics ============== all queries = 614 store = 519 hits = 7 not_cached = 0 >>> |
作為的なベンチマークも可能だが、pgbenchを改造する気もないので、ここで辞めておく。
ちなみに、 querycacheなしの結果も示す。
$ /usr/local/pgsql/bin/pgbench -S sample starting vacuum...end. transaction type: SELECT only scaling factor: 1 number of clients: 1 number of transactions per client: 10 number of transactions actually processed: 10/10 tps = 118.207501 (including connections establishing) tps = 126.035063 (excluding connections establishing) $ /usr/local/pgsql/bin/pgbench -S -c 10 -t 20 sample starting vacuum...end. transaction type: SELECT only scaling factor: 1 number of clients: 10 number of transactions per client: 20 number of transactions actually processed: 200/200 tps = 455.719969 (including connections establishing) tps = 536.595836 (excluding connections establishing) |
contents |index |previous |next