MySQL`FORCE INDEX`用例?

MySQL`FORCE INDEX`用例?,mysql,Mysql,几乎在我读到的每一篇文章中,使用强制索引都是非常令人沮丧的,我完全理解并知道原因——MySQL很有可能比(一般的)开发人员更清楚应该选择什么索引 但是,最近我发现一个案例,FORCE INDEX将我的执行时间提高了数百倍: JOIN在4个表上 第一个表有大约50万条记录 内部联接ed表中有2个记录超过100万条 第一个表有一个名为published_date的字段,以YMD格式存储为varchar(无法更改为datetime) 在发布日期上需要最多5000条记录的范围 此查询需要在第一个表中的

几乎在我读到的每一篇文章中,使用
强制索引
都是非常令人沮丧的,我完全理解并知道原因——MySQL很有可能比(一般的)开发人员更清楚应该选择什么索引

但是,最近我发现一个案例,
FORCE INDEX
将我的执行时间提高了数百倍:

  • JOIN
    在4个表上
  • 第一个表有大约50万条记录
  • 内部联接
    ed表中有2个记录超过100万条
  • 第一个表有一个名为
    published_date
    的字段,以YMD格式存储为
    varchar
    (无法更改为
    datetime
  • 在发布日期上需要最多5000条记录的范围
  • 此查询需要在第一个表中的
    发布日期
    以外的不同字段上的
    GROUP BY
    ORDER BY
    子句
尽管我以多种方式重写了查询,但我无法获得小于130秒的执行时间(最高超过700秒)。将
强制索引
发布日期
一起使用后,执行时间降至5秒以下

我花了几天时间才想起臭名昭著的
FORCE INDEX
选项

问题:

  • FORCE INDEX
    保存您的地方,您还发现了哪些其他用例
  • 当您考虑使用<代码>力索引 > 时,您是否有一些<强>最佳实践<>强>?
编辑-观察事项: 我在这里也提出了这个问题。你提供的所有答案也会出现在那里——包括学分和你想要的所有东西

编辑2

我应用了在您的评论中收到的建议(
ANALYZE TABLE
OPTIMIZE TABLE
),下面是应用于查询的
EXPLAIN
的输出-不幸的是,索引选择一点也不好:

1.在别名为
a的表上没有
强制索引
: 2.在别名为
a的表上使用
强制索引
: 3.在
分析表
之后,没有
力索引
: 4.在
优化表之后
,没有
强制索引
: 5.在
优化表
分析表
之后,使用
力索引

我注意到,当您在VARCHAR字段上有多个联接和子查询,其中FK和引用值都不是主键,同时在日期字段上有where子句时,强制索引会有所帮助

比如:

SELECT NAME, a.reference_no, i.value, p.value FROM customers AS c
INNER JOIN accounts AS a ON c.id = a.customer_id
INNER JOIN invoices AS i ON i.reference_no = a.reference_no
INNER JOIN payments AS p ON p.invoice_no = i.invoice_no
WHERE payments.date >= '2011-09-01' AND DATE < '2011-10-01';

通过您的
EXPLAIN
计划,我注意到表格顺序已经改变,前两个表格颠倒了,除了使用日期索引之外,这很可能是您绩效改进的原因

您是否研究过在查询中使用
直接连接
来强制表的顺序

我曾经在一个大型数据库模式上工作过,在这个模式中,最佳连接配置在整个查询过程中一直使用
stright\u join
s,性能比
内部连接
等价物提高了100倍

不幸的是,我再也无法访问该系统来获取一些示例
解释
计划,但是最佳的表序列是这样的

Table 1           10 rows              1 analysed
Table 2           500 rows             50 analysed
Table 3           1,000,000 rows       300,000 analysed
Table 4           500,000,000 rows     4,000,000 analysed
使用
stright\u JOIN
s来保持这个顺序会导致查询性能远远高于
内部连接
等价物,后者实际上只是颠倒了表的顺序

返回到原始查询,删除强制索引,并将
内部联接
s替换为
直接联接
s,然后查看解释计划给出了什么


您可能还希望使用
pub_date
serial
a
表上创建一个复合索引,我认为这将进一步改进查询。

您是否对必须
强制索引的表运行了
分析表
?我的经验还告诉我,您很少需要指示计划员使用某个索引,大多数情况下,它的选择很差,因为索引已损坏或不好,这将通过ANALYZE修复。@Romain-没有运行分析表…很好idea@TudorConstantin您可以尝试分析它们,然后将新的“普通”查询计划与“强制”查询计划进行比较。。。强制使用特定索引的问题是,即使今天的性能更好,也无法轻松预测修改表统计信息时会产生什么后果,特别是对于复杂的查询。查询优化器几乎总是选择最佳的执行计划,并且还可以适应更改。除了使用ANALYZE更新表统计信息外,如果您正在进行大量修改(大量删除/插入),也可以使用OPTIMIZE对索引页进行排序。为什么MySQL总是在表
客户
上使用主键?这取决于联接中表的顺序吗?我已经编辑了这篇文章。。。我遇到的更大的问题是没有在付款表上使用日期索引。然而,我认为表的顺序确实会影响正在使用的索引的顺序。您可以谈论这个话题,但不要在任何地方向我们展示这个强制索引解决方案的任何参考。如果您打算建议我们在第一个查询中使用它,为什么不列出第二个实现了强制索引的查询呢?谢谢你的帮助。
id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   SIMPLE  am2 range   PRIMARY,idx_meta_article    idx_meta_article    4   NULL    275228  Using where; Using index; Using temporary; Using f...
1   SIMPLE  a   eq_ref  PRIMARY,serial_issue_date_productid,pub_date,idx_d...   PRIMARY 4   mydb_toto.am2.ArticleID 1   Using where
1   SIMPLE  ai  ref PRIMARY,idx_iso_article PRIMARY 4   mydb_toto.a.serial  11523   Using where; Using index
1   SIMPLE  m   range   PRIMARY,meta_articles_type  meta_articles_type  4   NULL    96  Using where
1   SIMPLE  am  eq_ref  PRIMARY,idx_meta_article    PRIMARY 8   mydb_toto.a.serial,mydb_toto.m.meta_id  1   Using where; Using index
id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   SIMPLE  am2 range   PRIMARY,idx_meta_article    idx_meta_article    4   NULL    275228  Using where; Using index; Using temporary; Using f...
1   SIMPLE  a   eq_ref  PRIMARY,serial_issue_date_productid,pub_date,idx_d...   PRIMARY 4   mydb_toto.am2.ArticleID 1   Using where
1   SIMPLE  ai  ref PRIMARY,idx_iso_article PRIMARY 4   mydb_toto.a.serial  11523   Using where; Using index
1   SIMPLE  m   range   PRIMARY,meta_articles_type  meta_articles_type  4   NULL    96  Using where
1   SIMPLE  am  eq_ref  PRIMARY,idx_meta_article    PRIMARY 8   mydb_toto.a.serial,mydb_toto.m.meta_id  1   Using where; Using index
id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   SIMPLE  a   range   pub_date    pub_date    11  NULL    17679   Using where; Using temporary; Using filesort
1   SIMPLE  am2 ref PRIMARY,idx_meta_article    PRIMARY 4   mydb_toto.a.serial  21930   Using where; Using index
1   SIMPLE  ai  ref PRIMARY,idx_iso_article PRIMARY 4   mydb_toto.a.serial  11523   Using where; Using index
1   SIMPLE  m   range   PRIMARY,meta_articles_type  meta_articles_type  4   NULL    96  Using where
1   SIMPLE  am  eq_ref  PRIMARY,idx_meta_article    PRIMARY 8   mydb_toto.am2.ArticleID,mydb_toto.m.meta_id 1   Using where; Using index
SELECT NAME, a.reference_no, i.value, p.value FROM customers AS c
INNER JOIN accounts AS a ON c.id = a.customer_id
INNER JOIN invoices AS i ON i.reference_no = a.reference_no
INNER JOIN payments AS p ON p.invoice_no = i.invoice_no
WHERE payments.date >= '2011-09-01' AND DATE < '2011-10-01';
CREATE TABLE `customers` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `name` varchar(100) NOT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1;

CREATE TABLE `accounts` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `customer_id` int(11) NOT NULL,
  `reference_no` varchar(10) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `reference_no_uniq` (`reference_no`),
  KEY `FK_accounts` (`customer_id`),
  CONSTRAINT `FK_accounts` FOREIGN KEY (`customer_id`) REFERENCES `customers` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=latin1;

CREATE TABLE `invoices` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `reference_no` varchar(10) NOT NULL,
  `invoice_no` varchar(10) NOT NULL,
  `value` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `invoice_no_uniq` (`invoice_no`),
  KEY `FK_invoices` (`reference_no`),
  CONSTRAINT `FK_invoices` FOREIGN KEY (`reference_no`) REFERENCES `accounts` (`reference_no`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=latin1;

CREATE TABLE `payments` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `invoice_no` varchar(10) NOT NULL,
  `value` int(11) NOT NULL,
  `date` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `FK_payments` (`invoice_no`),
  KEY `payment_date` (`date`),
  CONSTRAINT `FK_payments` FOREIGN KEY (`invoice_no`) REFERENCES `invoices` (`invoice_no`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=latin1;
Table 1           10 rows              1 analysed
Table 2           500 rows             50 analysed
Table 3           1,000,000 rows       300,000 analysed
Table 4           500,000,000 rows     4,000,000 analysed