千万条据下的分页
2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
1.1.?背景
對于開發(fā)來說,分頁功能碰到的頻率還是算蠻高的,基本上在每個模塊中都需要都會遇到列表分頁的功能。他們實(shí)現(xiàn)的都很快,因?yàn)榛旧现灰阎暗拇a改改就OK了。他們的實(shí)現(xiàn)基本是是如下語句:
SELECT * FROM goods WHERE user_id = 4 LIMIT 1000, 20;... omit ...20 rows in set (0.11 sec)像這樣的語句對數(shù)據(jù)量小或偏移量小的時(shí)候是十分快的。但是當(dāng)數(shù)據(jù)量大并且偏移量大的時(shí)候就會有問題了。如下:
SELECT * FROM goods WHERE user_id = 4 LIMIT 500000, 20;... omit ...20 rows in set (7.84 sec)為什么會這樣就不說了。下面給出優(yōu)化的過程。
1.2.?構(gòu)建數(shù)據(jù)
-- 創(chuàng)建商品表DROP TABLE IF EXISTS goods;CREATE TABLE goods(id bigint(20) unsigned NOT NULL AUTO_INCREMENT,good_name VARCHAR(50) NOT NULL,user_id TINYINT unsigned NOT NULL,PRIMARY KEY (`id`));-- 創(chuàng)建批量添加數(shù)據(jù)存儲過程-- 下面創(chuàng)建數(shù)據(jù)可能需要一點(diǎn)時(shí)間DROP PROCEDURE IF EXISTS insert_batch;DELIMITER //CREATE PROCEDURE insert_batch()BEGINDECLARE num INT;DECLARE user_id TINYINT;SET num=1;WHILE num <= 100000 DOSELECT FLOOR(RAND() * 10 + 1) INTO user_id;INSERT INTO goods VALUES(NULL, REPEAT('X', 50), user_id);SET num=num+1;END WHILE;SET num=1;WHILE num <= 7 DOINSERT INTO goods SELECT NULL, good_name, user_id FROM goods;SET num=num+1;END WHILE;END //DELIMITER ;-- 調(diào)用存儲過程CALL insert_batch();-- 添加索引ALTER TABLE goodsADD INDEX idx$goods$user_id(user_id);SELECT user_id, COUNT(*) FROM goods GROUP BY user_id;+---------+----------+| user_id | COUNT(*) |+---------+----------+|?????? 1 |????10089 ||?????? 2 |????10077 ||?????? 3 |???? 9944 ||?????? 4 | 12710074 ||?????? 5 |????10011 ||?????? 6 |???? 9925 ||?????? 7 |???? 9950 ||?????? 8 |????10149 ||?????? 9 |???? 9949 ||??????10 |???? 9832 |+---------+----------+這邊我們以數(shù)據(jù)最多的user_id=4的記錄來模擬
1.3.?優(yōu)化規(guī)則
讓所有結(jié)果集數(shù)據(jù)最小化。如果是臨時(shí)表,還是行數(shù)據(jù)還是列數(shù)據(jù)都讓結(jié)果最小化,還有就是臨時(shí)結(jié)果集盡量不走主鍵索引,走二級索引。
1.4.?模擬
現(xiàn)在我們需要查詢用戶4在10000000后20條數(shù)據(jù)
1、通過user_id找到主鍵ID(讓列結(jié)果最小化)
SELECT id FROM goods WHERE user_id = 4 LIMIT 10000000, 20;10343427... omit ...1034344620 rows in set (1.83 sec)2、通過獲得的主鍵ID尋找需要的數(shù)據(jù),這邊我就不使用python來演示了。在程序里面就需要拼出IN里面的條件。
SELECT *FROM goodsWHERE id IN(10343427, 10343428, 10343429, 10343430, 10343431,10343432, 10343433, 10343434, 10343435, 10343436,10343437, 10343438, 10343439, 10343440, 10343441,10343442, 10343443, 10343444, 10343445, 10343446);+----------+----------------------------------------------------+---------+| id?????? | good_name??????????????????????????????????????????| user_id |+----------+----------------------------------------------------+---------+| 10343427 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |?????? 4 |... omit ...20 rows in set (0.01 sec)1.5.?進(jìn)一步優(yōu)化
其實(shí)上面我們還能讓結(jié)構(gòu)級變少。來看下面列表簡圖:
在網(wǎng)頁的分頁按鈕基本省都是一個連接,或者通過jquery時(shí)間分頁。我們可以在按鈕上添加兩個屬性參數(shù)為max_id和min_id。分別記錄的是當(dāng)前頁數(shù)據(jù)的最小ID和最大ID(如:min_max=10343427、max_id=10343446)。
查找數(shù)據(jù)如下:
1、通過user_id找到主鍵ID(讓列結(jié)果最小化)
如果是點(diǎn)擊下一頁
SELECT id FROM goods WHERE id > 10343446 AND user_id = 4 LIMIT 0, 20;+----------+| id?????? |+----------+| 10343447 |... omit ...20 rows in set (0.02 sec)如果是點(diǎn)擊上一頁(上一頁會比下一頁性能來的差一點(diǎn),因?yàn)橛杏玫脚判?
SELECT id FROM goods WHERE id < 10343427 AND user_id = 4ORDER BY id DESCLIMIT 0, 20;2、通過獲得的主鍵ID尋找需要的數(shù)據(jù)
SELECT *FROM goodsWHERE id IN(10343447, 10343448, 10343449, 10343450, 10343451,10343452, 10343453, 10343454, 10343455, 10343456,10343457, 10343458, 10343459, 10343460, 10343461,10343462, 10343463, 10343464, 10343465, 10343466);+----------+----------------------------------------------------+---------+| id?????? | good_name??????????????????????????????????????????| user_id |+----------+----------------------------------------------------+---------+| 10343447 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |?????? 4 |... omit ...20 rows in set (0.01 sec)1.6.?總結(jié)
這種優(yōu)化可能在一些使用到聚合函數(shù)的排序的情況下沒法使用。
在這邊鼓勵使用MySQL的盡量使用比較簡單的語句,不使用JOIN。因?yàn)閮?yōu)化器對簡單的語句解析的很快,而且在維護(hù)的角度來說越白癡的語句越讓人容易明白。
當(dāng)然,強(qiáng)烈反對在程序中 for 循環(huán)取數(shù)據(jù)庫。
轉(zhuǎn)載于:https://my.oschina.net/lemonwater/blog/1546328
總結(jié)
- 上一篇: Android 权限的一些细节
- 下一篇: WannaCry警示:学会检测和减轻云端