【问题标题】:How to force mariaDB to use index?如何强制mariaDB使用索引?
【发布时间】:2019-10-07 13:39:24
【问题描述】:

我正在尝试执行 SQL 查询。不幸的是,它不使用索引,而是进行表扫描。

我已经创建了以下索引:

  • PRIMARY($phone, $$fc_date)
  • idx $$fc_status_detail
  • idx $$fc_date
  • idx $$fc_status
  • idx $$电话

另外,我复制了表格,但这也没有提供任何有用的结果。

这是表结构:

+--------------------+--------------+------+-----+---------+-------+
| Field              | Type         | Null | Key | Default | Extra |
+--------------------+--------------+------+-----+---------+-------+
| $id                | varchar(100) | NO   |     | NULL    |       |
| $created_date      | varchar(100) | YES  |     | NULL    |       |
| $phone             | varchar(100) | NO   | PRI | NULL    |       |
| $source            | varchar(100) | YES  |     | NULL    |       |
| Orga               | varchar(100) | YES  |     | NULL    |       |
| Anrede             | varchar(100) | YES  |     | NULL    |       |
| Vorname            | varchar(100) | YES  |     | NULL    |       |
| Zuname             | varchar(100) | YES  |     | NULL    |       |
| Strasse            | varchar(100) | YES  |     | NULL    |       |
| PLZ                | varchar(100) | YES  |     | NULL    |       |
| Ort                | varchar(100) | YES  |     | NULL    |       |
| Geburtsdatum       | varchar(100) | YES  |     | NULL    |       |
| Email              | varchar(100) | YES  |     | NULL    |       |
| Zeitschrift        | varchar(100) | YES  |     | NULL    |       |
| Herkunft           | varchar(100) | YES  |     | NULL    |       |
| Zeitschrift_Titel  | varchar(100) | YES  |     | NULL    |       |
| telefon            | varchar(100) | YES  |     | NULL    |       |
| Stornogrund        | varchar(100) | YES  |     | NULL    |       |
| Storno             | varchar(100) | YES  |     | NULL    |       |
| Telefonnummer      | varchar(100) | YES  |     | NULL    |       |
| Postleitzahl       | varchar(100) | YES  |     | NULL    |       |
| Geburtsjahr        | varchar(100) | YES  |     | NULL    |       |
| $$fc_task          | varchar(100) | YES  |     | NULL    |       |
| $$fc_user          | varchar(100) | YES  |     | NULL    |       |
| $$fc_date          | varchar(100) | NO   | PRI | NULL    |       |
| $$fc_status        | varchar(100) | YES  | MUL | NULL    |       |
| $$fc_status_detail | varchar(100) | YES  | MUL | NULL    |       |
| $$qc_task          | varchar(100) | YES  |     | NULL    |       |
| $$qc_user          | varchar(100) | YES  |     | NULL    |       |
| $$qc_date          | varchar(100) | YES  |     | NULL    |       |
| $$qc_status        | varchar(100) | YES  |     | NULL    |       |
| $$qc_status_detail | varchar(100) | YES  |     | NULL    |       |
| $call_duration     | smallint(6)  | YES  |     | NULL    |       |
| $call_attempts     | smallint(6)  | YES  |     | NULL    |       |
+--------------------+--------------+------+-----+---------+-------+

这是查询:

EXPLAIN SELECT
    count(*) as total,
    CONCAT(case when c1.$$fc_date < 240 then "short" else "long" end, "/", c1.$$fc_status, "/", c1.$$fc_status_detail) as ergebnis,
    sum(case when c2.$$fc_status = 'success' then 1 else 0 end)/ count(*) as c2_succes_rate
FROM
    contacts c1 FORCE INDEX (PRIMARY),
    contacts_copy c2
WHERE
    c1.$phone = c2.$phone
    and c1.$$fc_date < c2.$$fc_date
group by
    ergebnis

这是结果:

