2009-12-04 97 views
1

我想在其中有200k条记录的表上使用以下查询。有各种其他可以被过滤的字段,但这是一个基本的例子。MySQL索引策略

SELECT b.isbn FROM books b 
WHERE 
b.price IS NOT NULL AND 
b.deleted = '' AND 
b.publication_date <= '2009-12-04' AND 
(
    b.subject1_id IN ('CAT1','CAT2','CAT3','CAT4','CAT5') OR 
    b.subject2_id IN ('CAT1','CAT2','CAT3','CAT4','CAT5') OR 
    b.subject3_id IN ('CAT1','CAT2','CAT3','CAT4','CAT5') 
) 

目前,我对所有这些字段都有一个单独的索引,这个查询需要4.5秒,这太长了。 EXPLAIN列出密钥下的NULL

我还尝试创建一个包含上述查询中所有字段的大型索引,但EXPLAIN显示未使用此多字段索引。

如何索引这些字段以加快查询速度?

编辑:这是我目前的指标(其中没有一个似乎是由查询使用):

  • 指数(价格)
  • 指数(删除)
  • 指数(publication_date)
  • 指数(subject1_id)
  • 指数(subject2_id)
  • 指数(subject3_id)
  • 指数(价格,删除,publication_date,subject1_id,subject2_id,subject3_id)

EDIT2:每ʞɔıu的答案 - 正火表和使用基本上是他查询后,它加速它的一些(现在是时候〜3.5秒),但没有我期待的那么多。我将新表作为PRIMARY KEY(isbn,subject_id)编制索引,并且此索引正在用于连接。

EDIT3:我在第二个表(subject_id,isbn)上添加了一个额外的索引,这有所帮助。在下面提到的其他索引的增加会有所帮助,但只有在查询中使用“FORCE INDEX”时才会使用。现在下降到大约1.5秒。是否有希望将其降低得多?

+0

尝试对主题表 – 2009-12-04 21:57:35

+0

也给出了相同的连接上添加一个索引上添加(主题,ISBN)其他指数(ISBN,出版日期,已删除,价格) – 2009-12-04 22:01:16

回答

3

在这种情况下,您需要对索引编制进行规范化,然后索引才会有帮助。

您可以创建包含另一个表(主题,ISBN),书本和主题添加索引,然后再加入到该表,如:

select b.isbn from books b 
inner join book_subject bs on bs.isbn=b.isbn 
where 
    b.price is not null and b.deleted != 'DELETED' 
    AND b.publication_date <= '2009-12-04' 
    AND bs.subject in ('CAT1', 'CAT2'...) 

的模式规范化规则#1(字面意思)是: “no repeating groups”。 在3个主题列的where子句中执行OR操作将阻止您能够利用该部分查询的索引。

(更新,以反映国际标准书号是主键)

+0

那么,它没有正常化的原因只是因为那是源数据的格式。将不得不加入一个200k记录的表格和另一个高达~60万记录的表格确实有助于查询的效率? – Wickethewok 2009-12-04 21:09:33

+0

很有可能,因为它可以使用索引,成本将是检查行数* log *的函数。 – 2009-12-04 21:14:05

+0

相当不错的东西 - 您可能希望将您对我的问题的评论添加到您的答案中,因为主题表上的附加索引非常重要。 – Wickethewok 2009-12-04 22:37:38

0

我想更多地了解您的查询的实际含义和可能帮助带路到你的答案。

让我们解开它来向你展示问题。

SELECT b.isbn FROM books b 
WHERE 
b.price IS NOT NULL AND 
b.deleted != 'DELETED' AND 
b.publication_date <= '2009-12-04' AND 
(
    b.subject1_id = 'CAT1' OR 
    b.subject1_id = 'CAT2' OR 
    b.subject1_id = 'CAT3' OR 
    b.subject1_id = 'CAT4' OR 
    b.subject1_id = 'CAT5' OR 
    b.subject2_id = 'CAT1' OR 
    b.subject2_id = 'CAT2' OR 
    b.subject2_id = 'CAT3' OR 
    b.subject2_id = 'CAT4' OR 
    b.subject2_id = 'CAT5' OR 
    b.subject3_id = 'CAT1' OR 
    b.subject3_id = 'CAT2' OR 
    b.subject3_id = 'CAT3' OR 
    b.subject3_id = 'CAT4' OR 
    b.subject3_id = 'CAT5' 

) 

很显然,没有任何索引会用于其他主题(price,deleted,publication_date,subject1)之外的其他索引。

索引中有哪些字段?

+0

关于索引字段,请参阅上面的编辑。 – Wickethewok 2009-12-04 21:15:57

0

关于尼克的帖子:

创建包含 (主题,book_id)另一个表上 书和主题添加索引:

岂不是更清洁有

select b.isbn from books b 

where 
#various table b where restrictions 

AND b.isbn IN (
    Select isbn 
    from book_subject bs 
    where bs.subject IN ('CAT1', 'CAT2' ...) 
) 
+0

我对你的回答有点困惑。如果有帮助,“isbn”是主键。 – Wickethewok 2009-12-04 21:12:27

+0

该查询需要一个临时表来存储子查询的中间结果;内部联接方法不会。与其他RDBMS相比,mysql的子查询操作并没有得到很好的优化。 – 2009-12-04 21:19:22

+0

更新为使用isbn作为PK – Zak 2009-12-04 21:31:50

0

首先:MySQL在选择期间每个表只能使用一个索引。它试图选择尽可能好的索引,但有时服务器有几个原因无法做出决定。只有一个字段有多个索引只会帮助你,如果你有很多语句一次只运行一个where子句。

为了优化在这里:你需要建立一个不包括该领域

price 
deleted 
publication_date 

不包括类别,索引,因为你正在使用的OR子句。

ALTER TABLE `test`.`books` ADD INDEX `idxPriceDeletedPublication`(`price`, `deleted`, `publication_date`); 

这应该然后给你以下EXPLAIN输出:

*************************** 1. row *************************** 
      id: 1 
    select_type: SIMPLE 
     table: b 
     type: range 
possible_keys: idxPriceDeletedPublication 
      key: idxPriceDeletedPublication 
     key_len: 5 
      ref: NULL 
     rows: 1 
     Extra: Using where 
1 row in set (0.00 sec) 
+0

它将无法为已删除列创建索引;你不能索引不等于。更好的策略是查询deleted ='NOTDELETED'或任何与'DELETED'状态相反的地方是 – 2009-12-04 21:40:01

+0

我添加了这三个组件索引。默认情况下,MySQL不会在我的查询中使用它,当我使用“FORCE INDEX”时,出于某种原因实际需要13秒。 'DELETED'的反义词是'',ʞɔıu是对的,我应该使用它。 – Wickethewok 2009-12-04 21:47:01