Mysql:选择id,然后在id上选择*比选择*快得多。为什么?
我有一个MySQL数据库表(大约10万行): id BIGINT(索引)、外部条形码VARCHAR(索引)、其他简单列和长文本列 LongText列是一个JSON数据转储。我保存大型JSON对象,因为将来需要提取更多数据 运行此查询需要29秒以上的时间:Mysql:选择id,然后在id上选择*比选择*快得多。为什么?,mysql,query-optimization,Mysql,Query Optimization,我有一个MySQL数据库表(大约10万行): id BIGINT(索引)、外部条形码VARCHAR(索引)、其他简单列和长文本列 LongText列是一个JSON数据转储。我保存大型JSON对象,因为将来需要提取更多数据 运行此查询需要29秒以上的时间: SELECT * FROM scraper_data WHERE external_barcode = '032429257284' 说明 #id select_type table partitions type po
SELECT * FROM scraper_data WHERE external_barcode = '032429257284'
说明
#id select_type table partitions type possible_keys key key_len ref rows filtered Extra
'1' 'SIMPLE' 'scraper_data' NULL 'ALL' NULL NULL NULL NULL '119902' '0.00' 'Using where'
# id, select_type, table, partitions, type, possible_keys, key, key_len, ref, rows, filtered, Extra
'1', 'PRIMARY', 'scraper_data', NULL, 'const', 'PRIMARY,id_UNIQUE', 'PRIMARY', '8', 'const', '1', '100.00', NULL
'2', 'SUBQUERY', 'scraper_data', NULL, 'ALL', NULL, NULL, NULL, NULL, '119902', '0.00', 'Using where'
此更复杂的查询需要0.00秒:
SELECT * FROM scraper_data WHERE id = (
SELECT id FROM scraper_data WHERE external_barcode = '032429257284'
)
说明
#id select_type table partitions type possible_keys key key_len ref rows filtered Extra
'1' 'SIMPLE' 'scraper_data' NULL 'ALL' NULL NULL NULL NULL '119902' '0.00' 'Using where'
# id, select_type, table, partitions, type, possible_keys, key, key_len, ref, rows, filtered, Extra
'1', 'PRIMARY', 'scraper_data', NULL, 'const', 'PRIMARY,id_UNIQUE', 'PRIMARY', '8', 'const', '1', '100.00', NULL
'2', 'SUBQUERY', 'scraper_data', NULL, 'ALL', NULL, NULL, NULL, NULL, '119902', '0.00', 'Using where'
从这些查询返回的行少于6行。既然where子句中没有引用第一个查询,为什么长文本会减慢第一个查询的速度
创建表
CREATE TABLE `scraper_data` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`bzic` varchar(10) NOT NULL,
`pzic` varchar(10) DEFAULT NULL,
`internal_barcode` varchar(20) DEFAULT NULL,
`external_barcode_type` enum('upc','isbn','ean','gtin') DEFAULT NULL,
`external_barcode` varchar(15) DEFAULT NULL,
`url` varchar(255) NOT NULL,
`title` varchar(255) DEFAULT NULL,
`category` varchar(3) DEFAULT NULL,
`description` text,
`logo_image_url` varchar(255) DEFAULT NULL,
`variant_image_urls` text,
`parent_brand` varchar(10) DEFAULT NULL,
`parent_brand_name` varchar(255) DEFAULT NULL,
`manufacturer` varchar(10) DEFAULT NULL,
`manufacturer_name` varchar(255) DEFAULT NULL,
`manufacturer_part_number` varchar(255) DEFAULT NULL,
`manufacturer_model_number` varchar(255) DEFAULT NULL,
`contributors` text,
`content_info` text,
`content_rating` text,
`release_date` timestamp NULL DEFAULT NULL,
`reviews` int(11) DEFAULT NULL,
`ratings` int(11) DEFAULT NULL,
`internal_path` varchar(255) DEFAULT NULL,
`price` int(11) DEFAULT NULL,
`adult_product` tinyint(4) DEFAULT NULL,
`height` varchar(255) DEFAULT NULL,
`length` varchar(255) DEFAULT NULL,
`width` varchar(255) DEFAULT NULL,
`weight` varchar(255) DEFAULT NULL,
`scraped` tinyint(4) NOT NULL DEFAULT '0',
`scraped_timestamp` timestamp NULL DEFAULT NULL,
`scrape_attempt_timestamp` timestamp NULL DEFAULT NULL,
`processed` tinyint(4) NOT NULL DEFAULT '0',
`processed_timestamp` timestamp NULL DEFAULT NULL,
`modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`scrape_dump` longtext,
PRIMARY KEY (`id`),
UNIQUE KEY `id_UNIQUE` (`id`),
UNIQUE KEY `url_UNIQUE` (`url`),
UNIQUE KEY `internal_barcode_UNIQUE` (`internal_barcode`),
KEY `bzic` (`bzic`),
KEY `pzic` (`pzic`),
KEY `internal_barcode` (`internal_barcode`),
KEY `external_barcode` (`external_barcode`,`external_barcode_type`) /*!80000 INVISIBLE */,
KEY `scrape_attempt` (`bzic`,`scraped`,`scrape_attempt_timestamp`)
) ENGINE=InnoDB AUTO_INCREMENT=121674 DEFAULT CHARSET=latin1;
第二个查询可能受益于已经包含第一个查询结果的缓存 此外,在第二个子查询中,您只需选择使用两列(id,external_barcode)在这两列中都在索引中。所有查询结果仅通过索引扫描获得,而在第一次查询中,要检索所有数据,查询必须扫描所有表行 为了避免第一次查询的时间过长,您应该在
external\u barcode
列中添加适当的索引
create index my_idx on scraper_data (external_barcode, id)
您的查询不相等,如果有多行带有该条形码,则第二次查询将抛出错误: 错误代码:1242。子查询返回超过1行 这可能就是这里发生的情况:您实际上并没有得到一个结果,只是一个错误。由于MySQL可以在找到第二行时立即停止全表扫描,因此,如果这些行位于扫描的第一行中(例如在
id
s1和2中),则可以比正确的结果更快地得到此错误,包括“0.00s”
从执行计划中,您可以看到两者都执行完整的表扫描(在当前版本中,包括读取blob字段),因此执行速度应该类似地快(因为第二个解释计划中的第一个条目仅在几行中可以忽略)
因此,对于不会引发错误的条形码,您的两个查询以及已更正的第二个查询(您在中使用,而不是=
)
以及运行子查询
SELECT id FROM scraper_data WHERE external_barcode = '032429257284'
另外(如果您的假设是正确的,则必须比您的第二次查询更快)将有类似的(长)执行时间
如中所述,在external_barcode
上建立索引将显著提高性能,因为您不需要进行完整的表格扫描,也不需要读取blob字段。实际上您有这样一个索引,但是您禁用了它(不可见的
)。您可以使用
ALTER TABLE scraper_data ALTER INDEX `external_barcode` VISIBLE;
显示statemetns和CREATE Table scraper的示例_data@nbk更新的postMaybe您只需在第一个查询之后运行第二个查询。因此,它使用第一个查询的缓存结果?@IldarAkhmetov第一个查询需要很长时间,因此我构建了第二个查询作为优化。它们没有一起运行。这两个查询没有一起运行。第一个查询需要很长时间,所以我构建了第二个查询作为优化。文章显示,外部条形码也已经有了索引。这两个查询都在where子句中使用外部\u条形码,因此在比较两个查询的性能差异时,索引的存在应该没有什么区别。两个查询中的外部\u条形码列都被索引,并且这些查询不会一起运行。SELECT语句中捕获的内容应该不会影响WHERE子句中发生的内容。您有一个带有外部条形码id???的索引。。如果这是真的,子查询将被解析,只需访问索引,并且只访问与匹配的外部条形码相关的行。。。在第一个查询中。。您还需要有others列,为此,查询必须访问表数据。。请记住,查询优化器分析所有查询子句。。在开始和结束选择时启动。。并计算解决查询所需的所有列。。并在此基础上执行对索引(仅)和/或表的正确访问WHERE子句在两个查询中完全相同。只需选择*。另一个SELECT id。选择id的那个将用第二个查询包装,该查询显示SELECT*FROM id。执行双倍工作的那个将更快。这似乎反映了您从结果中选择的内容会影响WHERE子句查找结果的速度,但不会影响SELECT。。与子查询返回的id匹配的外部查询。。id可能是主键,并且具有索引。。子查询将外部_条形码用于具有索引的匹配。。第一个查询执行表的完整扫描,以获取所有列的值。。