为JOIN + LIKE xyz%查询正确设置索引

时间:2014-07-30 19:12:23

标签: mysql indexing query-optimization database-performance query-performance

EDIT3:这是真正的问题:

它现在使用Contact的复合索引,但EXPLAIN仅列出Address的fkey索引,既不列出我的多列索引也不列出任何其他单列索引。

EXPLAIN EXTENDED SELECT c.*
FROM Contact_Contact c 
LEFT OUTER JOIN Address a ON c.id=a.Contact_id 
WHERE c.deleted<>1 
AND c.external<>1 
AND (
  c.firstName LIKE 'abc%' OR
  c.lastName LIKE 'abc%' OR
  c.Company LIKE 'abc%' OR
  c.label LIKE 'abc%' OR
  a.street LIKE 'abc%' OR
  a.city LIKE 'abc%' OR
  a.region LIKE 'abc%') 
GROUP BY c.id 
ORDER BY c.lastName ASC
LIMIT 30

输出:

# id, select_type, table, type, possible_keys, key, key_len, ref, rows, filtered, Extra
1, SIMPLE, c, range, myCompositeContactIdx,firstNameIdx,lastNameIdx, myCompositeContactIdx, 1, , 66241, 100.00, Using where; Using temporary; Using filesort
1, SIMPLE, a, ref, FK_dc6mn93h87cim4yr8nnijj3fa,myCompositeAddressIdx , FK_dc6mn93h87cim4yr8nnijj3fa, 17, c.id, 1, 100.00, Using where

FK_dc6mn93h87cim4yr8nnijj3faAddress.Contact_id

上的fkey

所以看起来它使用Contact上的多列索引,但它不使用Address上的多列,其定义为:

ADD INDEX `myCompositeAddressIdx` USING BTREE (
  `Contact_id` ASC, `street`(255) ASC, `city`(255) ASC, `region`(255) ASC
);

知道如何使用myCompositeAddressIdx


EDIT4:修改后的查询,快30%,子查询没有索引:

这是我的新查询,需要250毫秒(旧:350毫秒)

SELECT c.*
FROM Contact_Contact c
WHERE c.deleted<>1 
AND c.external<>1 
AND (
  c.firstName LIKE 'abc%' OR
  c.lastName LIKE 'abc%' OR
  c.Company LIKE 'abc%' OR
  c.label LIKE 'abc%' OR
  c.id IN (SELECT a.Contact_id FROM Address a WHERE a.street LIKE 'abc%' OR a.city LIKE 'abc%' OR a.region LIKE 'abc%')
  -- ALTERNATIVE:
  -- EXISTS (SELECT 1 FROM Address a WHERE a.Contact_id=c.id AND (a.street LIKE 'abc%' OR a.city LIKE 'abc%' OR a.region LIKE 'abc%'))
)
GROUP BY c.id 
ORDER BY c.lastName ASC
LIMIT 30

INEXISTS似乎同样快“

EXPLAIN仍然说,它不使用Address表上的索引:

# id, select_type, table, type, possible_keys, key, key_len, ref, rows, filtered, Extra
1, PRIMARY, c, range, myCompositeContactIdx,firstNameIdx,lastNameIdx, myCompositeContactIdx, 1, , 67138, 100.00, Using where; Using filesort
2, DEPENDENT SUBQUERY, a, index_subquery, FK_dc6mn93h87cim4yr8nnijj3fa,myCompositeAddressIdx,myNEWCompositeAddressIdx, FK_dc6mn93h87cim4yr8nnijj3fa, 17, func, 1, 100.00, Using where

myNEWCompositeAddressIdx定义为:

ADD INDEX `myNEWCompositeAddressIdx` USING BTREE (
  `street`(255) ASC, `city`(255) ASC, `region`(255) ASC
);

原始问题:

我们的MySQL数据库存在一些性能问题,架构如下:

TABLE Contact:
id, firstName, lastName, company

TABLE Address:
id, contact_id, street, city

TABLE ContactGroup:
id, name

TABLE Contact_ContactGroup:
contact_id, contact_group_id

(还有一些表,但它总是一样的) (所有文本字段都是VARCHAR(最多255个))

我们的查询与输入"abc xyz"

的查询类似
SELECT c.*
FROM Contact c
LEFT JOIN Address a ON a.contact_id = c.id
LEFT JOIN Contact_ContactGroup ccg ON ccg.contact_id = c.id
WHERE
  (
    c.firstName LIKE 'abc%' OR c.firstName LIKE 'xyz%' OR
    c.lastName LIKE 'abc%' OR c.lastName LIKE 'xyz%' OR
    c.company LIKE 'abc%' OR c.company LIKE 'xyz%' OR
    a.street LIKE 'abc%' OR a.street LIKE 'xyz%' OR
    a.city LIKE 'abc%' OR a.city LIKE 'xyz%'
  ) AND
  ccg.contact_group_id IN (1, 2, 3)
GROUP BY
  c.id
ORDER BY
  c.lastName ASC

在我的开发机器上,接触表中大约130,000行需要 280ms 。 (我们的服务器需要超过2秒)

我们之前有一个DISTINCT而不是GROUP BY,但它似乎几乎相同。之前我们还有LIKE '%abc%',但我读到如果在搜索词前面有一张外卡,MySQL就无法使用索引。该查询大约需要 310毫秒

正如您所看到的,查询速度并没有明显提高。我想我确实把索引设置错了。我设置的内容:

Contact Index: primary key
id

Contact Index: BTREE
firstName ASC, lastName ASC, company ASC

Address Index: primary key
id

Address Index: BTREE
street ASC, city ASC

Contact_ContactGroup: compound primary key
contact_id, contact_group_id

我在中添加了索引所有数据已经​​在表格中。 MySQL会自动索引旧数据吗?添加索引的时间不会超过半秒,但我不确定它是否确实对数据进行了索引。而且我找不到强制重新索引表格的命令。

或者对每列使用单列索引而不是对所有列使用巨大的复合索引更好吗?

顺便说一下:我们正在使用MySQL 5.5和InnoDB Tables。

任何帮助将不胜感激。谢谢:)

1 个答案:

答案 0 :(得分:-1)

是否依赖于系统,导致此查询在我的服务器上耗时180毫秒,  我碰巧拥有你的数据库模式的精确副本,我也在PL / SQL过程开发之前编写了索引。

SELECT c.*
FROM Contact c LEFT JOIN Contact_ContactGroup ccg ON ccg.contact_id = c.id
 LEFT JOIN Address a ON a.contact_id = c.id

WHERE
  c.firstName  LIKE 'abc%xyz%' OR
  c.lastName   LIKE 'abc%xyz%' OR
  c.company   LIKE 'abc%xyz%' OR
  a.street   LIKE 'abc%xyz%' OR
  a.city   LIKE 'abc%xyz%' AND
  ccg.contact_group_id IN (1, 2, 3)
GROUP BY
  c.id
ORDER BY
  c.lastName ASC