日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

mysql slave lock 跳过_slave开启MTS时执行mysqldump引发死锁案例

發布時間:2023/12/19 数据库 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 mysql slave lock 跳过_slave开启MTS时执行mysqldump引发死锁案例 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

出現這種問題除非手動干預,殺掉FTWRL的session,復制線程方可以繼續進行。版本社區版5.7.26。

二、堵塞圖

如果分析上面的堵塞可以畫圖如下:

三、關于woker線程w1和w3的等待

這里我們需要重點關注參數?slave_preserve_commit_order,在我將要出版的《深入理解MySQL主從原理》一書中做了詳細描述,這里簡單說明如下:這個參數是為了保證從庫 group commit 中的每個工作線程的事務提交順序和主庫事務執行的順序一致。它在 order commit 的flush階段前就生效。工作線程的事務在等待獲取自己提交權限期間會堵塞在狀態 ‘Waiting for preceding transaction to commit’ 下。

但是我們知道在order commit的flush之前就會獲取 MDL_key::COMMIT。因此這里w1和w3工作線程正在等待自己提交權限的到來,但是遺憾的是w2的事務由于不能獲取 global read lock 而遲遲不能提交。同時它們堵塞了FTWRL。

四、關于FTWRL的等待

這個我也多次描述過了,FTWRL的過程大概如下:

第一步:?加MDL LOCK類型為GLOBAL,級別為S。如果出現等待狀態為 ‘Waiting for global read lock’。注意select語句不會上GLOBAL級別上鎖,但是DML/DDL/FOR UPDATE語句會上GLOBAL級別的IX鎖,IX鎖和S鎖不兼容會出現這種等待。下面是這個兼容矩陣:|?Type?of?active???|??Request?|???scoped?lock????|???type???|?IS(*)??IX???S??X?|?---------+------------------+?IS???????|??+??????+???+??+?|?IX???????|??+??????+???-??-?|?S????????|??+??????-???+??-?|?X????????|??+??????-???-??-?|

第二步:?推進全局表緩存版本。源碼中就是一個全局變量 refresh_version++。?第三步:?釋放沒有使用的table 緩存??勺孕袇⒖己瘮?close_cached_tables。?第四步:?判斷是否有正在占用的table緩存,如果有則等待,等待占用者釋放。等待狀態為 'Waiting for table flush'。這一步會去判斷table緩存的版本和全局表緩存版本是否匹配,如果不匹配則等待如下:for?(uint?idx=0?;?idx?has_old_version())?//如果版本?和?當前?的?refresh_version?版本不一致???????{?????????found=?TRUE;?????????break;?//跳出第一層查找?是否有老版本?存在???????}?????}...if?(found)//如果找到老版本,需要等待???{?????/*???????The?method?below?temporarily?unlocks?LOCK_open?and?frees???????share's?memory.?????*/?????if?(share->wait_for_old_version(thd,?&abstime,???????????????????????????????????MDL_wait_for_subgraph::DEADLOCK_WEIGHT_DDL))?????{???????mysql_mutex_unlock(&LOCK_open);???????result=?TRUE;???????goto?err_with_reopen;?????}???}