+------+-------------+-------+------+--------------------------------------------------------------+---------+---------+---------------+---------+---------------------------------+
| id   | select_type | table | type | possible_keys                                                | key     | key_len | ref           | rows    | Extra                           |
+------+-------------+-------+------+--------------------------------------------------------------+---------+---------+---------------+---------+---------------------------------+
|    1 | SIMPLE      | c1    | ALL  | PRIMARY                                                      | NULL    | NULL    | NULL          | 2017450 | Using temporary; Using filesort |
|    1 | SIMPLE      | c2    | ref  | PRIMARY,contacts_copy_$phone_IDX,contacts_copy_$$fc_date_IDX | PRIMARY | 402     | nmv.c1.$phone |       1 | Using where                     |
+------+-------------+-------+------+--------------------------------------------------------------+---------+---------+---------------+---------+---------------------------------+

正如您在第 1 行中看到的,虽然它识别主键,但它不使用它。问题是它扫描了 2 Mio。行,查询持续至少 5 分钟。

谁能解释一下,可能是什么问题?

【问题讨论】:

  • 你能提供表格定义吗?我的印象是您正在尝试动态创建索引,而这正是占用您时间的原因。此外,如果没有这些,很难“调试”查询
  • 我已将结构包含在我的帖子中。
  • 由于我们不需要事务,我已经从 InnoDB 切换到 MyISAM Engine,但这并没有帮助。我已将“innodb_buffer_pool_size”增加到“5G”,但这也无济于事。
  • 你试过用JOIN contacts_copy c2 ON c1.$phone = c2.$phone and c1.$$fc_date &lt; c2.$$fc_date代替where吗?
  • @MarkoSeidenglanz 见Why should I provide an MCVE for what seems to me to be a very simple SQL query? 你能提供示例数据和预期结果吗.. 也许你的查询有更好的重写,结果相同但性能更好.. 另外我们必须知道你的 MySQL 版本因为在 MySQL 8.0 中获取连续记录更容易。我们也可以更好地测试优化,因为我们有数据优化器计划可以根据数据大小而改变。..

标签: mysql indexing mariadb explain


【解决方案1】:

您使用的是 InnoDB,对吗? InnoDB 辅助键(例如INDEX(phone))隐式包含PRIMARY KEY 的列。因此,索引实际上是(phone, fc_date) 的 BTree。

接下来,让我们注意需要c2 中的另一列:fc_status。因此,优化器查看了运行查询的两种方式。首先,请注意,任何一种方法都具有索引中的最佳列,并且它们处于最佳顺序。

方案A:使用索引,然后在索引和数据之间来回弹跳。

B 计划:进行表扫描,不需要来回。

优化器正确选择了 B。

你可以有一个更好的索引,优化器很可能会选择它。而且会更快:

INDEX(phone, fc_date, fc_status)  -- in this order

这是“覆盖”,因为所有需要的列都存在。因此,不会有任何来回。

我要批评VARCHAR(100)。这可能对日期非常不利,因为根据格式,它们可能无法正确排序。

名称可能永远不会是 100 个字符;电子邮件可能会更长。等4个电话号码?怎么了?

【讨论】:

  • 真的很喜欢你的解释(索引部分)!至于为什么一切都是 varchar,我认为他只是懒得转换,毕竟即使在示例查询中,他也将 $$fc_status 字段与 'success' 进行了比较,但他本可以在表中获得成功的布尔值。他可能刚刚开始使用数据库,还没有了解到使用正确的数据类型会更快并且通常会生成更小的数据库。我们不要用粗俗的语言吓唬他,他是新人!
  • 这个答案确实有助于增加我对表格设计的理解。不幸的是,使用组合 INDEX(phone, fc_date, fc_status) 查询没有加快速度。我已将所有内容导入 BigQuery。这解决了问题。
  • @MarkoSeidenglanz - 你是否也删除了FORCE INDEX (PRIMARY)。我对“索引提示”的看法:“它们今天可能会有所帮助,但明天可能会使事情变得更糟。”
  • 问题不是因为索引,而是因为坏数据。 $phone="" 有很多行,这会破坏整个查询。
猜你喜欢
  • 1970-01-01
  • 2013-06-13
  • 1970-01-01
  • 2010-09-23
  • 1970-01-01
  • 2017-05-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多