MySQL`FORCE INDEX`用例?

Tud*_*tin 24 mysql

几乎在任何地方,我FORCE INDEX都非常沮丧地使用它,我完全理解并知道原因 - MySQL有更大的机会知道哪些索引比(普通)开发人员更好.

但是,最近我发现了一个案例,它FORCE INDEX在几百次的范围内改进了我的执行时间:

  • JOIN 在4张桌子上
  • 第一张表有大约500 000条记录
  • INNER JOINed表中的2个有超过1百万的记录
  • 第一个表有一个名为的字段published_date,varchar以YMD格式存储(无法更改为datetime)
  • 需要published_date最多5 000条记录的范围
  • 第一个表上的一些其他GROUP BYORDER BY子句在不同的字段上,而不是published_date此查询所需的

虽然我以多种方式重写了查询,但我的执行时间不到130秒(最高超过700秒).使用FORCE INDEXpublished_date,执行时间降至5秒以下.

我花了几天时间才想起臭名昭着的FORCE INDEX选择.

问题:

  • 您找到的其他用例是什么FORCE INDEX
  • 考虑使用时,您是否有一些最佳实践FORCE INDEX

编辑 - Obsevations: 我也在这里创建了这篇博文.你提供的所有答案也会出现在那里 - 有学分和你想要的所有东西.

编辑2

I applied the suggestions I received in your comments (ANALYZE TABLE and OPTIMIZE TABLE), below is the output of EXPLAIN applied on query - unfortunately the index selection is not at all better:

1. without FORCE INDEX on table with alias a:

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
Run Code Online (Sandbox Code Playgroud)

2. with FORCE INDEX on table with alias a:

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
Run Code Online (Sandbox Code Playgroud)

3. after ANALYZE TABLE, without FORCE 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
Run Code Online (Sandbox Code Playgroud)

4. after OPTIMIZE TABLE, without FORCE 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
Run Code Online (Sandbox Code Playgroud)

5. after OPTIMIZE TABLE and ANALYZE TABLE, with FORCE 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
Run Code Online (Sandbox Code Playgroud)

Mar*_*ski 7

我注意到FORCE INDEX有助于在VARCHAR字段上有多个连接和子查询,其中FK和引用的值都不是主键,同时在DATE字段上有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';
Run Code Online (Sandbox Code Playgroud)

mysql将始终使用PK和FK,您可以在其中首先使用payment表上的payment_date索引,因为它是最大的一个.因此FORCE INDEX(payment_date),支付表加入会有很大帮助.

这是我们在工作中使用的第三方计费数据库中的一个示例.我们在优化方面遇到了很大的问题,FORCE INDEX大部分时间都在做这项工作.通常我们使用mysqladmin找到缓慢的quires,用FORCE INDEX测试它们并将它们发送给供应商,以便在应用程序的源代码中重写它们.

以下是四个表格,以便更好地掌握示例:

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;
Run Code Online (Sandbox Code Playgroud)


Dav*_*Rix 5

通过您的EXPLAIN计划,我注意到表顺序已更改,前两个表颠倒了,除了使用日期索引之外,这很可能是您性能提高的来源。

您是否研究过STRAIGHT_JOIN在查询中使用来强制表的顺序?

我研究了一个大型数据库架构,其中最佳连接配置STRAIGHT_JOIN在整个查询过程中一直使用,并且性能比INNER JOIN同等性能提高了100倍。

不幸的是,我再也无法访问系统来获得一些示例EXPLAIN计划,但是最佳表顺序却是这样。

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
Run Code Online (Sandbox Code Playgroud)

使用STRAIGHT_JOINs来保持此顺序会导致查询性能远高于INNER JOIN等效结果,这实际上只是颠倒了表的顺序。

返回到原始查询,删除力索引,将INNER JOINs 替换为s,STRAIGHT_JOIN然后查看解释计划为您提供的内容。

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