几乎在任何地方我都读到使用FORCE INDEX
时非常气馁,我完全理解并知道原因 - MySQL有更大的机会知道选择哪些索引比(普通)开发人员更好。
但是,最近我发现FORCE INDEX
在一百次的范围内改善执行时间的情况:
JOIN
INNER JOIN
ed表的记录超过1百万published_date
的字段,以YMD格式存储为varchar
(无法更改为datetime
)published_date
的范围GROUP BY
不同的字段上的其他ORDER BY
和published_date
条款虽然我已经以多种方式重写了查询,但我的执行时间不到130秒(最高超过700秒)。将FORCE INDEX
与published_date
一起使用后,执行时间降至5秒以下。
我花了几天的时间来记住臭名昭着的FORCE INDEX
选项。
问题:
FORCE INDEX
为您节省了哪些其他用例?FORCE INDEX
时,您是否有一些最佳做法?编辑 - Obsevations: 我也在这里创建了this blog post这个问题。你提供的所有答案也会出现在那里 - 有学分和你想要的所有东西。
修改2
我在评论中应用了我收到的建议(ANALYZE TABLE
和OPTIMIZE TABLE
),下面是EXPLAIN
在查询时应用的输出 - 遗憾的是,索引选择完全没有改善:
FORCE INDEX
的桌子上没有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
FORCE INDEX
的桌面上使用别名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
ANALYZE TABLE
之后,没有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
OPTIMIZE TABLE
之后,没有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
OPTIMIZE TABLE
和ANALYZE TABLE
后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
答案 0 :(得分:7)
我注意到FORCE INDEX在VARCHAR字段上有多个连接和子查询时有帮助,其中FK和引用的值都不是主键,同时在DATE字段上有where子句。 / p>
类似的东西:
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';
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;
答案 1 :(得分:5)
我通过你的EXPLAIN
计划注意到表序列已经改变,前两个表被颠倒了,除了使用日期索引之外,这可能是你的性能改进的来源。 / p>
您是否考虑过在查询中使用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
使用STRAIGHT_JOIN
来保持此序列导致查询性能远远大于INNER JOIN
等效,这基本上只是颠倒了表的顺序。
返回原始查询,删除强制索引,并将INNER JOIN
替换为STRAIGHT_JOIN
,然后查看解释计划为您提供的内容。
您可能还想使用a
和pub_date
在serial
表格上创建综合索引,我认为这将进一步改进查询。