而等待的結束就是占用的table緩存的占用者釋放,這個釋放操作存在于函數 close_thread_table中,如下:if?(table->s->has_old_version()?||?table->needs_reopen()?||??????table_def_shutdown_in_progress)??{????tc->remove_table(table);//關閉?table?cache?instance????mysql_mutex_lock(&LOCK_open);????intern_close_table(table);//去掉?table?cache?define????mysql_mutex_unlock(&LOCK_open);??}

最終會調用函數 MDL_wait::set_status 將 FTWRL 喚醒,也就是說對于正在占用的table緩存釋放者不是FTWRL會話而是占用者自己。不管怎么樣最終整個table緩存將會被清空,如果經過FTWRL后去查看 Open_table_definitions 和 Open_tables 將會發現重新計數了。下面是喚醒函數的代碼,也很明顯:bool?MDL_wait::set_status(enum_wait_status?status_arg)?open_table{??bool?was_occupied=?TRUE;??mysql_mutex_lock(&m_LOCK_wait_status);??if?(m_wait_status?==?EMPTY)??{????was_occupied=?FALSE;????m_wait_status=?status_arg;????mysql_cond_signal(&m_COND_wait_status);//喚醒??}??mysql_mutex_unlock(&m_LOCK_wait_status);//解鎖??return?was_occupied;}

第五步:?加MDL LOCK類型COMMIT 級別為S。如果出現等待狀態為 ‘Waiting for commit lock’。如果有大事務的提交很可能出現這種等待。

注意?這里的第五步,正是因為w1和w3獲取了 MDL LOCK COMMIT,而又在等待w2的事務提交因此FTWRL也不得不等待。

五、關于woker線程w2的等待

這里可能的原因有2個:多線程并行的情況下,線程執行的順序本生就是不定的,很可能線程由于丟失CPU而落后其他線程的處理,因為CPU調度的最小單位是線程。如果保證某個共享內存操作的完整性需要用到mutex、原子變量等技術。

如果w2中的事務本生就包含了多個DML語句,那么獲取 GLOBAL READ LOCK 本身就是間歇性的,也就是每個語句結束都會釋放,然后下一個語句開始的時候再次open table來獲取。

我們來看看第二點,只考慮row_format格式的binlog。

我們知道一個事務可以包含多個語句,每條語句都會包含一個map Event和多個DML Event,當本Event是語句的最后一個Event的時候會使用STMT_END_F進行標記,也正是在這個時候會釋放 GLOBAL READ LOCK,源碼有如下:if?(get_flags(STMT_END_F))??{????if((error=?rows_event_stmt_cleanup(rli,?thd)))棧:#0??MDL_context::release_lock?(this=0x7fffa8000a08,?duration=MDL_STATEMENT,?ticket=0x7fffa800ea40)?at?/opt/percona-server-locks-detail-5.7.22/sql/mdl.cc:4350#1??0x0000000001464bf1?in?MDL_context::release_locks_stored_before?(this=0x7fffa8000a08,?duration=MDL_STATEMENT,?sentinel=0x0)?at?/opt/percona-server-locks-detail-5.7.22/sql/mdl.cc:4521#2??0x000000000146541b?in?MDL_context::release_statement_locks?(this=0x7fffa8000a08)?at?/opt/percona-server-locks-detail-5.7.22/sql/mdl.cc:4813#3??0x0000000001865c75?in?Relay_log_info::slave_close_thread_tables?(this=0x341e8b0,?thd=0x7fffa8000970)?at?/opt/percona-server-locks-detail-5.7.22/sql/rpl_rli.cc:2014#4??0x0000000001865873?in?Relay_log_info::cleanup_context?(this=0x341e8b0,?thd=0x7fffa8000970,?error=false)?at?/opt/percona-server-locks-detail-5.7.22/sql/rpl_rli.cc:1886#5??0x00000000017e8fc7?in?rows_event_stmt_cleanup?(rli=0x341e8b0,?thd=0x7fffa8000970)?at?/opt/percona-server-locks-detail-5.7.22/sql/log_event.cc:11782#6??0x00000000017e8c79?in?Rows_log_event::do_apply_event?(this=0x7fffa8017dc0,?rli=0x341e8b0)?at?/opt/percona-server-locks-detail-5.7.22/sql/log_event.cc:11660#7??0x00000000017cfdcd?in?Log_event::apply_event?(this=0x7fffa8017dc0,?rli=0x341e8b0)?at?/opt/percona-server-locks-detail-5.7.22/sql/log_event.cc:3570#8??0x00000000018476dc?in?apply_event_and_update_pos?(ptr_ev=0x7fffec14f880,?thd=0x7fffa8000970,?rli=0x341e8b0)?at?/opt/percona-server-locks-detail-5.7.22/sql/rpl_slave.cc:4766#9??0x0000000001848d9a?in?exec_relay_log_event?(thd=0x7fffa8000970,?rli=0x341e8b0)?at?/opt/percona-server-locks-detail-5.7.22/sql/rpl_slave.cc:5300#10?0x000000000184f9cc?in?handle_slave_sql?(arg=0x33769a0)?at?/opt/percona-server-locks-detail-5.7.22/sql/rpl_slave.cc:7543(gdb)?p?ticket->m_lock->key.mdl_namespace()$1?=?MDL_key::GLOBAL(gdb)?p?ticket->m_type$2?=?MDL_INTENTION_EXCLUSIVE(gdb)?p?ticket->m_duration$3?=?MDL_STATEMENT

如果下一條語句開始又會重新獲取GLOBAL READ LOCK,這就是我說的間歇性獲取。

到這里死鎖條件已經成熟,只要遇到這種情況就可能需要人為介入才能繼續了。

六、關于mysqldump

社區版在如下情況下需要增加FTWRL:設置了master-data

設置了singal-transaction和flush-logs

percona版在如下情況需要增加FTWRL:設置了singal-transaction和flush-logs

我們來大概看看社區版的代碼如下(代碼版本8.0.21),下面是從FTWRL倒UNLOCK的過程:if?((opt_lock_all_tables?||?opt_master_data?||?//如果設置了?master?data?設置flush?table?with?read?lock???????(opt_single_transaction?&&?flush_logs))?&&//如果設置了single?transaction和flush?logs?設置flush?table?with?read?lock??????do_flush_tables_read_lock(mysql))?//設置flush?table?with?read?lock????goto?err;??/*??/*????Flush?logs?before?starting?transaction?since????this?causes?implicit?commit?starting?mysql-5.5.??*/??if?(opt_lock_all_tables?||?opt_master_data?||???????(opt_single_transaction?&&?flush_logs)?||?opt_delete_master_logs)?{????if?(flush_logs?||?opt_delete_master_logs)?{//如果設置了?flush?logs?進行日志刷新??????if?(mysql_refresh(mysql,?REFRESH_LOG))?{?//進行日志刷新????????DB_error(mysql,?"when?doing?refresh");????????goto?err;??????}??????verbose_msg("--?main?:?logs?flushed?successfully!\n");????}????/*?Not?anymore!?That?would?not?be?sensible.?*/????flush_logs?=?false;??}??if?(opt_delete_master_logs)?{????if?(get_bin_log_name(mysql,?bin_log_name,?sizeof(bin_log_name)))?goto?err;??}??if?(opt_single_transaction?&&?start_transaction(mysql))?goto?err;?//開啟事務?RR??/*?Add?'STOP?SLAVE?to?beginning?of?dump?*/??if?(opt_slave_apply?&&?add_stop_slave())?goto?err;??/*?Process?opt_set_gtid_purged?and?add?SET?@@GLOBAL.GTID_PURGED?if?required.???*/??if?(process_set_gtid_purged(mysql))?goto?err;?//設置GTID,如果設置了gtid_purged?這個函數會跳過??if?(opt_master_data?&&?do_show_master_status(mysql))?goto?err;?//獲取主庫binlog位置??if?(opt_slave_data?&&?do_show_slave_status(mysql))?goto?err;?//slave_data?設置相關?從show?slave中獲取??if?(opt_single_transaction?&&??????do_unlock_tables(mysql))?/*?unlock?but?no?commit!?*/????goto?err;

percona版本中增加了判斷函數 check_consistent_binlog_pos,如下(不過多描述):if?(opt_single_transaction?&&?opt_master_data)??{????/*???????See?if?we?can?avoid?FLUSH?TABLES?WITH?READ?LOCK?with?Binlog_snapshot_*???????variables.????*/????consistent_binlog_pos=?check_consistent_binlog_pos(NULL,?NULL);??}??if?((opt_lock_all_tables?||?(opt_master_data?&&?!consistent_binlog_pos)?||//consistent_binlog_pos?0?需要?1?不需要???????(opt_single_transaction?&&?flush_logs)))??{????if?(do_flush_tables_read_lock(mysql))??????goto?err;??}

七、如何解決

總結如下:master-data 一般備份都會增加,因此只能在低峰期進行備份,盡量減少影響。

考慮關閉參數 slave_preserve_commit_order。但是FTWRL的堵塞還是存在,只是不會產生死鎖。

如果壓力不大可以考慮關閉MTS。但是FTWRL的堵塞還是存在,只是不會產生死鎖。

全文完。

Enjoy MySQL :)

掃碼添加作者微信

葉老師的「MySQL核心優化」大課已升級到MySQL 8.0,掃碼開啟MySQL 8.0修行之旅吧

總結

以上是生活随笔為你收集整理的mysql slave lock 跳过_slave开启MTS时执行mysqldump引发死锁案例的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。