相同 MySql 查询执行时间长但存档时间短 table 多了 600 万条记录

Same MySql Query Long execution time but short on archive table with 6million more records

我对这个问题有点困惑。 我有一个 gps 跟踪应用程序,它将 gps 点记录到 track_log table 中。 当我对 运行ning 日志 table 进行基本查询时,大约需要 50 秒才能完成:

 SELECT * FROM track_log WHERE node_id = '26' ORDER BY time_stamp DESC LIMIT 1

当我 运行 在存档 table 上执行完全相同的查询时,我将大部分日志复制到其中以减少 运行ning table 的日志到大约 120 万条记录。 存档 table 有 750 万条记录。

在同一台服务器上对存档 table 运行s 进行 0.1 秒的完全相同的查询,即使它大六倍! 怎么回事?

Here's the full Create Table schema:


    CREATE TABLE `track_log` (
    `id_track_log` INT(11) NOT NULL AUTO_INCREMENT,
    `node_id` INT(11) DEFAULT NULL,
    `client_id` INT(11) DEFAULT NULL,
    `time_stamp` DATETIME NOT NULL,
    `latitude` DOUBLE DEFAULT NULL,
    `longitude` DOUBLE DEFAULT NULL,
    `altitude` DOUBLE DEFAULT NULL,
    `direction` DOUBLE DEFAULT NULL,
    `speed` DOUBLE DEFAULT NULL,
    `event_code` INT(11) DEFAULT NULL,
    `event_description` VARCHAR(255) DEFAULT NULL,
    `street_address` VARCHAR(255) DEFAULT NULL,
    `mileage` INT(11) DEFAULT NULL,
    `run_time` INT(11) DEFAULT NULL,
    `satellites` INT(11) DEFAULT NULL,
    `gsm_signal_status` DOUBLE DEFAULT NULL,
    `hor_pos_accuracy` double DEFAULT NULL,
    `positioning_status` char(1) DEFAULT NULL,
    `io_port_status` char(16) DEFAULT NULL,
    `AD1` decimal(10,2) DEFAULT NULL,
    `AD2` decimal(10,2) DEFAULT NULL,
    `AD3` decimal(10,2) DEFAULT NULL,
    `battery_voltage` decimal(10,2) DEFAULT NULL,
    `ext_power_voltage` decimal(10,2) DEFAULT NULL,
    `rfid` char(8) DEFAULT NULL,
    `pic_name` varchar(255) DEFAULT NULL,
    `temp_sensor_no` char(2) DEFAULT NULL,
    PRIMARY KEY (`id_track_log`),
    UNIQUE KEY `id_track_log_UNIQUE` (`id_track_log`),
    KEY `client_id_fk_idx` (`client_id`),
    KEY `track_log_node_id_fk_idx` (`node_id`),
    KEY `track_log_event_code_fk_idx` (`event_code`),
    KEY `track_log_time_stamp_index` (`time_stamp`),
    CONSTRAINT `track_log_client_id` FOREIGN KEY (`client_id`) REFERENCES    `clients` (`client_id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
    CONSTRAINT `track_log_event_code_fk` FOREIGN KEY (`event_code`) REFERENCES `event_codes` (`event_code`) ON DELETE NO ACTION ON UPDATE NO ACTION,
    CONSTRAINT `track_log_node_id_fk` FOREIGN KEY (`node_id`) REFERENCES `nodes` (`id_nodes`) ON DELETE NO ACTION ON UPDATE NO ACTION
    ) ENGINE=InnoDB AUTO_INCREMENT=8632967 DEFAULT CHARSET=utf8

TL;DR

索引

我猜这与您在 table 上定义的索引有关。你能 post SHOW CREATE TABLES track_log 输出和你的档案 table 的输出吗?您正在执行的查询需要 node_idtime_stamp 上的索引以获得最佳性能。

碎片整理

除了您在 table 上定义的索引外,这可能与数据碎片有关。我假设您现在使用 InnoDB 作为 table 引擎。根据您的设置,数据库中的每个 table 都存储在一个单独的文件中,或者数据库中的每个 table 都存储在一个文件中(innodb_file_per_table 变量)。这些文件的大小永远不会缩小。如果您的 track_log table 已增长到 870 万条记录,在磁盘上,它仍然占用 space 所有这 870 万条记录。

如果您已将记录从 track_log table 移动到存档 table,数据可能仍位于 [=12] 的物理文件的开头和结尾=].如果 time_stamp 处未定义索引,则仍需要完整 table 扫描才能按时间戳排序。这意味着:从磁盘读取完整的文件。因为您删除的记录仍然在文件中占用 space,所以这可能会有所不同。

编辑:

事务

其他交易可能会阻止您的 SELECT 查询。 InnoDB 引擎可能会发生这种情况。如果您不断地向 track_log table 中插入大量数据,这些查询可能会阻止您的查询。它将必须等到在此 table.

上没有其他事务正在执行

有办法解决这个问题,但你应该小心。您可以更改查询的事务隔离级别。通过将事务隔离级别设置为 READ UNCOMMITTED 您将能够读取数据,而其他插入是 运行。但它可能并不总是为您提供最新数据。如果你想牺牲这取决于你的情况。如果稍后要更改数据并更新数据,通常不想更改事务隔离级别。但是,例如,当显示不应始终准确和最新的统计信息时,这可能会真正加快您的查询速度。

当我需要显示定期更新的大型 table 的统计数据时,我有时会自己使用它。

让我们分析您的查询:

SELECT * FROM track_log WHERE node_id = '26' ORDER BY time_stamp DESC LIMIT 1

上述查询首先根据 time_stamp 对 table 中存在的所有数据进行排序,然后 returns 对顶行进行排序。

但是,当在 archived table 上执行此查询时,order by 子句可能会被忽略(基于压缩和系统设置),因此它 returns 它遇到的第一行table.

您可以通过将结果与实际的最新行进行比较来验证存档 table 的输出。

这几乎可以肯定是因为您的存档 table 的索引优于 track_log table。

为了有效地满足此查询,您需要一个复合索引 (node_id, time_stamp) 为什么这样做有效?因为 InnoDB 和 MyISAM 索引是所谓的 BTREE 索引,这意味着我们按顺序搜索它们的直觉会起作用。您的查询查找 node_id 的特定值,这意味着它可以有效地跳转到索引中的该值。查询然后调用与该 node_id 值相关的 time_stamp 的最高可能值。现在它在同一个索引中,并且以正确的顺序也可以快速访问它。因此,您需要的行可以随机访问,并且 MySQL 不必通过逐行扫描 table 来寻找它。扫描几乎肯定是您查询中花费时间的原因。

要记住三件事:

一:单列上的大量索引无法像精心选择的复合索引那样帮助查询。阅读此 http://use-the-index-luke.com/

二:SELECT * 通常对 table 有害,列数与您显示的列一样多。相反,您应该在 SELECT 查询中枚举您实际需要的列。这样 MySQL 就不必传输那么多数据了。

三:DOUBLE 数据类型对于商业级 GPS 数据来说太过分了。 FLOAT 足够精确。