頂点数が 100k 以下、エッジ数が 1k 以下のかなり安定した有向グラフがあります。(x, y)
頂点が(カーディナリティ ~100 x ~1000 の)整数のペアで識別でき、すべてのエッジが で厳密に増加している限り、これは 2 次元x
です。
(key, val)
さらに、各頂点に関連付けられた~1k ペアのディクショナリがあります。
私は現在、3つの(InnoDB)テーブルにまたがるMySQLデータベースにグラフを保存しています:頂点のテーブル(これは私の質問に関連しているとは思わないので、それと参照する外部キー制約の両方を含めることを省略しました以下の私の抜粋にあります); 辞書を保持するテーブル。ビル・カーウィンが雄弁に説明したように、接続された頂点の「クロージャテーブル」。
頂点辞書のテーブルは次のように定義されます。
CREATE TABLE `VertexDictionary` (
`x` smallint(6) unsigned NOT NULL,
`y` smallint(6) unsigned NOT NULL,
`key` varchar(50) NOT NULL DEFAULT '',
`val` smallint(1) DEFAULT NULL,
PRIMARY KEY (`x`, `y` , `key`),
KEY `dict` (`x`, `key`, `val`)
);
接続された頂点の閉包表は次のようになります。
CREATE TABLE `ConnectedVertices` (
`tail_x` smallint(6) unsigned NOT NULL,
`tail_y` smallint(6) unsigned NOT NULL,
`head_x` smallint(6) unsigned NOT NULL,
`head_y` smallint(6) unsigned NOT NULL,
PRIMARY KEY (`tail_x`, `tail_y`, `head_x`),
KEY `reverse` (`head_x`, `head_y`, `tail_x`),
KEY `fx` (`tail_x`, `head_x`),
KEY `rx` (`head_x`, `tail_x`)
);
ペアの辞書もあり、(x, key)
そのようなペアごとに、 that で識別されるすべての頂点x
が辞書内に that の値を持っていますkey
。このディクショナリは 4 番目のテーブルに格納されます。
CREATE TABLE `SpecialKeys` (
`x` smallint(6) unsigned NOT NULL,
`key` varchar(50) NOT NULL DEFAULT '',
PRIMARY KEY (`x`),
KEY `xkey` (`x`, `key`)
);
特定の を持つすべての頂点の辞書で使用されるキーのセットを、左に接続されたx=X
any の関連する値とともに抽出したいことがよくあります。SpecialKeys
SELECT DISTINCT
`v`.`key`,
`u`.`val`
FROM
`ConnectedVertices` AS `c`
JOIN `VertexDictionary` AS `u` ON (`u`.`x`, `u`.`y` ) = (`c`.`tail_x`, `c`.`tail_y`)
JOIN `VertexDictionary` AS `v` ON (`v`.`x`, `v`.`y` ) = (`c`.`head_x`, `c`.`head_y`)
JOIN `SpecialKeys` AS `k` ON (`k`.`x`, `k`.`key`) = (`u`.`x`, `u`.`key`)
WHERE
`v`.`x` = X
;
EXPLAIN
出力は次のとおりです。
id select_type テーブル タイプ possible_keys キー key_len ref 行 エクストラ 1 SIMPLE k index PRIMARY,xkey xkey 154 NULL 40 インデックスを使用。一時的な使用 1 SIMPLE c ref PRIMARY,reverse,fx,rx PRIMARY 2 db.kx 1 where の使用 1 SIMPLE v ref PRIMARY,dict PRIMARY 4 const,db.c.head_y 136 インデックスの使用 1 SIMPLE u eq_ref PRIMARY,dict PRIMARY 156 db.c.tail_x,db.c.tail_y,db.k.key 1 where の使用
ただし、このクエリは完了するまでに 10 秒ほどかかります。問題を改善しようとしてレンガの壁に頭をぶつけていましたが、役に立ちませんでした。
クエリを改善できますか、または別のデータ構造を検討する必要がありますか? あなたの考えに非常に感謝します!
アップデート
テーブルを再構築したところ、出力がわずかに異なることがわかりましたEXPLAIN
(上記のように、フェッチされた行の数がv
1 から 136 に増加しました!)。クエリの実行にはまだ 10 秒ほどかかります。
ここで何が起こっているのか本当にわかりません。(x, y, SpecialValue)
すべてのタプルとすべてのタプルを取得するためのクエリ(x, y, key)
はどちらも非常に高速ですが (それぞれ約 30 ミリ秒と約 150 ミリ秒)、基本的に 2 つの結合には合計時間の 50 倍の時間がかかります... その結合を実行するのにかかる時間を改善するにはどうすればよいですか?
以下の出力SHOW VARIABLES LIKE '%innodb%';
:
変数名 値 -------------------------------------------------- ---------- have_innodb はい ignore_builtin_innodb オン innodb_adaptive_flushing ON innodb_adaptive_hash_index オン innodb_additional_mem_pool_size 2097152 innodb_autoextend_increment 8 innodb_autoinc_lock_mode 1 innodb_buffer_pool_size 1179648000 innodb_change_buffering 挿入 innodb_checksums オン innodb_commit_concurrency 0 innodb_concurrency_tickets 500 innodb_data_file_path ibdata1:10M:autoextend innodb_data_home_dir /rdsdbdata/db/innodb innodb_doublewrite オン innodb_fast_shutdown 1 innodb_file_format アンテロープ innodb_file_format_check バラクーダ innodb_file_per_table オン innodb_flush_log_at_trx_commit 1 innodb_flush_method O_DIRECT innodb_force_recovery 0 innodb_io_capacity 200 innodb_lock_wait_timeout 50 innodb_locks_unsafe_for_binlog オフ innodb_log_buffer_size 8388608 innodb_log_file_size 134217728 innodb_log_files_in_group 2 innodb_log_group_home_dir /rdsdbdata/log/innodb innodb_max_dirty_pages_pct 75 innodb_max_purge_lag 0 innodb_mirrored_log_groups 1 innodb_old_blocks_pct 37 innodb_old_blocks_time 0 innodb_open_files 300 innodb_read_ahead_threshold 56 innodb_read_io_threads 4 innodb_replication_delay 0 innodb_rollback_on_timeout オフ innodb_spin_wait_delay 6 innodb_stats_method nulls_equal innodb_stats_on_metadata オン innodb_stats_sample_pages 8 innodb_strict_mode オフ innodb_support_xa オン innodb_sync_spin_loops 30 innodb_table_locks オン innodb_thread_concurrency 0 innodb_thread_sleep_delay 10000 innodb_use_sys_malloc オン innodb_バージョン 1.0.16 innodb_write_io_threads 4