千万条据下的分页
2019獨角獸企業重金招聘Python工程師標準>>>
1.1.?背景
對于開發來說,分頁功能碰到的頻率還是算蠻高的,基本上在每個模塊中都需要都會遇到列表分頁的功能。他們實現的都很快,因為基本上只要把之前的代碼改改就OK了。他們的實現基本是是如下語句:
SELECT * FROM goods WHERE user_id = 4 LIMIT 1000, 20;... omit ...20 rows in set (0.11 sec)像這樣的語句對數據量小或偏移量小的時候是十分快的。但是當數據量大并且偏移量大的時候就會有問題了。如下:
SELECT * FROM goods WHERE user_id = 4 LIMIT 500000, 20;... omit ...20 rows in set (7.84 sec)為什么會這樣就不說了。下面給出優化的過程。
1.2.?構建數據
-- 創建商品表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`));-- 創建批量添加數據存儲過程-- 下面創建數據可能需要一點時間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 ;-- 調用存儲過程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 |+---------+----------+這邊我們以數據最多的user_id=4的記錄來模擬
1.3.?優化規則
讓所有結果集數據最小化。如果是臨時表,還是行數據還是列數據都讓結果最小化,還有就是臨時結果集盡量不走主鍵索引,走二級索引。
1.4.?模擬
現在我們需要查詢用戶4在10000000后20條數據
1、通過user_id找到主鍵ID(讓列結果最小化)
SELECT id FROM goods WHERE user_id = 4 LIMIT 10000000, 20;10343427... omit ...1034344620 rows in set (1.83 sec)2、通過獲得的主鍵ID尋找需要的數據,這邊我就不使用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.?進一步優化
其實上面我們還能讓結構級變少。來看下面列表簡圖:
在網頁的分頁按鈕基本省都是一個連接,或者通過jquery時間分頁。我們可以在按鈕上添加兩個屬性參數為max_id和min_id。分別記錄的是當前頁數據的最小ID和最大ID(如:min_max=10343427、max_id=10343446)。
查找數據如下:
1、通過user_id找到主鍵ID(讓列結果最小化)
如果是點擊下一頁
SELECT id FROM goods WHERE id > 10343446 AND user_id = 4 LIMIT 0, 20;+----------+| id?????? |+----------+| 10343447 |... omit ...20 rows in set (0.02 sec)如果是點擊上一頁(上一頁會比下一頁性能來的差一點,因為有用到排序)
SELECT id FROM goods WHERE id < 10343427 AND user_id = 4ORDER BY id DESCLIMIT 0, 20;2、通過獲得的主鍵ID尋找需要的數據
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.?總結
這種優化可能在一些使用到聚合函數的排序的情況下沒法使用。
在這邊鼓勵使用MySQL的盡量使用比較簡單的語句,不使用JOIN。因為優化器對簡單的語句解析的很快,而且在維護的角度來說越白癡的語句越讓人容易明白。
當然,強烈反對在程序中 for 循環取數據庫。
轉載于:https://my.oschina.net/lemonwater/blog/1546328
總結
- 上一篇: Android 权限的一些细节
- 下一篇: WannaCry警示:学会检测和减轻云端