* 我們經常會碰到以下的場景
mysql中的用戶數據(以下例子中以郵件爲例)達到上百萬級別,單個用戶的郵件上萬,
當進行用戶郵件記錄查詢時需要支持按照時間,按照標題,和按照收件人進行排序,
排序之後還要分頁顯示(用戶郵件可能會分上千頁),當用戶選擇3000 頁,5000頁,
或者是尾頁的時候,性能很差
* 構造測試數據模型
表結構
create table user_mail
(
id int(9) NOT NULL auto_increment,
user_id int(9) NOT NULL,
subject varchar(128),
creating_time datetime,
from_addr varchar(128),
detail varchar(1024),
PRIMARY KEY(id)
);
數據庫爲MyISAM, 字符集Latin-1
生成測試數據的程序,代碼如下, 產生300萬條數據, 對應1000個用戶,數據300萬條(每個人3000條左右浮動)
爲了測試較差的情況,特別讓其中一個用戶關聯大量記錄, 執行下面的語句
update user_mail set user_id=900 where user_id>900;
數據統計,編號0 ~ 899的用戶的郵件數目在3000左右浮動,900號用戶的數據爲30萬( 下面的測試均針對900號用戶進行查詢 ),數據一共300萬
- 試驗環境
使用本機 mysql (5.0.21), 本機配置 , IBM think pad T61, 雙核 1.8G, 2G 內存
- 查詢要求,分頁查詢某人的郵件,按照subject進行排序
- 普通做法
創建索引
create index i_user_mail_subject on user_mail(user_id,subject);
下面是執行的查詢
select * from user_mail where user_id=900 order by subject limit 100 offset ???
offset 執行時間
100 0.01秒
1000 0.03秒
10000 0.25秒
100000 1.63秒
最差情況的這個用戶,當我選擇尾頁郵件的時候,數據庫的負載極高,而且耗時超過2秒
- 優化做法
創建索引
create index i_user_mail_subject1 on user_mail(user_id,subject,id);
下面是執行的查詢
select * from user_mail um
inner join (
select id from user_mail where user_id=900 order by subject limit 100 offset ???
) page on page.id=um.id;
offset 執行時間
100 0.01秒
1000 0.02秒
10000 0.03秒
100000 0.24秒
優化做法的sql語句和普通做法的sql是等價的,但是速度相差8倍左右
可以看到當查詢offset 10000時,只用了30ms,性能槓槓的
- 優化原理
covering index
查詢的所有字段在索引裏都可以找到,這樣查詢的時候只用找索引就可以了
這就是爲什麼優化算法中建立索引是三列主鍵
select id from user_mail where user_id=900 order by subject limit 100 offset 10000
上面的sql會很快,因爲id, user_id, subject都能應用索引
如果寫成
select * from user_mail where user_id=900 order by subject limit 100 offset 10000
就會慢很多
所以優化算法中先查詢出小結果集(100條)的id, 然後再進行join查詢,查詢其他字段的值