我有一个由三个表组成的数据库,结构如下:
餐厅餐桌: restaurant_id,location_id,评分。 示例: 1325,77,4.5
restaurant_name表: restaurant_id,语言,名称。 示例: 1325,' en',' Pizza Express'
location_name表: location_id,语言,名称。 示例: 77,' en','纽约'
我想用英语获取餐厅信息,按地点名称和餐馆名称排序,并使用LIMIT子句对结果进行分页。所以我的SQL是:
SELECT ln.name, rn.name
FROM restaurant r
INNER JOIN location_name ln
ON r.location_id = ln.location_id
AND ln.language = 'en'
INNER JOIN restaurant_name rn
ON r.restaurant_id = rn.restaurant_id
AND rn.language = 'en'
ORDER BY ln.name, rn.name
LIMIT 0, 50
这非常慢 - 所以我使用延迟JOIN来改进我的SQL,这使事情变得更快(从10秒到2秒):
SELECT ln.name, rn.name
FROM restaurant r
INNER JOIN (
SELECT r.restaurant_id
FROM restaurant r
INNER JOIN location_name ln
ON r.location_id = ln.location_id
AND ln.language = 'en'
INNER JOIN restaurant_name rn
ON r.restaurant_id = rn.restaurant_id
AND rn.language = 'en'
ORDER BY ln.name, rn.name
LIMIT 0, 50
) r1
ON r.restaurant_id = r1.restaurant_id
INNER JOIN location_name ln
ON r.location_id = ln.location_id
AND ln.language = 'en'
INNER JOIN restaurant_name rn
ON r.restaurant_id = rn.restaurant_id
AND rn.language = 'en'
ORDER BY ln.name, rn.name
遗憾的是,2秒仍然不是用户可以接受的,所以我去检查我的查询的EXPLAIN,看起来缓慢的部分是在ORDER BY子句上,我看到"使用临时;使用filesort"。我查看了关于ORDER BY optimization的官方参考手册,我发现了这个陈述:
在某些情况下,MySQL无法使用索引来解析ORDER BY, 虽然它仍然可以使用索引来查找匹配的行 WHERE子句。例子:
查询连接了许多表,而ORDER BY中的列则没有 所有这些都来自用于检索行的第一个非常量表。 (这是EXPLAIN输出中第一个没有的表 const连接类型。)
因此,对于我的情况,假设我排序的两列来自非常量连接表,则不能使用索引。我的问题是,我可以采取其他方法来加快速度,或者到目前为止我所做的已经是我能做到的最好的了吗?
提前感谢您的帮助!
编辑1
以下是带有ORDER BY子句的EXPLAIN输出:
+----+-------------+------------+--------+--------------------------+-----------------------+---------+--------------------------------+------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+--------+--------------------------+-----------------------+---------+--------------------------------+------+----------------------------------------------+
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 50 | |
| 1 | PRIMARY | rn | ref | idx_restaurant_name_1 | idx_restaurant_name_1 | 1538 | r1.restaurant_id,const,const | 1 | Using where |
| 1 | PRIMARY | r | eq_ref | PRIMARY,idx_restaurant_1 | PRIMARY | 4 | r1.restaurant_id | 1 | |
| 1 | PRIMARY | ln | ref | idx_location_name_1 | idx_location_name_1 | 1538 | test.r.location_id,const,const | 1 | Using where |
| 2 | DERIVED | rn | ALL | idx_restaurant_name_1 | NULL | NULL | NULL | 8484 | Using where; Using temporary; Using filesort |
| 2 | DERIVED | r | eq_ref | PRIMARY,idx_restaurant_1 | PRIMARY | 4 | test.rn.restaurant_id | 1 | |
| 2 | DERIVED | ln | ref | idx_location_name_1 | idx_location_name_1 | 1538 | test.r.location_id | 1 | Using where |
+----+-------------+------------+--------+--------------------------+-----------------------+---------+--------------------------------+------+----------------------------------------------+
以下是没有ORDER BY子句的EXPLAIN输出:
+----+-------------+------------+--------+--------------------------+-----------------------+---------+--------------------------------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+--------+--------------------------+-----------------------+---------+--------------------------------+------+--------------------------+
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 50 | |
| 1 | PRIMARY | rn | ref | idx_restaurant_name_1 | idx_restaurant_name_1 | 1538 | r1.restaurant_id,const,const | 1 | Using where |
| 1 | PRIMARY | r | eq_ref | PRIMARY,idx_restaurant_1 | PRIMARY | 4 | r1.restaurant_id | 1 | |
| 1 | PRIMARY | ln | ref | idx_location_name_1 | idx_location_name_1 | 1538 | test.r.location_id,const,const | 1 | Using where |
| 2 | DERIVED | rn | index | idx_restaurant_name_1 | idx_restaurant_name_1 | 1538 | NULL | 8484 | Using where; Using index |
| 2 | DERIVED | r | eq_ref | PRIMARY,idx_restaurant_1 | PRIMARY | 4 | test.rn.restaurant_id | 1 | |
| 2 | DERIVED | ln | ref | idx_location_name_1 | idx_location_name_1 | 1538 | test.r.location_id | 1 | Using where; Using index |
+----+-------------+------------+--------+--------------------------+-----------------------+---------+--------------------------------+------+--------------------------+
编辑2
以下是表格的DDL。我构建它们只是为了说明这个问题,真正的表有更多的列。
CREATE TABLE restaurant (
restaurant_id INT NOT NULL AUTO_INCREMENT,
location_id INT NOT NULL,
rating INT NOT NULL,
PRIMARY KEY (restaurant_id),
INDEX idx_restaurant_1 (location_id)
);
CREATE TABLE restaurant_name (
restaurant_id INT NOT NULL,
language VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL,
INDEX idx_restaurant_name_1 (restaurant_id, language),
INDEX idx_restaurant_name_2 (name)
);
CREATE TABLE location_name (
location_id INT NOT NULL,
language VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL,
INDEX idx_location_name_1 (location_id, language),
INDEX idx_location_name_2 (name)
);
答案 0 :(得分:0)
根据EXPLAIN
数字,可能有大约170页的餐馆(8484/50)?我建议这对于翻阅是不切实际的。我强烈建议您重新考虑用户界面。这样做,您声明的性能问题可能会消失。
例如,UI可以是2步而不是170步到达津巴布韦的餐馆。第1步,选择一个国家。 (好的,这可能是各国的第5页。)步骤2,查看该国家/地区的餐馆列表;翻阅只有几页。对用户来说好多了;更好的数据库。
<强>附加物强>
为了优化分页,请从单个表中获取分页的页面列表(这样您就可以“记住中断的位置”)。 然后加入语言表以查找翻译。请注意,这仅查看页面的翻译价值,而不是数千。