mysql - 尽管检查的行数相似,为什么查询时间会激增?

标签 mysql sql performance testing indexing

我正在查看慢速查询日志,试图确定为什么某些查询行为不稳定。为了保持一致性,不缓存查询,并且在运行测试之前进行了刷新以清除系统缓存。查询是这样的:

SELECT P.id, P.name, P.lat, P.lng, P.price * E.rate AS 'ask' FROM Property P
 INNER JOIN Exchange E ON E.currency = P.currency
 WHERE P.floor_area >= k?
  AND P.closing_date >= CURDATE() // this and key_buffer_size=0 prevents caching
  AND P.type ='c'
  AND P.lat BETWEEN v? AND v?
  AND P.lng BETWEEN v? AND v?
  AND P.price * E.rate BETWEEN k? AND k?
 ORDER BY P.floor_area DESC LIMIT 100;

k? 是用户定义的常量 值; v? 是随着用户在 map 上拖动或缩放而改变的变量。从表中拉出100条结果,按照占地面积降序排列。

id 上的 PRIMARY key 和 floor_area 上的 INDEX 仅设置。没有创建其他索引,因此 MySQL 将始终使用 floor_area 作为唯一的键。查询时间和检查的行记录如下:

query number              1    2    3    4    5    6    7    8    9    10
user action on map     start   >    +    +    <    ^    +    >    v    +
time in seconds          138  0.21 0.43 32.3 0.12 0.12 36.3 4.33 0.33 2.00
rows examined ('000)      43    43   43   60   43   43  111  139  133  176

查询执行计划如下:

+----+-------------+-------+--------+---------------+---------+---------+--------------------+---------+-------------+
| id | select_type | table | type   | possible_keys | key     | key_len | ref                | rows    | Extra       |
+----+-------------+-------+--------+---------------+---------+---------+--------------------+---------+-------------+
|  1 | SIMPLE      | P     | range  | id_flA        | id_flA  | 3       | NULL               | 4223660 | Using where |
|  1 | SIMPLE      | E     | eq_ref | PRIMARY       | PRIMARY | 3       | BuySell.P.currency |       1 | Using where |
+----+-------------+-------+--------+---------------+---------+---------+--------------------+---------+-------------+

正在做几次测试,结果和上面的比较一致。查询次数 4 和查询次数 7 中查询时间激增的原因是什么?我该如何降低它?

更新:

按照 Digital Precision 的建议删除 ORDER BY 的结果:

query number              1    2    3    4    5    6    7    8    9    10
user action on map     start   >    +    +    <    ^    +    >    v    +
time in seconds          255  3.10 3.16 3.08 3.18 3.21 3.32 3.18 3.17 3.80
rows examined ('000)     131  131  131  131  136  136  136  136  136  157

查询执行计划与上面相同,但看起来更像是表扫描。请注意,我使用的是 MyISAM 引擎,版本 5.5.14。

根据要求,架构如下:

| Property | CREATE TABLE `Property` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `type` char(1) NOT NULL DEFAULT '',
  `lat` decimal(6,4) NOT NULL DEFAULT '0.0000',
  `lng` decimal(7,4) NOT NULL DEFAULT '0.0000',
  `floor_area` mediumint(8) unsigned NOT NULL DEFAULT '0',
  `currency` char(3) NOT NULL DEFAULT '',
  `price` int(10) unsigned NOT NULL DEFAULT '0',
  `closing_date` date NOT NULL DEFAULT '0000-00-00',
  `name` char(25) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`),
  KEY `id_flA` (`floor_area`)
) ENGINE=MyISAM AUTO_INCREMENT=5000000 DEFAULT CHARSET=latin1

| Exchange | CREATE TABLE `Exchange` (
  `currency` char(3) NOT NULL,
  `rate` decimal(11,10) NOT NULL DEFAULT '0.0000000000',
  PRIMARY KEY (`currency`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1

第二次更新:

我认为在 my.cnf 配置文件中发布非默认参数是合适的,因为有两个回答者提到了参数:

max_heap_table_size = 1300M
key_buffer_size = 0
read_buffer_size = 1300M
read_rnd_buffer_size = 1024M
sort_buffer_size = 1300M

我的测试服务器上有 2GB RAM。

最佳答案

我想我弄清楚了尖峰的原因。这是怎么回事:

首先我创建了表并加载了一些随机生成的数据:

这是我的查询:

SELECT SQL_NO_CACHE P.id, P.name, P.lat, P.lng, P.price * E.rate AS 'ask' 
FROM Property P
 INNER JOIN Exchange E ON E.currency = P.currency
 WHERE P.floor_area >= 2000
  AND P.closing_date >= CURDATE()
  AND P.type ='c'
  AND P.lat BETWEEN 12.00 AND 22.00
  AND P.lng BETWEEN 10.00 AND 20.00
  AND P.price BETWEEN 100 / E.rate AND 10000 / E.rate
 ORDER BY P.floor_area DESC LIMIT 100;

这是描述:

+----+-------------+-------+-------+---------------+--------+---------+------+---------+----------------------------------------------+
| id | select_type | table | type  | possible_keys | key    | key_len | ref  | rows    | Extra                                        |
+----+-------------+-------+-------+---------------+--------+---------+------+---------+----------------------------------------------+
|  1 | SIMPLE      | P     | range | id_flA        | id_flA | 3       | NULL | 4559537 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | E     | ALL   | PRIMARY       | NULL   | NULL    | NULL |       6 | Using where; Using join buffer               |
+----+-------------+-------+-------+---------------+--------+---------+------+---------+----------------------------------------------+

每次查询数据都花费了 3.5 ~ 3.9 秒(我使用的参数没有任何区别)。这没有意义所以我研究了Using join buffer

然后我想在没有“连接缓冲区”的情况下尝试这个查询,所以我向 Exchange 表中插入了 1 个随机数据。

INSERT INTO Exchange(currency, rate) VALUES('JJ', 1);

现在我使用相同的 sql,响应时间为 0.3 ~ 0.5 秒。这是描述:

+----+-------------+-------+--------+---------------+---------+---------+-----------------+---------+-------------+
| id | select_type | table | type   | possible_keys | key     | key_len | ref             | rows    | Extra       |
+----+-------------+-------+--------+---------------+---------+---------+-----------------+---------+-------------+
|  1 | SIMPLE      | P     | range  | id_flA        | id_flA  | 3       | NULL            | 4559537 | Using where |
|  1 | SIMPLE      | E     | eq_ref | PRIMARY       | PRIMARY | 3       | test.P.currency |       1 | Using where |
+----+-------------+-------+--------+---------------+---------+---------+-----------------+---------+-------------+

所以问题(据我所知)是优化器试图使用“连接缓冲区”。这个问题的最佳解决方案是强制优化器不使用“连接缓冲区”。 (我找不到方法)或更改“join_buffer_size”值。我通过向 Exchange 表添加“虚拟”值来解决它(因此优化器不会使用连接缓冲区)但这不是一个精确的解决方案,它只是愚弄 mysql 的愚蠢技巧。

编辑: 我在 mysql 论坛/错误中研究了这种“加入缓冲区”行为;然后在official forums中询问.我将填写一份关于优化器这种不合理行为的错误报告。

关于mysql - 尽管检查的行数相似,为什么查询时间会激增?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8722343/

相关文章:

mysql - 如何同时更新Redis和MySQL中的数据?

sql - 可以从 sql plus 运行 oracle 包而不将其编译到数据库中吗?

mysql - 如果 new_value <= value_from_different_row 有条件更新

javascript - 哪个性能更好: XmlHttpRequest response as HTML or JSON?

mysql - IN mysql 带有小数值

mysql 无法删除损坏的 innodb 表

mysql - SQL好友数据表查询

mysql - 通用投票表与单独的投票表?

java - try-catch 区域对性能有什么影响?

mysql - 将Google表格中的特定范围插入到MYSQL