查詢數據不多,但卻很慢的原因


這篇文章主要記錄,造成查詢數量不大的情況下,造成查詢緩慢的原因,以及相應的解決方法。

環境說明:

  1. MySQL 版本 5.7.27
  2. 隔離級別:RR

鎖等待造成查詢速度很慢

MDL 鎖

如開啟如下事務:

Session A Session B
lock table t write;
begin;
select * from t where id=1;

Session A 持有表 t MDL 寫鎖。Session B 需要 MDL 讀鎖。這時讀寫鎖互斥,Session B 被阻塞。

flush 阻塞

flush 操作一般來說很快就能執行完,當通過查詢進程狀態后,看到被 flush 操作被阻塞,一般都是由其他語句引起的。

如下面事務:

Session A Session B Session C
select sleep(1) from t;
flush tables t;
select * from t where id =1;

Session A 在掃描每一行時會休眠1s,而 Session B 的 flush tables t; 需要關閉表 t,要等 Session A 結束。后面的 Session C 被 Session B 阻塞了。

flush 的操作示范:

#  flush 表 t
flush tables t with read lock;
# flush 所有表
flush tables with read lock;

flush 的作用在 全局鎖 文章中已經介紹過,FTWRL 主要用於 MyISAM 這樣不支持事務的引擎,保證在備份時視圖數據一致性。

行鎖

這里的行鎖,用兩階段鎖來體現。其他如間歇鎖和 next-key 鎖都會可以造成這樣的現象。

Session A Session B
begin;
update t set c=c+1 where id=1 select * from t where id=1 lock in share mode;

Session A 擁有 id=1 這行的寫鎖,Session B 想要擁有這一行的讀鎖,讀寫鎖互斥。

解決方法

首先通過 show processlist; 命令查詢被阻塞的狀態信息。如需進一步分析的話,可以將 performance_schema=on 打開,通過查詢 select blocking_pid from sys.schema_table_lock_waits; 具體造成鎖等待的原因。然后 kill掉相應的 session.

在打開 performance_schema 會有一定的性能損失。

查詢確實慢

沒有設置合適的索引

如果沒有設置合適的索引,導致掃描行數過多,時間自然就慢了。對於這種情況,可以開啟慢查詢日志,查看語句的執行過程,然后進行分析。

默認情況,慢查詢日志時關閉的,打開方式如下:

# 查詢慢查詢日志狀態及存儲位置
# show variables  like '%slow_query_log%';

# 查詢慢查詢日志的設置時間
show variables  like '%long_query%';

# 臨時打開慢查詢日志,MySQL 重啟后失效
set global slow_query_log=1;

# 改變時間
set long_query_time=0;

事務隔離的影響

事務究竟有沒有被隔離這篇中,我們知道表中的每行數據都有多個版本,在一致性視圖開啟后,視圖的一致性讀的結果就是通過和數據行的版本比較進而顯示的結果。

Session A Session B
start transaction with consistent snapshot;
update t set c=c+1 where id=1
update set c=c+1 where id=1; // 100 萬次
select * from t where id=1;
select * from t where id=1 lock in share mode;

這時第一個 select 語句就會比第二個加鎖的 select 語句還要慢。

因為第一個 select 語句是一致性讀,需要從 100 萬條回滾日志中比較直到找到適合的版本。

而第二個 select 語句是當前讀,直接讀取最新版本就可以了。所以花費的時間不一樣。

總結

造成查詢小數據量,卻很緩慢的原因一般有兩種,第一種可能是所查數據被鎖住。另一種確實是查找過程是真的很慢。

對於數據被鎖住的情況,一般會由 MDL 鎖,FLUSH 操作被阻塞,行鎖造成。

對於查詢確實很慢來說,考慮下索引是否設置的合適。並注意在 RR 級別下,是否由於一致性讀和當前讀的不同而造成查詢速度不一致的情況。

在分析原因時,可以通過進程狀態以及 sys.innodb_lock_waits 中的信息,來做出相應的處理。

參考

https://dev.mysql.com/doc/refman/5.7/en/flush.html

https://dev.mysql.com/doc/internals/en/flush-tables.html


免責聲明!

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



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