MySQL 普通列和全文列的索引
MySQL index for normal column and full text column
我正在尝试加快以下查询的速度:
我的 table 有大约 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`
);
怎么可以"combine"这两个索引呢?由于上述查询需要 15+ 秒来执行(仅使用一个索引)。
整个table定义:
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`
);
尝试使用 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 表上的大量单列索引通常对性能有害。您应该避免创建索引,除非它们是为帮助特定查询而构建的。
(
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
(更快),这样您就会知道您考虑过是否需要重复数据删除,而不是速度。
- 没有
ORDER BY
的LIMIT
意义不大
- 但是,如果您只想查看一些行,则可以删除
ORDER BYs
。
- 是的,
ORDER BY
和 LIMIT
需要在外部重复,以便您可以正确排序并限制为 10。
如果你需要一个OFFSET
,里面需要一个完整的计数,比如说LIMIT 50
5页,然后外面需要跳到第5页:LIMIT 40,10
.
我正在尝试加快以下查询的速度:
我的 table 有大约 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`
);
怎么可以"combine"这两个索引呢?由于上述查询需要 15+ 秒来执行(仅使用一个索引)。
整个table定义:
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`
);
尝试使用 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 表上的大量单列索引通常对性能有害。您应该避免创建索引,除非它们是为帮助特定查询而构建的。
(
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
(更快),这样您就会知道您考虑过是否需要重复数据删除,而不是速度。 - 没有
ORDER BY
的LIMIT
意义不大 - 但是,如果您只想查看一些行,则可以删除
ORDER BYs
。 - 是的,
ORDER BY
和LIMIT
需要在外部重复,以便您可以正确排序并限制为 10。
如果你需要一个OFFSET
,里面需要一个完整的计数,比如说LIMIT 50
5页,然后外面需要跳到第5页:LIMIT 40,10
.