У меня довольно устойчивый ориентированный граф порядка ~ 100k вершин и размер ~ 1k ребер. Он двумерен, поскольку его вершины могут быть идентифицированы парой целых чисел (x, y)
(мощности ~ 100 x 1000), а все ребра строго возрастают в x
.
Кроме того, имеется словарь из ~ 1k (key, val)
пар, связанных с каждой вершиной.
В настоящее время я храню график в базе данных MySQL по трем (InnoDB) таблицам: таблица вершин (что, по моему мнению, не имеет отношения к моему вопросу, поэтому я опустил включение как его, так и ограничений внешнего ключа которые относятся к нему в моих выдержках ниже); таблица, в которой хранятся словари; и "таблицу замыкания" связанных вершин, как это было описано красноречиво Биллом Карвином.
Таблица вершинных словарей определяется следующим образом:
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)
, такой, что для каждой такой пары все вершины, идентифицированные с этим x
, имеют в своих словарях значение для этого key
. Этот словарь хранится в четвертой таблице:
CREATE TABLE `SpecialKeys` (
`x` smallint(6) unsigned NOT NULL,
`key` varchar(50) NOT NULL DEFAULT '',
PRIMARY KEY (`x`),
KEY `xkey` (`x`, `key`)
);
Я часто хочу извлечь набор ключей, используемых в словарях всех вершин, имеющих конкретный x=X
, вместе со связанным значением любого 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 table type possible_keys key key_len ref rows Extra 1 SIMPLE k index PRIMARY,xkey xkey 154 NULL 40 Using index; Using temporary 1 SIMPLE c ref PRIMARY,reverse,fx,rx PRIMARY 2 db.k.x 1 Using where 1 SIMPLE v ref PRIMARY,dict PRIMARY 4 const,db.c.head_y 136 Using index 1 SIMPLE u eq_ref PRIMARY,dict PRIMARY 156 db.c.tail_x,db.c.tail_y,db.k.key 1 Using where
Но этот запрос занимает ~ 10 секунд. Я стучал головой о кирпичную стену, пытаясь улучшить ситуацию, но безуспешно.
Можно ли улучшить запрос, или я должен рассмотреть другую структуру данных? Чрезвычайно благодарен за ваши мысли!
UPDATE
Я по-прежнему не получаю этого, хотя я перестроил таблицы и нашел вывод EXPLAIN
немного отличающимся (как показано выше, количество строк, полученных из v
, увеличилось с 1 до 136!); запрос все еще принимает ~ 10 секунд для выполнения.
Я действительно не понимаю, что происходит здесь. Запросы на получение всех (x, y, SpecialValue)
и всех (x, y, key)
кортежей очень быстрые (~ 30 мс и ~ 150 мс соответственно), но, по сути, соединение двух занимает в пятьдесят раз больше времени, чем их комбинированное время... как я могу улучшить время для выполнения этого соединения?
Вывод SHOW VARIABLES LIKE '%innodb%';
ниже:
Variable_name Value ------------------------------------------------------------ have_innodb YES ignore_builtin_innodb ON innodb_adaptive_flushing ON innodb_adaptive_hash_index ON innodb_additional_mem_pool_size 2097152 innodb_autoextend_increment 8 innodb_autoinc_lock_mode 1 innodb_buffer_pool_size 1179648000 innodb_change_buffering inserts innodb_checksums ON innodb_commit_concurrency 0 innodb_concurrency_tickets 500 innodb_data_file_path ibdata1:10M:autoextend innodb_data_home_dir /rdsdbdata/db/innodb innodb_doublewrite ON innodb_fast_shutdown 1 innodb_file_format Antelope innodb_file_format_check Barracuda innodb_file_per_table ON 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 OFF 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 OFF innodb_spin_wait_delay 6 innodb_stats_method nulls_equal innodb_stats_on_metadata ON innodb_stats_sample_pages 8 innodb_strict_mode OFF innodb_support_xa ON innodb_sync_spin_loops 30 innodb_table_locks ON innodb_thread_concurrency 0 innodb_thread_sleep_delay 10000 innodb_use_sys_malloc ON innodb_version 1.0.16 innodb_write_io_threads 4