爲何我只查一行的語句,執行的仍是這麼慢?

有些狀況,只查一行數據,執行的也會特別慢,接下來咱們就梳理一下,會出現這裏現象的場景。

數據庫壓力

若是MySQL數據庫自己就有很大的壓力,致使CPU佔用率很高,IO利用率很高,那麼即便是執行一條SQL語句,也會執行的很慢。

若是這條SQL語句被鎖住,他就會執行的很慢。而MySQL中的鎖,又分爲全局鎖、表級鎖、行鎖。首先咱們會構建一個表,接下來咱們會分狀況進行分析。
CREATE TABLE `t` (
  `id` int(11) NOT NULL,
  `c` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

delimiter ;;
create procedure idata()
begin
  declare i int;
  set i=1;
  while(i<=100000) do
    insert into t values(i,i);
    set i=i+1;
  end while;
end;;
delimiter ;

call idata();

全局鎖

全局鎖的執行語句是:
flush tables with read lock; 
flush tables t with read lock;
若是指定了表t,表明的是隻關閉表t,若是沒有指定表名,則表示關閉MySQL裏全部打開的表。
可是這個操做是加全局的寫鎖,使整個表處於只讀的狀態,不會阻塞查詢語句。若是查詢語句被阻塞,那麼必定是這行命令被其餘命令阻塞了,接下來咱們來複現這個場景。
咱們能夠經過show processlist來查看語句執行狀態:
能夠看到 id爲27,28的語句都id爲26的語句被阻塞了,因此咱們只要kill掉26號語句,便可解決這個問題,返回執行結果。

表級鎖

若是有一個線程正在表t上請求或持有MDL寫鎖,就會把select語句阻塞住。
可是這種場景只能在 MySQL5.6版本復現,而MySQL5.7版本就修改了MDL加鎖的策略,而是採用了Online DDL的方式對錶結構進行修改(CopyOnWrite)因此這裏咱們可使用給表加寫鎖的方式復現。
經過processlist來查看執行狀態:
31pid的語句狀態處於等待元數據鎖。咱們能夠在MySQL啓動時設置performance_schema=on;(相比設置爲off會有10%左右的性能損失)咱們能夠直徑經過sys.schema_table_lock_waits這張表,找到形成阻塞的process id;kill掉便可。

行鎖

select * from t where id=1 lock in share mode;
若是咱們訪問id=1這個記錄時,加了讀鎖,這個時候其餘事物持有了寫鎖,咱們的select語句就會被阻塞。
咱們經過processlist能夠查詢到語句執行狀態:
SessionA啓動了事物,更改了數據,SessionB查詢這條數據的時候,因爲SessionA尚未提交事物。致使SessionB獲取不到最新的數據(當前讀)被阻塞。(讀寫鎖阻塞)
接下來咱們要找到是誰佔着這個寫鎖,若是咱們使用的MySQL版本是5.7,則能夠經過sys.innodb_lock_waits查到。
select * from t sys.innodb_lock_waits where locked_table=`'test'.'t'`\G
kill掉相應線程便可。

慢查詢

若是一個查詢(當前讀)伴隨着一個長事務大量的修改,那麼這個查詢操做就會很是的慢。
SessionB的update操做,會產生大量的回滾日誌,SessionA的
select * from t where id = 1;
是一致性讀,即基於MySQL事務開啓時,所產生的一致性視圖。因此返回的結果是1,而且查詢很是快。
而SessionA的
select * from t where id = 1 lock in share mode;
是當前讀,須要等待SessoinB的100萬次update操做,提交事物後,再根據回滾日誌查詢到當前值。
因此執行的就會很是慢。
相關文章
相關標籤/搜索