前面一篇日志中,我记录了自己优化关联表的过程,当时提到一点: 把数据按取出的需要进行物理聚集。
本篇我想强调的另一个的问题是:
任何一个查询,都必须充分利用索引。没有可利用的索引就建索引。
筛选条件太多,要考虑拆分成子查询来利用索引。
开始之前,我再次罗列几个表:
Album表: 专辑数据, 98万条记录,包含一个叫rate的字段
Tag表: 标签数据,20万条记录
Album_tags表: 关联表,300万条记录,保存了每个专辑都关联了哪些标签
我的需求是:
从Album表中取出和某个Tag关联的所有专辑,按照rate字段排序,再取rate最高的top 1000。
在上一篇日志里,我已经完成了取专辑ID的工作。
现在可以直接拿查询结果去和Album做inner join了。
但是Album表的大小超过1GB,这个inner join也不是可以随便写的。
1. 创建根据rate预排序的专辑表Tag_Albums
如果不预先排好序,而让MySQL每次都对得到的临时表做内存排序,将浪费不少时间。
我们首先来省掉这些时间,将Album中的数据按照rate降序导出到一个新的表:
create table Tag_Albums select rate,id,vote_count,album_name, pub_date from Album order by rate DESC;
这里我并不需要导出所有的列,需要哪些列就选择哪几列。
经过上面的精简,我得到的tag_albums表同样是98万条记录,却只有66MB。
2. 为Tag_Albums表增加主键和索引
建立这个表,是要让它预先按照rate排好序,所以选择主键是很关键的。
这里我选择复合的(rate, id)作为主键:
alter table tag_albums add primary key (`rate`,`id`)
如果错误地选择id作为主键,那我们导出的数据最后又是按照id排序和聚集了。
另外,我们还需要对id字段增加一个索引,至于何时用到,看了后文就知道了:
alter table tag_albums add key idx_id(id)
3. 预览最终的SQL
select T0.rate, T0.id, T0.album_name from tag_albums T0 inner join (select T3.rate,T3.id from (select album_id from album_tags T1 where tag_id = 8) T2, tag_albums T3 where T3.id = T2.album_id order by T3.rate DESC,T3.id DESC limit 1000) T4 on T0.rate = T4.rate and T0.id = T4.id 查询1
上面是我最终使用的版本,下面解释为什么需要这么写。