MySQL查询优化,通过两个表的并集处理三个表
我有一个查询,它根据两个表中的一个或两个表中的一个列中提供的ID返回单个表的结果。下面提供了相关表的DB模式以及初始查询,以及后来由同行向我推荐的内容。我将在下面详细介绍这个查询的工作原理,但我需要进一步优化它以适应更大的数据集和分页MySQL查询优化,通过两个表的并集处理三个表,mysql,optimization,pagination,Mysql,Optimization,Pagination,我有一个查询,它根据两个表中的一个或两个表中的一个列中提供的ID返回单个表的结果。下面提供了相关表的DB模式以及初始查询,以及后来由同行向我推荐的内容。我将在下面详细介绍这个查询的工作原理,但我需要进一步优化它以适应更大的数据集和分页 CREATE TABLE `killmails` ( `id` BIGINT(20) UNSIGNED NOT NULL, `hash` VARCHAR(255) NOT NULL, `moon_id` BIGINT(20) NULL DE
CREATE TABLE `killmails` (
`id` BIGINT(20) UNSIGNED NOT NULL,
`hash` VARCHAR(255) NOT NULL,
`moon_id` BIGINT(20) NULL DEFAULT NULL,
`solar_system_id` BIGINT(20) UNSIGNED NOT NULL,
`war_id` BIGINT(20) NULL DEFAULT NULL,
`is_npc` TINYINT(1) NOT NULL DEFAULT '0',
`is_awox` TINYINT(1) NOT NULL DEFAULT '0',
`is_solo` TINYINT(1) NOT NULL DEFAULT '0',
`dropped_value` DECIMAL(18,4) UNSIGNED NOT NULL DEFAULT '0.0000',
`destroyed_value` DECIMAL(18,4) UNSIGNED NOT NULL DEFAULT '0.0000',
`fitted_value` DECIMAL(18,4) UNSIGNED NOT NULL DEFAULT '0.0000',
`total_value` DECIMAL(18,4) UNSIGNED NOT NULL DEFAULT '0.0000',
`killmail_time` DATETIME NOT NULL,
`created_at` DATETIME NOT NULL,
`updated_at` DATETIME NOT NULL,
PRIMARY KEY (`id`, `hash`),
INDEX `total_value` (`total_value`),
INDEX `killmail_time` (`killmail_time`),
INDEX `solar_system_id` (`solar_system_id`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
;
CREATE TABLE `killmail_attackers` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`killmail_id` BIGINT(20) UNSIGNED NOT NULL,
`alliance_id` BIGINT(20) UNSIGNED NULL DEFAULT NULL,
`character_id` BIGINT(20) UNSIGNED NULL DEFAULT NULL,
`corporation_id` BIGINT(20) UNSIGNED NULL DEFAULT NULL,
`faction_id` BIGINT(20) UNSIGNED NULL DEFAULT NULL,
`damage_done` BIGINT(20) UNSIGNED NOT NULL,
`final_blow` TINYINT(1) NOT NULL DEFAULT '0',
`security_status` DECIMAL(17,15) NOT NULL,
`ship_type_id` BIGINT(20) UNSIGNED NULL DEFAULT NULL,
`weapon_type_id` BIGINT(20) UNSIGNED NULL DEFAULT NULL,
`created_at` DATETIME NOT NULL,
`updated_at` DATETIME NOT NULL,
PRIMARY KEY (`id`),
INDEX `ship_type_id` (`ship_type_id`),
INDEX `weapon_type_id` (`weapon_type_id`),
INDEX `alliance_id` (`alliance_id`),
INDEX `corporation_id` (`corporation_id`),
INDEX `killmail_id_character_id` (`killmail_id`, `character_id`),
CONSTRAINT `killmail_attackers_killmail_id_killmails_id_foreign_key` FOREIGN KEY (`killmail_id`) REFERENCES `killmails` (`id`) ON UPDATE CASCADE ON DELETE CASCADE
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
;
CREATE TABLE `killmail_victim` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`killmail_id` BIGINT(20) UNSIGNED NOT NULL,
`alliance_id` BIGINT(20) UNSIGNED NULL DEFAULT NULL,
`character_id` BIGINT(20) UNSIGNED NULL DEFAULT NULL,
`corporation_id` BIGINT(20) UNSIGNED NULL DEFAULT NULL,
`faction_id` BIGINT(20) UNSIGNED NULL DEFAULT NULL,
`damage_taken` BIGINT(20) UNSIGNED NOT NULL,
`ship_type_id` BIGINT(20) UNSIGNED NOT NULL,
`ship_value` DECIMAL(18,4) NOT NULL DEFAULT '0.0000',
`pos_x` DECIMAL(30,10) NULL DEFAULT NULL,
`pos_y` DECIMAL(30,10) NULL DEFAULT NULL,
`pos_z` DECIMAL(30,10) NULL DEFAULT NULL,
`created_at` DATETIME NOT NULL,
`updated_at` DATETIME NOT NULL,
PRIMARY KEY (`id`),
INDEX `corporation_id` (`corporation_id`),
INDEX `alliance_id` (`alliance_id`),
INDEX `ship_type_id` (`ship_type_id`),
INDEX `killmail_id_character_id` (`killmail_id`, `character_id`),
CONSTRAINT `killmail_victim_killmail_id_killmails_id_foreign_key` FOREIGN KEY (`killmail_id`) REFERENCES `killmails` (`id`) ON UPDATE CASCADE ON DELETE CASCADE
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
;
第一个查询是问题开始的地方:
SELECT
*
FROM
killmails k
LEFT JOIN killmail_attackers ka ON k.id = ka.killmail_id
LEFT JOIN killmail_victim kv ON k.id = kv.killmail_id
WHERE
ka.character_id = ?
OR kv.character_id = ?
ORDER BY killmails.killmail_time DESC
LIMIT ? OFFSET ?
这工作正常,但查询时间很长。我们对此进行了优化
SELECT
killmails.*,
FROM (
SELECT killmail_victim.killmail_id FROM killmail_victim
WHERE killmail_victim.corporation_id = ?
UNION
SELECT killmail_attackers.killmail_id FROM killmail_attackers
WHERE killmail_attackers.corporation_id = ?
) SELECTED_KMS
LEFT JOIN killmails ON killmails.id = SELECTED_KMS.killmail_id
ORDER BY killmails.killmail_time DESC
LIMIT ? OFFSET ?
在查找Killmail中的字符时,我看到了查询时间的巨大改进,但是当我开始查询更大的数据集(如Corporate和alliance Killmail)时,查询速度变慢了。这是因为联合在一起的查询可能会返回大量数据,而将所有数据读入内存以便创建所选的_KMS表所需的时间,我相信这会花费很多时间。大多数情况下,对于联盟,我与数据库的连接从应用程序超时。一个联盟从联盟的一个表中返回了900K个KillMailID,不知道另一个返回了什么
我可以很容易地在内部查询中添加limit语句,但是当我开始对数据进行分页时,或者当我引入一个按日期搜索KMs的功能时,这会带来很多复杂问题
我正在寻找有关如何优化此查询并在不久的将来允许轻松分页的建议
谢谢将两个表中的索引(公司id)
更改为索引(公司id、killmail id)
,以便内部查询将“覆盖”
一般来说,索引(a)
在您还有索引(a,b)
时是无用的。任何只需要a
的查询都可以使用这些索引中的任何一个。(此规则不适用于b
;仅适用于“最左侧”列。)
killmails.id来自哪里?它不是自动递增的;它在主键中不是唯一的,因此没有指定的“唯一性”约束。它是独一无二的其他设计吗?它是在代码的其他地方计算的吗?(我这样问是因为我需要感受到它的独特性和其他特点。)
添加索引(id、killmail\u时间)
你用的是什么版本
也许UNION ALL
会给出相同的结果?它会更快,因为它不需要重复数据消除
你有多少公羊?innodb\u buffer\u pool\u size的值是多少
您真的需要8字节的bigint
?即使您的应用程序正在使用longlong(或它所称的任何东西),您也可以在不更改应用程序的情况下更改模式
你需要这么高的精度和范围吗十进制(30,10)
——每个字节需要14个字节DOUBLE
将在8个字节中提供大约16个有效数字,值的范围更广(最多可达10^308)。你用的是什么“单位”?(对于光年或帕塞克来说,杀伤力过大;对于英里或公里来说,杀伤力不足。也许是因为?那么底部的数字的精度应该是几米?)
最后几个问题的目的是缩小表,看看是否可以避免像现在这样受到I/O限制
重要
innodb\u buffer\u pool\u size=128M
非常小,特别是对于32GB的机器,尤其是当您的数据集远远大于128MB时。如果服务器上没有运行任何其他应用程序,请将该设置设置为20G
非常感谢您的回复。一些后续问题如下:INDEX(corporation\u id,killmail\u id)
我是否需要将初始索引仅保留在corporation\u id上,还是可以将其删除?回复:killmails.id
你能澄清一下吗?它是killmail表上的主键,是killmail_受害者和killmail_攻击者之间的联系。RE Version:MySQL 5.7 RE UNION ALL:故意使用UNION对ids进行重复数据消除RE RAM:32GB,自从我开始这个项目以来,我还没有看到3GB以上的峰值RE Precision and Range:是的,这些是Universe中的坐标Big Ints:可能不是,我诚实地设计DB时很懒。我宁愿先尝试对索引进行其他建议的更改,因为更改这些类型也需要更新应用程序,但是我并不是完全反对。REinnodb\u buffer\u pool\u size
mysql>SELECT@@innodb\u buffer\u pool\u size/1024/1024;+--------------------------------------------------+\124;@@innodb_buffer_pool_size/1024/1024 |+----------------------------------------+| 128.00000000 |+----------------------------------------+@DevOverlord-我在几个地方编辑了我的答案。谢谢您的编辑。killmail id在killmail表中保证是唯一的。我只是在id和散列上创建了主键,因为它们作为一个包来自API,就是这样。我已经将pool_的大小提升到了20G,查询时间从之前的2.5分钟缩短到现在的1分钟。我来看看模式的变化