mysql優化:定期刪數據 + 批量insert + 字符串加索引為什么很傻


嗯,犯了一個很低級的錯誤,最近暴露出來了。

 

背景:

1. 內部平台,接口間斷性無返回,查詢日志注意到失敗時,接口耗時達到4000+(正常狀態:100+ms)

2. 增加日志打點,在關鍵步驟插入時間戳,發現單步insert 和 select操作耗時1000ms+

3. 查看數據庫表數據,查詢表數據量已積累到400w+,每天新增數據4W+,在字符串上建立了索引(之前埋下的很傻行為,無想法的加了索引,功能實現未考慮性能)

 

解決:

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);

附:事件標准語法

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樹插入數據

 

參考:MySQL索引背后的數據結構及算法原理

MySQL索引原理及慢查詢優化

 

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

參考:

MySQL 性能優化神器 Explain 使用分析 

https://dev.mysql.com/doc/refman/5.5/en/explain-output.html#explain_type


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM