MySQL 有时不使用 INDEX 但有时会使用
Posted
技术标签:
【中文标题】MySQL 有时不使用 INDEX 但有时会使用【英文标题】:MySQL sometimes not using the INDEX but sometimes it does 【发布时间】:2017-03-25 10:46:32 【问题描述】:有时我的查询没有使用索引,但有时确实使用了。你们能解释一下为什么会这样吗?
这是表结构。
MariaDB [crm]> desc vtiger_project;
+------------------------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------------------+---------------+------+-----+---------+-------+
| projectid | int(11) | NO | PRI | 0 | |
| projectname | varchar(100) | YES | | NULL | |
| projecttype | varchar(50) | YES | | NULL | |
| siteaddress | varchar(500) | YES | | NULL | |
| state | varchar(100) | YES | | NULL | |
| district | varchar(100) | YES | | NULL | |
| city | varchar(100) | YES | | NULL | |
| pincode | varchar(100) | YES | | NULL | |
| phone | varchar(100) | YES | | NULL | |
| startdate | date | YES | MUL | NULL | |
| branch | varchar(100) | YES | | NULL | |
| customer | int(11) | YES | MUL | NULL | |
| dealer | int(11) | YES | | NULL | |
| contractor | int(11) | YES | MUL | NULL | |
| architect | int(11) | YES | MUL | NULL | |
| carpenter | int(11) | YES | MUL | NULL | |
| productcategory | varchar(100) | YES | | NULL | |
| brand_preferred | varchar(100) | YES | | NULL | |
| formal_spec_check | varchar(3) | YES | | NULL | |
| formal_spec_details | varchar(250) | YES | | NULL | |
| projectstatus | varchar(25) | YES | | NULL | |
| project_reason_loosing | varchar(100) | YES | | NULL | |
| reason_loosing_deatils | varchar(250) | YES | | NULL | |
| reason_winning_deatils | varchar(250) | YES | | NULL | |
| adjustment | decimal(25,8) | YES | | NULL | |
| exciseduty | decimal(25,3) | YES | | NULL | |
| total | decimal(25,8) | YES | | NULL | |
| subtotal | decimal(25,8) | YES | | NULL | |
| taxtype | varchar(25) | YES | | NULL | |
| discount_percent | decimal(25,3) | YES | | NULL | |
| discount_amount | decimal(25,8) | YES | | NULL | |
| s_h_amount | decimal(25,8) | YES | | NULL | |
| currency_id | int(19) | NO | | 1 | |
| conversion_rate | decimal(10,3) | NO | | 1.000 | |
| actual_sale | varchar(255) | YES | | NULL | |
| expected_sale_in_na | varchar(255) | YES | | NULL | |
| primary_decision_maker | varchar(100) | YES | | NULL | |
+------------------------+---------------+------+-----+---------+-------+
正如您在下面的输出中看到的,第一个查询确实命中了索引,但第二个没有,我对该查询所做的唯一更改是 startdate 部分。 我做错了什么?
MariaDB [crm]> explain SELECT
-> COUNT(projectid)
-> FROM
-> vtiger_project
-> WHERE
-> 82582 IN (customer , contractor, architect, carpenter)
-> AND projectstatus NOT IN ('Supplied' , 'Closed As Complete', 'Closed As Lost')
-> AND actual_sale IS NULL
-> AND startdate > NOW()
-> ;
+------+-------------+----------------+-------+---------------+---------------+---------+------+------+------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+----------------+-------+---------------+---------------+---------+------+------+------------------------------------+
| 1 | SIMPLE | vtiger_project | range | startdate_idx | startdate_idx | 4 | NULL | 352 | Using index condition; Using where |
+------+-------------+----------------+-------+---------------+---------------+---------+------+------+------------------------------------+
1 row in set (0.00 sec)
MariaDB [crm]> explain SELECT
-> COUNT(projectid)
-> FROM
-> vtiger_project
-> WHERE
-> 82582 IN (customer , contractor, architect, carpenter)
-> AND projectstatus NOT IN ('Supplied' , 'Closed As Complete', 'Closed As Lost')
-> AND actual_sale IS NULL
-> AND startdate < NOW()
-> ;
+------+-------------+----------------+------+---------------+------+---------+------+-------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+----------------+------+---------------+------+---------+------+-------+-------------+
| 1 | SIMPLE | vtiger_project | ALL | startdate_idx | NULL | NULL | NULL | 15779 | Using where |
+------+-------------+----------------+------+---------------+------+---------+------+-------+-------------+
【问题讨论】:
查询优化器会做它认为最适合查询的事情。这可能意味着有时如果索引对查询没有帮助,则不会使用它。 【参考方案1】:答案在解释计划的行列中
在第一种情况下你有352
在第二个15779
这建议查询优化器在两个查询之间使用不同的策略。
也可能是您在不同的服务器上执行这两个查询,或者在解释无法评估的其他不同条件下执行这两个查询
【讨论】:
我在同一台服务器上执行查询,唯一的变化是 startdate NOW() 它使用索引.. 然后是所涉及的行数 a.. 正如我的回答中所建议的那样.. 导致查询优化器以不同的方式工作.. 您可以强制使用索引添加强制使用指定如果您认为索引效果更好,请索引dev.mysql.com/doc/refman/5.7/en/index-hints.html @Salim 唯一的变化是 startdate ...如果一个重要的数字,也许大多数 startdate 值是 对特定查询使用索引 是没有意义的。超过表中总行数的一定百分比,全扫描是比使用索引更有效的策略,因为从匹配行中读取其他列需要在使用索引时进行额外的读取。当大量行匹配时,扫描避免了这种情况。一开始并不直观,但这种行为对我来说似乎也是正确的。【参考方案2】:(sqlbot 的评论回答了这个问题。)
此复合索引可能可以进一步优化:
INDEX(actual_sale, startdate)
但是,只有 actual_sale
不是 NULL
'太频繁',这可能会很好。否则,全表扫描会更好,但优化器可能无法解决这个问题。
您的许多列看起来过大。这种过度行为会导致性能问题。
【讨论】:
以上是关于MySQL 有时不使用 INDEX 但有时会使用的主要内容,如果未能解决你的问题,请参考以下文章
CLLocation Manager 有时会给出不正确的位置