正常列和全文列的MySQL索引

时间:2017-04-30 11:59:07

标签: mysql full-text-search union query-performance database-indexes

我正在尝试加快下面的查询:

我的桌子有大约400万条记录。

EXPLAIN SELECT  * FROM chrecords WHERE  company_number = 'test'  OR MATCH (company_name,registered_office_address_address_line_1,registered_office_address_address_line_2) AGAINST('test') LIMIT 0, 10;
+------+-------------+-----------+------+------------------+------+---------+------+---------+-------------+
| id   | select_type | table     | type | possible_keys    | key  | key_len | ref  | rows    | Extra       |
+------+-------------+-----------+------+------------------+------+---------+------+---------+-------------+
|    1 | SIMPLE      | chrecords | ALL  | i_company_number | NULL | NULL    | NULL | 2208348 | Using where |
+------+-------------+-----------+------+------------------+------+---------+------+---------+-------------+
1 row in set (0.00 sec)

我使用以下方法创建了两个索引:

ALTER TABLE `chapp`.`chrecords` ADD INDEX `i_company_number` (`company_number`);

ALTER TABLE `chapp`.`chrecords`ADD FULLTEXT(
    `company_name`,
    `registered_office_address_address_line_1`,
    `registered_office_address_address_line_2`
);

但是如何“组合”两个索引呢?由于上述查询需要15秒以上才能执行(仅使用一个索引)。

整个表定义:

CREATE TABLE `chapp`.`chrecords` (
  `id` INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
  `company_name` VARCHAR(100) NULL,
  `company_number` VARCHAR(100) NULL,
  `registered_office_care_of` VARCHAR(100) NULL,
  `registered_office_po_box` VARCHAR(100) NULL,
  `registered_office_address_address_line_1` VARCHAR(100) NULL,
  `registered_office_address_address_line_2` VARCHAR(100) NULL,
  `registered_office_locality` VARCHAR(100) NULL,
  `registered_office_region` VARCHAR(100) NULL,
  `registered_office_country` VARCHAR(100) NULL,
  `registered_office_postal_code` VARCHAR(100) NULL
  );

ALTER TABLE `chapp`.`chrecords` ADD INDEX `i_company_name` (`company_name`);
ALTER TABLE `chapp`.`chrecords` ADD INDEX `i_company_number` (`company_number`);
ALTER TABLE `chapp`.`chrecords` ADD INDEX `i_registered_office_address_address_line_1` (`registered_office_address_address_line_1`);
ALTER TABLE `chapp`.`chrecords` ADD INDEX `i_registered_office_address_address_line_2` (`registered_office_address_address_line_2`);

ALTER TABLE `chapp`.`chrecords`ADD FULLTEXT(
    `company_name`,
    `registered_office_address_address_line_1`,
    `registered_office_address_address_line_2`
);

2 个答案:

答案 0 :(得分:1)

尝试使用UNION而不是OR

  SELECT *
    FROM (
       SELECT  * 
        FROM chrecords 
        WHERE company_number = 'test'
    ) a
    UNION (
       SELECT * 
         FROM cbrecords
        WHERE MATCH (company_name, 
                     registered_office_address_address_line_1, 
                     registered_office_address_address_line_2)
              AGAINST('test') 
        LIMIT 0, 10
     ) b

如果这有帮助,那是因为MySQL努力在一个子查询中使用多个索引。这为查询计划程序提供了两个查询。

您可以分别在每个子查询上运行EXPLAIN以了解其性能。 UNION只是将结果放在一起并消除重复。如果您想保留重复项,请执行UNION ALL

请注意,MySQL表上的大量单列索引通常对性能有害。您应该避免创建索引,除非它们构造为帮助特定查询。

答案 1 :(得分:1)

    (
        SELECT  *
            FROM  chrecords
            WHERE  company_number = 'test' 
            ORDER BY something
            LIMIT 10
    )
    UNION DISTINCT
    (
        SELECT  *
            FROM  cbrecords
            WHERE  MATCH (company_name, registered_office_address_address_line_1,
                                        registered_office_address_address_line_2)
                   AGAINST('test')
            ORDER BY something
            LIMIT 10
    ) 
    ORDER BY something
    LIMIT 10

注意:

  • 无需外SELECT
  • 明确地说DISTINCT(默认)或ALL(更快),这样您就会知道是否需要重复数据删除,而不是速度。
  • 没有LIMIT的{​​{1}}不是很有意义
  • 但是,如果您只想查看某些行,则可以删除ORDER BY
  • 是的,ORDER BYsORDER BY需要在外面重复,这样才能使订单正确并限制为10。

如果您需要LIMIT,则内部需要完整计数,例如OFFSET为5页,外部需要跳到第5页:LIMIT 50。< / p>