嗯,犯了一個很低級的錯誤,最近暴露出來了。html
背景:mysql
1. 內部平臺,接口間斷性無返回,查詢日誌注意到失敗時,接口耗時達到4000+(正常狀態:100+ms)git
2. 增長日誌打點,在關鍵步驟插入時間戳,發現單步insert 和 select操做耗時1000ms+github
3. 查看數據庫表數據,查詢表數據量已積累到400w+,天天新增數據4W+,在字符串上創建了索引(以前埋下的很傻行爲,無想法的加了索引,功能實現未考慮性能)算法
解決:sql
1. 考慮了數據的必要性,增長事件按期刪除過時數據數據庫
1. 查看事件開關並開啓 SHOW VARIABLES LIKE 'event_scheduler'; set global event_scheduler = on 2. 具體執行須要調用的存儲過程,刪除數據 DELIMITER // CREATE PROCEDURE delete_data_n_day_ago(IN n int) BEGIN delete from test_record where time < date_sub(curdate(), INTERVAL n day) ; END // DELIMITER ; 3. 寫事件,調用存儲過程 CREATE EVENT del_data ON SCHEDULE EVERY 1 DAY ON COMPLETION PRESERVE DO CALL delete_data_n_day_ago (3);
附:事件標準語法segmentfault
CREATE [DEFINER = { user | CURRENT_USER }] EVENT [IF NOT EXISTS] event_name ON SCHEDULE schedule [ON COMPLETION [NOT] PRESERVE] [ENABLE | DISABLE | DISABLE ON SLAVE] [COMMENT 'string'] DO event_body; schedule: AT timestamp [+ INTERVAL interval] ... | EVERY interval [STARTS timestamp [+ INTERVAL interval] ...] [ENDS timestamp [+ INTERVAL interval] ...] interval: quantity {YEAR | QUARTER | MONTH | DAY | HOUR | MINUTE | WEEK | SECOND | YEAR_MONTH | DAY_HOUR | DAY_MINUTE | DAY_SECOND | HOUR_MINUTE | HOUR_SECOND | MINUTE_SECOND} 詳見:mysql手冊:https://dev.mysql.com/doc/refman/5.7/en/create-event.html
註釋: event_name :建立的event名字(惟一肯定的)。 ON SCHEDULE:計劃任務。 schedule: 決定event的執行時間和頻率(注意時間必定要是未來的時間,過去的時間會出錯),有兩種形式 AT和EVERY。 [ON COMPLETION [NOT] PRESERVE]: 可選項,默認是ON COMPLETION NOT PRESERVE 即計劃任務執行完畢後自動drop該事件;ON COMPLETION PRESERVE則不會drop掉。 [COMMENT 'comment'] :可選項,comment 用來描述event;至關注釋,最大長度64個字節。 [ENABLE | DISABLE] :設定event的狀態,默認ENABLE:表示系統嘗試執行這個事件, DISABLE:關閉該事情,能夠用alter修改 DO event_body: 須要執行的sql語句(能夠是複合語句)。CREATE EVENT在存儲過程當中使用時合法的。 --------------------- 做者:jesseyoung 來源:CSDN 原文:https://blog.csdn.net/JesseYoung/article/details/35257527 版權聲明:本文爲博主原創文章,轉載請附上博文連接!
2. 循環調用 insert單條語句修改成批量 性能優化
這一點也是網上不少文章提示的,修改成insert into table(keys) values (values1), (values2)...... ,但可能是實驗證實,沒有原理,直至找到mysql手冊數據結構
實際減小了下面提到的connecting + sending query to server + inserting indexes
3. 修改索引設置,增長自增字段做爲主鍵
接下來就是作的很蠢的事情了,不知當初出於什麼考慮,在URL字段增長了索引,壓根沒有考慮到性能問題,發現以後感受太對不起老師。。。。
不少文章也提到最佳實踐,不要在字符串創建索引,本身沒有跑具體數據,借用網文數聽說明,好比:https://foio.github.io/mysql-stridx/
問題一:在字符串上建索引爲何會慢呢?
1. 關於索引使用數據結構,不一樣引擎內部實現存在不一致,手冊中涉及索引部分是說使用B- tree(雖然,網文不少是說B+ ) 更正下,B-樹實際就是B樹,B+樹能夠理解爲一種特殊的B樹
2. B+ 和B-的區別在因而否存數據非葉子節點不保存關鍵字記錄的指針,數據均存在葉子節點
3. 數據庫索引選擇使用B樹,而不是紅黑樹/二叉樹之類
磁盤I/O操做耗時比內存久,計算機會預讀磁盤,以頁爲單位讀取,選取標準:磁盤I/O操做盡可能少
每一個節點做爲一頁,查詢深度越小,磁盤讀取操做越少,二叉樹之類每一個節點分爲2路,相同數據深度更大,故不選取
4. 每一個節點可分的路數越多,深度越小,讀取的IO次數越少,索引字段選取int 和 長字符串大小差別,就致使了在字符串上建索引會慢
insert過程也伴隨寫索引,複雜度參考B樹插入數據
4. 確認其餘查詢語句,新增索引
explain select * from tableName where columnName=2; id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+------------------------------+------+---------------+------+---------+------+--------+-------------+ | 1 | SIMPLE | tableName | ALL | NULL | NULL | NULL | NULL | 437081 | Using where |
ALTER table tableName ADD INDEX indexName(columnName)
後續:代碼寫以前,先確認SQL語句,explain確認步驟,儘可能避免all類型 和 filesorting
參考:
https://dev.mysql.com/doc/refman/5.5/en/explain-output.html#explain_type