Que*_*low 7 mysql sql testing indexing performance
我正在通过慢查询日志来尝试确定为什么一些查询行为不正常.为了保持一致性,在运行测试之前不会缓存查询并执行刷新以清除系统缓存.查询是这样的:
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;
Run Code Online (Sandbox Code Playgroud)
的k?是用户定义的恒定值; v?是用户在地图上拖动或缩放时更改的变量.从表中拉出100个结果,并按照楼面面积按降序排序.
仅设置PRIMARY键id和INDEX on floor_area.没有创建其他索引,因此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
Run Code Online (Sandbox Code Playgroud)
查询执行计划如下:
+----+-------------+-------+--------+---------------+---------+---------+--------------------+---------+-------------+
| 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 |
+----+-------------+-------+--------+---------------+---------+---------+--------------------+---------+-------------+
Run Code Online (Sandbox Code Playgroud)
测试正在进行几次,结果与上述结果非常一致.查询号码4和号码7 中查询时间的峰值可能是什么原因以及如何将其关闭?
更新:
ORDER BYDigital Precision建议删除的结果:
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
Run Code Online (Sandbox Code Playgroud)
查询执行计划与上面相同,虽然看起来更像是表扫描.请注意,我使用的是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
Run Code Online (Sandbox Code Playgroud)
第二次更新:
我认为在my.cnf配置文件中发布非默认参数是合适的,因为两个回答者提到了这些参数:
max_heap_table_size = 1300M
key_buffer_size = 0
read_buffer_size = 1300M
read_rnd_buffer_size = 1024M
sort_buffer_size = 1300M
Run Code Online (Sandbox Code Playgroud)
我的测试服务器上有2GB的RAM.
有几件事:
为什么要在 SELECT 中计算 P.price 和 E.rate 的乘积并别名为“ask”,然后在 where 子句中再次进行计算?应该能够做到AND ask BETWEEN k? and k?--编辑:由于MySQL的工作方式,这不会起作用。显然,MySQL 在任何别名( sourced )之前评估 WHERE 子句。
你们在 Exchange.currency 和 Property.currency 上有什么样的索引?如果交换是一个查找表,也许您最好添加一个带有 Property.Id 和 Exchange.Id 的数据透视(链接)表
order by Floor_area 强制 MySQL 创建临时表以便正确排序,您有机会在应用程序层进行排序吗?
在类型列上添加索引也会有所帮助。
-编辑
// this and key_buffer_size=0 prevents caching不确定CURDATE where 条件上的注释是什么意思,您可以在select语句上使用“SQL_NO_CACHE”标志强制不进行 sql 缓存。
现在我建议您删除 ORDER BY 后,按如下方式更新查询语句(向列添加 P 别名以减少任何混淆):
WHERE P.type ='condominium'
    AND P.floor_area >= k?
    AND P.closing_date >= CURDATE() // No longer necessary with SQL_NO_CACHE
    AND P.lat BETWEEN v? AND v?
    AND P.lng BETWEEN v? AND v?
    AND P.price * E.rate BETWEEN k? AND k?
Run Code Online (Sandbox Code Playgroud)
然后向“type”列添加索引,并在“type”和“floor_area”列上添加复合索引。正如您所说,类型列是低基数列,但表很大并且应该有所帮助。即使 Floor_area 看起来是一个高基数列,复合索引也将有助于加快查询时间。
您可能还想研究使用 BETWEEN 而不是范围运算符(>、<、<= 等)是否会产生惩罚。
|   归档时间:  |  
           
  |  
        
|   查看次数:  |  
           855 次  |  
        
|   最近记录:  |