只要我在 select 子句中包含“longtext”类型,查询时间就会从 8 秒变为 3 分钟(Amazon RDS t2.small)。where 子句中不使用长文本,结果集为空。见下文:
mysql> select id from mbp_process where errorAcknowledged='N' and (exitCode != 0 or exitCode is null);
Empty set (8.03 sec)
mysql> select id, stdoutContents from mbp_process where errorAcknowledged='N' and (exitCode != 0 or exitCode is null);
Empty set (3 min 43.36 sec)
Run Code Online (Sandbox Code Playgroud)
令我难以置信的是,通过主键请求 longtext 列很快:
select stdoutContents from mbp_process where id = 49213;
...
1 row in set (0.00 sec)
Run Code Online (Sandbox Code Playgroud)
为什么是这样?在我的裸机服务器上效果不太明显:查询从 0.2s 减慢到 1:05m。
这是“select id from...”查询的解释:
+----+-------------+-------------+------+-----------------------------------------------------+----------------------------+---------+-------+-------+------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------------+------+-----------------------------------------------------+----------------------------+---------+-------+-------+------------------------------------+
| 1 | SIMPLE | mbp_process | ref | idx_mbp_process_exitCode,idx_mbp_process_errorAcked | idx_mbp_process_errorAcked | 2 | const | 22551 | Using index condition; Using where |
+----+-------------+-------------+------+-----------------------------------------------------+----------------------------+---------+-------+-------+------------------------------------+
Run Code Online (Sandbox Code Playgroud)
这是来自“select id, stdoutContents from ...”查询的解释:
+----+-------------+-------------+------+-----------------------------------------------------+----------------------------+---------+-------+-------+------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------------+------+-----------------------------------------------------+----------------------------+---------+-------+-------+------------------------------------+
| 1 | SIMPLE | mbp_process | ref | idx_mbp_process_exitCode,idx_mbp_process_errorAcked | idx_mbp_process_errorAcked | 2 | const | 22552 | Using index condition; Using where |
+----+-------------+-------------+------+-----------------------------------------------------+----------------------------+---------+-------+-------+------------------------------------+
Run Code Online (Sandbox Code Playgroud)
它们是相同的。
这是“SHOW CREATE TABLE mbp_process”中的创建表语句:
CREATE TABLE `mbp_process` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`command` varchar(1000) DEFAULT NULL,
`pid` varchar(45) DEFAULT NULL,
`state` varchar(45) NOT NULL,
`exitCode` int(11) DEFAULT NULL,
`stdoutContents` longtext,
`stdoutTruncated` char(1) DEFAULT NULL,
`stdoutFilename` varchar(200) DEFAULT NULL,
`stderrContents` longtext,
`stderrTruncated` char(1) DEFAULT NULL,
`stderrFilename` varchar(200) DEFAULT NULL,
`majorProgress` varchar(45) DEFAULT NULL,
`minorProgress` varchar(45) DEFAULT NULL,
`startTime` datetime DEFAULT NULL,
`endTime` datetime DEFAULT NULL,
`errorAcknowledged` char(1) DEFAULT 'N',
`errorComments` text,
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_mbp_process_command` (`command`(767)),
KEY `idx_mbp_process_exitCode` (`exitCode`),
KEY `idx_mbp_process_state` (`state`),
KEY `idx_mbp_process_errorAcked` (`errorAcknowledged`)
) ENGINE=InnoDB AUTO_INCREMENT=50184 DEFAULT CHARSET=latin1
Run Code Online (Sandbox Code Playgroud)
选择要包含的另一列不会减慢查询速度:
mysql> select id, created from mbp_process where errorAcknowledged='N' and (exitCode != 0 or exitCode is null);
Empty set (1.69 sec)
Run Code Online (Sandbox Code Playgroud)
这很奇怪:如果我包含“stderrContents”,查询就会很快。这也是一个长文本列,尽管它通常包含的数据要少得多。但是,我不是要求 MySQL 检查列的内容,结果集是空的,那么为什么“stdoutContents”很慢?
mysql> select id, stderrContents from mbp_process where errorAcknowledged='N' and (exitCode != 0 or exitCode is null);
Empty set (0.57 sec)
Run Code Online (Sandbox Code Playgroud)
PRIMARY KEY(id)
说它与数据聚集在一起。这不是问题。也没有尝试使用二级索引。这就是正在发生的事情。
在InnoDB中,通常所有列都位于BTree中由PK索引的主键旁边。然而,“大”列被放置在其他地方。正如您所描述的,最多大约 8KB 的行被保留在一起。大列位于其自己的 16KB 块中。这些列包括任何 TEXT/BLOB,甚至长 VARCHAR/VARBINARY 列。(详细信息因 innodb_file_format 和其他一些因素而异。例如,大列的前 767 个字节可能会保留为短列。)
因此,当选择排除此类大列的列时,查询将避免获取这些额外的块并且相对较快。听起来 stdoutContents 真的很大(需要多个 16KB 块)?
亚马逊与裸机:如果我没记错的话,亚马逊将数据存储在类似 SAN 的系统上,而不是同一个盒子上。
我看到的另一件事......说它EXPLAIN
正在使用这个辅助密钥
KEY `idx_mbp_process_errorAcked` (`errorAcknowledged`)
Run Code Online (Sandbox Code Playgroud)
每个辅助密钥都隐式包含 PK ( id
)。所以,处理过程是这样的:
errorAcknowledged='N'
id
来访问“数据”BTree。(每行 1 个块,如果缓存得很好,希望更少)and (exitCode != 0 or exitCode is null)
我希望这能解释一切。如果您需要进一步说明,请告诉我。
归档时间: |
|
查看次数: |
8105 次 |
最近记录: |