我有一个相当稳定的有向图,其阶数约为 100k 个顶点,边数约为 1k。它是二维的,因为它的顶点可以通过一对整数(x, y)
(基数 ~100 x ~1000)来识别,并且所有边都严格递增x
。
此外,还有一个(key, val)
与每个顶点相关联的约 1k 对字典。
我目前将图形存储在三个(InnoDB)表的 MySQL 数据库中:一个顶点表(我认为这与我的问题无关,所以我省略了包括它和引用的外键约束它在我下面的摘录中);存放字典的表;以及 Bill Karwin 雄辩地描述的连接顶点的“闭合表”。
顶点字典表定义如下:
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
。该字典存储在第四个表中:
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 使用索引;使用临时 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 毫秒),但实际上加入这两者的时间比它们的总时间长 50 倍......我如何才能缩短执行该连接所花费的时间?
下面的输出SHOW VARIABLES LIKE '%innodb%';
:
变量名值 -------------------------------------------------- ---------- have_innodb 是 ignore_builtin_innodb ON 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 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 开启 innodb_fast_shutdown 1 innodb_file_format 羚羊 innodb_file_format_check 梭子鱼 innodb_file_per_table ON innodb_flush_log_at_trx_commit 1 innodb_flush_method O_DIRECT innodb_force_recovery 0 innodb_io_容量 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_version 1.0.16 innodb_write_io_threads 4