在mysql中分頁優化是一個老生常談的問題,如果我們幾萬條數據直接使用limit不需要做任何操作即可完成分頁並且性能還不錯,但是百萬千萬級數據我們就需要對limit進行有效的優化才能完成高效分頁了,要不就會卡死數據庫哦。在Percona Performance Conference 2009大會上來自yahoo的Surat Singh Bhati ([email protected]) 和 Rick James ([email protected])給大家分享了MySQL高效分頁的經驗。 一、概述
代碼如下 復制代碼CREATE TABLE `message` ( `id` int(11) NOT NULL AUTO_INCREMENT, `title` varchar(255) COLLATE utf8_unicode_ci NOT NULL, `user_id` int(11) NOT NULL, `content` text COLLATE utf8_unicode_ci NOT NULL, `create_time` int(11) NOT NULL, `thumbs_up` int(11) NOT NULL DEFAULT '0', /* 投票數 */ PRIMARY KEY (`id`), KEY `thumbs_up_key` (`thumbs_up`,`id`) ) ENGINE=InnoDB mysql> show table status like 'message' G Engine: InnoDB Version: 10 Row_format: Compact Rows: 50000040 /* 5千萬 */ Avg_row_length: 565 Data_length: 28273803264 /* 26 GB */ Index_length: 789577728 /* 753 MB */ Data_free: 6291456 Create_time: 2009-04-20 13:30:45兩個分頁例子:
代碼如下 復制代碼SELECT count(*) FROM message2. 查詢當前頁
代碼如下 復制代碼SELECT * FROM message ORDER BY id DESC LIMIT 0, 20
代碼如下 復制代碼mysql> explain SELECT * FROM message ORDER BY id DESC LIMIT 10000, 20G ***************** 1. row ************** id: 1 select_type: SIMPLE table: message type: index possible_keys: NULL key: PRIMARY key_len: 4 ref: NULL rows: 10020 Extra: 1 row in set (0.00 sec)
WHERE id< 100 /* last_seen */ ORDER BY id DESC LIMIT $page_size /* 沒有偏移 */上一頁:
WHERE id > 98 /* last_seen */ ORDER BY id ASC LIMIT $page_size /* 沒有偏移 */譯:通過每頁第一條或最後一條記錄的id來做條件篩選,再配合降序和升序獲得上/下一頁的結果集
代碼如下 復制代碼代碼如下 復制代碼 99mysql> explain SELECT * FROM message WHERE id < '49999961' ORDER BY id DESC LIMIT 20 G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: message type: range possible_keys: PRIMARY key: PRIMARY key_len: 4 ref: NULL Rows: 25000020 /* 忽略這裡 */ Extra: Using where 1 row in set (0.00 sec)十三、當你排序的字段不是唯一的,怎麼辦?
代碼如下 復制代碼WHERE thumbs_up< 98 ORDER BY thumbs_up DESC /* 結果將返回重復的記錄 */我們可以這樣查詢:
代碼如下 復制代碼WHERE thumbs_up <= 98 AND <額外的條件> ORDER BY thumbs_up DESC十四、額外的條件
代碼如下 復制代碼SELECT thumbs_up, id FROM message ORDER BY thumbs_up DESC, id DESC LIMIT $page_size +-----------+----+ | thumbs_up | id | +-----------+----+ | 99 | 14 | | 99 | 2 | | 98 | 18 | | 98 | 15 | | 98 | 13 | +-----------+----+下一頁:
代碼如下 復制代碼SELECT thumbs_up, id FROM message WHERE thumbs_up <= 98 AND (id < 13 OR thumbs_up< 98) ORDER BY thumbs_up DESC, id DESC LIMIT $page_size +-----------+----+ | thumbs_up | id | +-----------+----+ | 98 | 10 | | 98 | 6 | | 97 | 17 |十六、優化
查詢:
代碼如下 復制代碼SELECT * FROM message WHERE thumbs_up <= 98 AND (id < 13 OR thumbs_up < 98) ORDER BY thumbs_up DESC, id DESC LIMIT 20我們可以這樣寫:
代碼如下 復制代碼SELECT m2.* FROM message m1, message m2 WHERE m1.id = m2.id AND m1.thumbs_up <= 98 AND (m1.id <13 OR m1.thumbs_up< 98) ORDER BY m1.thumbs_up DESC, m1.id DESC LIMIT 20;十七、explain
代碼如下 復制代碼*************************** 1. row *************************** id: 1 select_type: SIMPLE table: m1 type: range possible_keys: PRIMARY,thumbs_up_key key: thumbs_up_key /* (thumbs_up,id) */ key_len: 4 ref: NULL Rows: 25000020 /* 忽略這裡 */ Extra: Using where; Using index /* Cover 譯:Cover就是說所需要的數據之從索引裡獲取就可以滿足了 */ *************************** 2. row *************************** id: 1 select_type: SIMPLE table: m2 type: eq_ref possible_keys: PRIMARY key: PRIMARY key_len: 4 ref: forum.m1.id rows: 1 Extra:十八、性能提升