Re回4楼cyb的帖子
distinct 可以认为是group by的特例。
SELECT DISTINCT
LoginId,
SubId
FROM
TB
WHERE 1
ORDER BY Visit ASC
LIMIT 8888, 10
这个SQL等价于: select
LoginId, SubId
from TB where 1 group by
LoginId, SubId
ORDER BY Visit ASC
LIMIT 8888, 10
单纯就这条SQL而言,
ORDER BY Visit ASC 是没必要的,因为select选出的字段中没有
Visit字段,建议添加组合索引(
LoginId,
SubId
)。
假如SQL是 select
LoginId,SubId,Visit
from TB where 1 group by
LoginId, SubId
ORDER BY Visit ASC
LIMIT 8888, 10
这里可以建议组合索引(
LoginId,
SubId) 或者(
LoginId,
SubId,Visit)
将
Visit字段添加到索引中,仍旧避免不了排序,上面SQL的执行过程是:
1.
select
LoginId,SubId,Visit from TB where 1 group by LoginId, SubId
2.
ORDER BY Visit ASC
LIMIT 8888, 10
第一步中的group by (或者distinct)可以利用到索引(
LoginId,
SubId
)避免临时表,排序
下面是测试案例:
mysql> show create table tb\G
*************************** 1. row ***************************
Table: tb
Create Table: CREATE TABLE `tb` (
`id` int(11) DEFAULT NULL,
`LoginId` int(11) DEFAULT NULL,
`SubId` int(11) DEFAULT NULL,
`vist` int(11) DEFAULT NULL,
KEY `idx1` (`id`),
KEY `login_subId` (`LoginId`,`SubId`),
KEY `login_subId_vist` (`LoginId`,`SubId`,`vist`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
mysql> explain select LoginId, SubId, vist from tb where 1 group by LoginId,SubId ;
+----+-------------+-------+-------+------------------------------+-------------+---------+------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+------------------------------+-------------+---------+------+------+-------+
| 1 | SIMPLE | tb | index | login_subId,login_subId_vist | login_subId | 10 | NULL | 9 | NULL |
+----+-------------+-------+-------+------------------------------+-------------+---------+------+------+-------+
1 row in set (0.00 sec)
第二步中将步骤1中的结果集按照
Visit 字段排序,这一步的排序不可避免。
上面提到了将visit字段加入到组合索引中,是为了构建覆盖索引。避免返回到数据行去查询。
对于您提到的问题,
排序方式有多种,Visit 、ID、Count等,是不是有一种排序就要建一个“覆盖索引”?
distinct/group by的优化,关键是要利用索引,避免distinct/group by时的创建临时表,排序。
将order by的字段加入到组合索引中,目的是为了直接从二级索引字段获取到结果集,避免再去查数据行。
加了覆盖索引,索引字段变长,查询性能变好,同时也会导致占用空间、插入性能变慢。
所以是否去创建覆盖索引,还是需要依旧您的具体业务而定的。
还需要单列给Visit 、ID、Count、SubId、LoginId建索引吗?
不需要给单列
Visit 、ID、Count、SubId、LoginId建索引,这里在order by 的字段上建立索引,还是不能避免排序。
-------------------------
Re回5楼cyb的帖子
普通limit M,N的翻页写法,往往在越往后翻页的过程中速度越慢,原因
mysql会读取表中的前M+N条数据,M越大,性能就越差:
select * from t where sellerid=100 limit 100000,20
优化写法:
select t1.* from t t1,
(select id from t sellerid=100 limit 100000,20) t2
where t1.id=t2.id;
优化后的翻页写法,先查询翻页中需要的N条数据的主键id,在根据主键id
回表查询所需要的N条数据,此过程中查询N条数据的主键ID在索引中完成
这种优化的根本出发点,是减少在数据页中的扫描量。
覆盖索引,也是一种优化思路,出发点就是直接从二级索引中直接获取查询结果。
您也可以将您的业务SQL发一下,一起来看看如何优化。
-------------------------
回15楼cyb的帖子
T_Query 是一个视图对吧?
可否将T_Query的建表语句发一下呢?
-------------------------
Re回15楼cyb的帖子
SELECT DISTINCT
LoginId,
SubId
FROM
T_Query
WHERE 1
ORDER BY fenshu DESC
LIMIT 61630, 10
这条SQL从实现的功能而言,其实没必要加ORDER BY fenshu DESC 的。
distinct只能返回它的目标字段,而无法返回其它字段。 所以在SELECT DISTINCT LoginId, SubId FROM T_Query中取出的是 LoginId,SubId不重复的行。也就是说,必须LoginId和SubId都相同才会被排除。
做个测试:
mysql> select * from tb;
+------+---------+-------+------+
| id | LoginId | SubId | vist |
+------+---------+-------+------+
| 1 | 123 | 21 | 78 |
| 2 | 43 | 71 | 78 |
| 3 | 43 | 21 | 78 |
| 2 | 43 | 71 | 78 |
| 3 | 43 | 21 | 78 |
| 2 | 43 | 71 | 78 |
| 5 | 73 | 21 | 78 |
| 2 | 55 | 67 | 78 |
| 1 | 98 | 21 | 78 |
+------+---------+-------+------+
9 rows in set (0.01 sec)
mysql> select distinct LoginId,SubId from tb ;
+---------+-------+
| LoginId | SubId |
+---------+-------+
| 43 | 21 |
| 43 | 71 |
| 55 | 67 |
| 73 | 21 |
| 98 | 21 |
| 123 | 21 |
+---------+-------+
6 rows in set (0.00 sec)
mysql> select distinct LoginId,SubId from tb where 1 order by vist;
+---------+-------+
| LoginId | SubId |
+---------+-------+
| 43 | 21 |
| 43 | 71 |
| 55 | 67 |
| 73 | 21 |
| 98 | 21 |
| 123 | 21 |
+---------+-------+
6 rows in set (0.00 sec)
这里如果建了(LoginId,SubId)即可避免distinct的创建临时表,避免排序。
mysql> explain select distinct LoginId,SubId from tb;
+----+-------------+-------+-------+------------------------------+-------------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+------------------------------+-------------+---------+------+------+-------------+
| 1 | SIMPLE | tb | index | login_subId,login_subId_vist | login_subId | 10 | NULL | 9 | Using index |
+----+-------------+-------+-------+------------------------------+-------------+---------+------+------+-------------+
1 row in set (0.00 sec)
所以,对于您给出的SQL,我的建议是改写SQL为:
SELECT DISTINCT
LoginId,
SubId
FROM
T_Query
LIMIT 61630, 10
并添加索引( LoginId, SubId),这里一方面可以利用到索引避免临时表、排序;另一方面其实也是覆盖索引。
如果您发现去掉ORDER BY fenshu DESC 不符合您的业务需求,那么就需要考虑一下distinct的用法是否正确? select出来的结果集是否是您真实需要的。
另外需要提到一点:
您发给我的这张表,索引用法有点问题,建了很多不必要的索引。
假如建了(A),(A,B),(A,B,C)三个索引,其实(A),(A,B)都是不需要的。
-------------------------
Re回20楼华夏一剑的帖子
在您给的例子中,select id, title from tb_news where title like '%mal%'; 是可以走上索引的,并且是覆盖索引。
innodb表的二级索引上存储了主键值,上面的SQL语句只需要查询id(主键字段)和title,所以扫描二级索引字段就可以获取到结果,不要再返回主键索引读取数据了。
mysql> explain select id, title from tb_news where title like '%mal%';
+----+-------------+---------+-------+---------------+---------------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+-------+---------------+---------------+---------+------+------+--------------------------+
| 1 | SIMPLE | tb_news | index | NULL | tb_news_title | 203 | NULL | 5 | Using where; Using index |
+----+-------------+---------+-------+---------------+---------------+---------+------+------+--------------------------+
1 row in set (0.00 sec)
而类似这种,select * from tb_news where title like '%mal%';会走全表扫描。
mysql> explain select * from tb_news where title like '%mal%';
+----+-------------+---------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+------+---------------+------+---------+------+------+-------------+
| 1 | SIMPLE | tb_news | ALL | NULL | NULL | NULL | NULL | 5 | Using where |
+----+-------------+---------+------+---------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
通过覆盖索引可以获得性能上的一定优化,但是在数据量特别大,请求频繁的业务场景下不要在数据库进行模糊查询;
非得使用数据库的话 ,建议不要在生产库进行查询,可以在只读节点进行查询,避免查询造成主业务数据库的资源消耗完,导致故障;
可以使用MySQL自带的全文检索,或者一些开源的搜索引擎技术,比如sphinx.