【发布时间】:2017-03-06 11:52:32
【问题描述】:
我尝试使用 MariaDB 10.1.18 (Linux Debian Jessie) 提高 SQL 查询的性能。
服务器有大量 RAM (192GB) 和 SSD 磁盘。
真实表有数亿行,但我可以在数据子集和简化布局上重现我的性能问题。
这是(简化的)表定义:
CREATE TABLE `data` (
`uri` varchar(255) NOT NULL,
`category` tinyint(4) NOT NULL,
`value` varchar(255) NOT NULL,
PRIMARY KEY (`uri`,`category`),
KEY `cvu` (`category`,`value`,`uri`),
KEY `cu` (`category`,`uri`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
为了重现我的内容的实际分布,我插入了大约 200,000 行这样的行(bash 脚本):
#!/bin/bash
for i in `seq 1 100000`;
do
mysql mydb -e "INSERT INTO data (uri, category, value) VALUES ('uri${i}', 1, 'foo');"
done
for i in `seq 99981 200000`;
do
mysql mydb -e "INSERT INTO data (uri, category, value) VALUES ('uri${i}', 2, '$(($i % 5))');"
done
所以,我们插入:
- 类别 1 中的 100'000 行以静态字符串 ("foo") 作为值
- 类别 2 中的 100'000 行,数值为 1 到 5 之间的数字
- 20 行在每个数据集之间有一个共同的“uri”(类别 1 / 2)
我总是在查询之前运行分析表。
这是我运行的查询的解释输出:
MariaDB [mydb]> EXPLAIN EXTENDED
-> SELECT d2.uri, d2.value
-> FROM data as d1
-> INNER JOIN data as d2 ON d1.uri = d2.uri AND d2.category = 2
-> WHERE d1.category = 1 and d1.value = 'foo';
+------+-------------+-------+--------+----------------+---------+---------+-------------------+-------+----------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+------+-------------+-------+--------+----------------+---------+---------+-------------------+-------+----------+-------------+
| 1 | SIMPLE | d1 | ref | PRIMARY,cvu,cu | cu | 1 | const | 92964 | 100.00 | Using where |
| 1 | SIMPLE | d2 | eq_ref | PRIMARY,cvu,cu | PRIMARY | 768 | mydb.d1.uri,const | 1 | 100.00 | |
+------+-------------+-------+--------+----------------+---------+---------+-------------------+-------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)
MariaDB [mydb]> SHOW WARNINGS;
+-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Level | Code | Message |
+-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Note | 1003 | select `mydb`.`d2`.`uri` AS `uri`,`mydb`.`d2`.`value` AS `value` from `mydb`.`data` `d1` join `mydb`.`data` `d2` where ((`mydb`.`d1`.`category` = 1) and (`mydb`.`d2`.`uri` = `mydb`.`d1`.`uri`) and (`mydb`.`d2`.`category` = 2) and (`mydb`.`d1`.`value` = 'foo')) |
+-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
MariaDB [mydb]> SELECT d2.uri, d2.value FROM data as d1 INNER JOIN data as d2 ON d1.uri = d2.uri AND d2.category = 2 WHERE d1.category = 1 and d1.value = 'foo';
+-----------+-------+
| uri | value |
+-----------+-------+
| uri100000 | 0 |
| uri99981 | 1 |
| uri99982 | 2 |
| uri99983 | 3 |
| uri99984 | 4 |
| uri99985 | 0 |
| uri99986 | 1 |
| uri99987 | 2 |
| uri99988 | 3 |
| uri99989 | 4 |
| uri99990 | 0 |
| uri99991 | 1 |
| uri99992 | 2 |
| uri99993 | 3 |
| uri99994 | 4 |
| uri99995 | 0 |
| uri99996 | 1 |
| uri99997 | 2 |
| uri99998 | 3 |
| uri99999 | 4 |
+-----------+-------+
20 rows in set (0.35 sec)
此查询在 ~350 毫秒内返回 20 行。
对我来说似乎很慢。
有没有办法提高此类查询的性能?有什么建议吗?
【问题讨论】:
-
一般经验法则:只要在“决策上下文”(
where、join、order by等)中使用字段,您就可以在其上放置索引。 -
查询返回多少个结果?
-
看起来你的索引已经覆盖了;也许(如果
category = 2是data的一个小得多的子集)你可能在第二次引用data作为子查询时会有更好的运气。否则,我的主要建议是重组您的数据,以便您不使用 varchar 或任何字符串类型作为主键(或连接条件)...尤其是不作为 PK 的第一个元素。 -
如果您使用 EXPLAIN EXTENDED,您还应该通过 SHOW WARNINGS 提供扩展信息。
-
请提供
EXPLAIN FORMAT=JSON SELECT ...;。
标签: mysql performance mariadb