簽到提醒推送之MySQL分頁優化
需求背景
簽到提醒功能,每天18點推送服務通知提醒用戶簽到。推送對象爲昨日簽到,截止推送前未簽到的用戶。
開發過程
數據庫表結構:
CREATE TABLE `cultivate_game_signin` (
`id` bigint(20) NOT NULL COMMENT '主鍵',
`uid` bigint(20) DEFAULT NULL COMMENT '用戶id',
`signin_time` bigint(20) DEFAULT NULL COMMENT '簽到時間',
`continue_times` int(11) DEFAULT NULL COMMENT '連續簽到次數',
`singnin_count` int(11) DEFAULT NULL COMMENT '簽到次數',
`activity_id` bigint(20) DEFAULT NULL COMMENT '活動id',
`remind_status` tinyint(4) DEFAULT '0' COMMENT '0打開簽到提醒,1關閉簽到提醒',
PRIMARY KEY (`id`),
KEY `idx_uid` (`uid`),
KEY `idx_time` (`signin_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='活動簽到表'
第一版開發:
select * from cultivate_game_signin where signin_time betweent '2018-1-13 00:00:00' and '2018-1-14 00:00:00' limit 0 , 100;
在定時任務中執行,直至遍歷完所有數據。 第一版上線後基本實現了需求。但是在12月,一次觀察job發現任務竟然執行了9個小時,執行到的翌日凌晨。在pushcode 防打擾策略中其實21點後的推送已經無效了,用戶也不能查收。沒有意義且浪費了資源。
第二版優化:
根據問題排查,由於用戶量激增,目標推送用戶達到了60W。爲了增加用戶留存,保住新增用戶,所以推送優化箭在弦上。
優化過程
優化主要採取了兩點:
- 線程池優化 設置合理的線程數,在保證服務穩定的情況下推送完畢。這裏不多贅述。
- 推送接口優化 單單使用MySQL分頁不必多說,偏移量持續增大勢必造成接口性能下降,無法滿足需求。
Limit分頁優化:
推薦分頁: 分頁方式一: Select * from table WHERE id>=23423 limit 11; #10+1 (每頁10條) Select * from table WHERE id>=23434 limit 11; 分頁方式二: Select * from table WHERE id >= ( select id from table limit 10000,1 ) limit 10; 分頁方式三: Select * from table INNER JOIN (SELECT id from table limit 10000,10) USING(id) 分頁方式四: 程序取ID: Select id from table limit 10000,10; Select * from table WHERE ID in(123,456…);
不難看出,優化主要是在解決偏移量過大。
explain select * from cultivate_game_signin where signin_time between 0 and 1540443740869 limit 99,1;
id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
---|---|---|---|---|---|---|---|---|---|
1 | SIMPLE | cultivate_game_signin | range | idx_time | idx_time | 9 | NULL | 129940 | Using where |
掃描數量會越來越大造成性能衰減。所以,在推送優化中使用了id,前提是id有序。
改進後的SQL:
select * from cultivate_game_signin where id > id signin_time betweent begin_time and end_time limit 100;
將id作爲參數,作爲下個執行語句的查詢條件。避免了大偏移量的產生。效果也是立竿見影,30分鐘內可以推送完所有目標用戶。
分頁的指導思想,查詢命中索引並且避免大的偏移量。