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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Update高并发下变慢分析及semi-consistent read

發布時間:2024/1/18 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Update高并发下变慢分析及semi-consistent read 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

背景提醒

本文主要討論的是RC隔離級別,代碼主要集中在5.7.22。

為了描述方便本文中涉及的semi update就是官方說的semi-consistent read特性。

水平有限,僅供參考。

一、問題說明

最近遇到一個問題,以下是模擬出來的現象(RC隔離級別,5.7.31版本),正常情況下,這個update語句的執行時間很快,但是到了高并發情況下就很慢了。

當然這個問題解決很簡單,但是其背后還是有很多值得挖掘的地方,這里就從問題分析出發,順帶挖一下其涉及的部分。

二、分析方式

既然是update語句并發處理的情況變慢,我們先從常規觸發看看是不是被阻塞了。

首先我們能看到state為updating狀態,那么就說明如下:

  • MDL LOCK阻塞不可能,因為state狀態不對,MDL LOCK阻塞的現象是waitting for開頭的。

  • 可能是row lock阻塞,因為在update語句的情況下row lock阻塞也是updating狀態。

進一步通過show engine 和 確認沒有出現row lock阻塞,show engine截圖如下:

我們可以看到這里事務都處于活躍狀態,大部分是unlock_row階段,也有fetching rows階段的事務,那么說明事務是在運行的。

接下來通過CPU耗用確認是否會話出現了內部阻塞,如果長時間的阻塞CPU肯定會下降,如果是在耗用CPU干活就可能CPU就比較高,如下:

我們看到CPU還是比較高的,那么CPU高也有兩種可能就是遇到spin 和 正常的代碼邏輯。

對于spin來講,一般是內部mutex在正式放棄CPU前做的多次嘗試,這個和我們的參數innodb_sync_spin_loops/innodb_spin_wait_delay設置有關(一般沒有設置保持默認值),并且show engine 可能會有輸出,通過show engine進行確認如下:

這里我們確實可以看到一個mutex叫做LOCK_SYS,接著看看perf信息如下:

確實有大量的ut_delay耗用CPU,且函數指向了加行鎖等待上,同時LOCK_SYS也正是row_lock的全局hash結構所在位置的mutex。

這就說明了這個語句出現了大量的row_lock需要加鎖和解鎖,導致LOCK_SYS mutex出現了熱點鎖。

接著查看表結構,建表語句如下:

create?table?testsemi( a?int?auto_increment?primary?key, b?int, c?int, d?int, key(b,c));數據量大約百萬左右。?修改語句大概如下:update?testsemi?set?d=20?where?c=20;

當然這樣由于c=20不是索引的前綴,在RR模式下會出現表中所有航全部加行鎖的問題,而在RC模式下會觸發2個優化:

  • Innodb層 semi update

  • MySQL層unlock row

解決當然也很簡單,起碼c列上要有個索引能夠用到。接下來我們就討論這兩個優化大概實現方式和一個存在的問題。

三、RC隔離級別下的semi update和unlock row優化

3.1 相關列子

為了更好的解釋這兩種特性我們先來看兩個例子,建表語句和數據如下:

mysql>?show?variables?like?'%transaction_isolation%'; +-----------------------+----------------+ |?Variable_name?????????|?Value??????????| +-----------------------+----------------+ |?transaction_isolation?|?READ-COMMITTED?| +-----------------------+----------------+ mysql>?show?create?table?testsemi30?\G; ***************************?1.?row?***************************Table:?testsemi30 Create?Table:?CREATE?TABLE?`testsemi30`?(`a`?int(11)?NOT?NULL?AUTO_INCREMENT,`b`?int(11)?DEFAULT?NULL,`c`?int(11)?DEFAULT?NULL,`d`?int(11)?NOT?NULL,PRIMARY?KEY?(`a`),KEY?`b`?(`b`,`c`) )?ENGINE=InnoDB?AUTO_INCREMENT=29?DEFAULT?CHARSET=utf8mb4?COLLATE=utf8mb4_unicode_ci 1?row?in?set?(0.00?sec)mysql>?select?*?from?testsemi30; +----+------+------+---+ |?a??|?b????|?c????|?d?| +----+------+------+---+ |??2?|????2?|????2?|?0?| |??4?|????4?|????4?|?0?| |??6?|????6?|????6?|?0?| |??8?|????8?|????8?|?0?| |?12?|???12?|???12?|?0?| +----+------+------+---+

例子1:

session1: mysql>?begin; Query?OK,?0?rows?affected?(0.01?sec) mysql>?update?testsemi30?set?d=6?where?c=6; Query?OK,?1?row?affected?(0.00?sec) Rows?matched:?1??Changed:?1??Warnings:?0 mysql>?desc?update?testsemi30?set?d=6?where?c=6; +----+-------------+------------+------------+-------+---------------+---------+---------+------+------+----------+-------------+ |?id?|?select_type?|?table??????|?partitions?|?type??|?possible_keys?|?key?????|?key_len?|?ref??|?rows?|?filtered?|?Extra???????| +----+-------------+------------+------------+-------+---------------+---------+---------+------+------+----------+-------------+ |??1?|?UPDATE??????|?testsemi30?|?NULL???????|?index?|?NULL??????????|?PRIMARY?|?4???????|?NULL?|????5?|???100.00?|?Using?where?| +----+-------------+------------+------------+-------+---------------+---------+---------+------+------+----------+-------------+ 1?row?in?set?(0.01?sec)

顯然這個語句是全表掃描的update,但是最終看到的加鎖row lock只有一條如下:

---TRANSACTION?808623,?ACTIVE?19?sec 2?lock?struct(s),?heap?size?1160,?1?row?lock(s),?undo?log?entries?1 MySQL?thread?id?16,?OS?thread?handle?140735862056704,?query?id?349?localhost?root TABLE?LOCK?table?`test`.`testsemi30`?trx?id?808623?lock?mode?IX RECORD?LOCKS?space?id?9694?page?no?3?n?bits?72?index?PRIMARY?of?table?`test`.`testsemi30`?trx?id?808623?lock_mode?X(LOCK_X)?locks?rec?but?not?gap(LOCK_REC_NOT_GAP) Record?lock,?heap?no?4?PHYSICAL?RECORD:?n_fields?6;?compact?format;?info?bits?00:?len?4;?hex?80000006;?asc?????;;1:?len?6;?hex?0000000c56af;?asc?????V?;;2:?len?7;?hex?7b000001ea0fdc;?asc?{??????;;3:?len?4;?hex?80000006;?asc?????;;4:?len?4;?hex?80000006;?asc?????;;5:?len?4;?hex?80000006;?asc?????;;

這就是unlock row的核心作用,但是實際上每行都加過鎖,只是不符合where條件的記錄的被unlock 掉了,下文描述。繼續做一個操作如下:

session2: mysql>?begin; Query?OK,?0?rows?affected?(0.00?sec) mysql>?select?*?from?testsemi30?where?c=4?for?update; 此處被阻塞,row lock如下: TABLE?LOCK?table?`test`.`testsemi30`?trx?id?808624?lock?mode?IX RECORD?LOCKS?space?id?9694?page?no?3?n?bits?72?index?PRIMARY?of?table?`test`.`testsemi30`?trx?id?808624?lock_mode?X(LOCK_X)?locks?rec?but?not?gap(LOCK_REC_NOT_GAP) Record?lock,?heap?no?3?PHYSICAL?RECORD:?n_fields?6;?compact?format;?info?bits?00:?len?4;?hex?80000004;?asc?????;;1:?len?6;?hex?0000000c5687;?asc?????V?;;2:?len?7;?hex?e200000089011d;?asc????????;;3:?len?4;?hex?80000004;?asc?????;;4:?len?4;?hex?80000004;?asc?????;;5:?len?4;?hex?80000004;?asc?????;;RECORD?LOCKS?space?id?9694?page?no?3?n?bits?72?index?PRIMARY?of?table?`test`.`testsemi30`?trx?id?808624?lock_mode?X(LOCK_X)?locks?rec?but?not?gap(LOCK_REC_NOT_GAP)?waiting(LOCK_WAIT) Record?lock,?heap?no?4?PHYSICAL?RECORD:?n_fields?6;?compact?format;?info?bits?00:?len?4;?hex?80000006;?asc?????;;1:?len?6;?hex?0000000c56af;?asc?????V?;;2:?len?7;?hex?7b000001ea0fdc;?asc?{??????;;3:?len?4;?hex?80000006;?asc?????;;4:?len?4;?hex?80000006;?asc?????;;5:?len?4;?hex?80000006;?asc?????;;

這是因為這個語句雖然會觸發unlock row,但是當加鎖在primary id a=6 這一行的時候被session 1阻塞了,因為session 1經過unlock row特性優化后還是持有primary id a=6的這行記錄的鎖,當然select語句不存在semi update一說。

例子2

如果將上面session 2的select for update語句換為update語句就不同了:

mysql>?begin; Query?OK,?0?rows?affected?(0.00?sec) mysql>?update?testsemi30?set?d=4?where?c=4; Query?OK,?1?row?affected?(0.00?sec) Rows?matched:?1??Changed:?1??Warnings:?0 這個語句是可以完成。事務上鎖如下: ---TRANSACTION?808627,?ACTIVE?4?sec 2?lock?struct(s),?heap?size?1160,?2?row?lock(s),?undo?log?entries?1 MySQL?thread?id?18,?OS?thread?handle?140735862867712,?query?id?363?localhost?root TABLE?LOCK?table?`test`.`testsemi30`?trx?id?808627?lock?mode?IX RECORD?LOCKS?space?id?9694?page?no?3?n?bits?72?index?PRIMARY?of?table?`test`.`testsemi30`?trx?id?808627?lock_mode?X(LOCK_X)?locks?rec?but?not?gap(LOCK_REC_NOT_GAP) Record?lock,?heap?no?3?PHYSICAL?RECORD:?n_fields?6;?compact?format;?info?bits?00:?len?4;?hex?80000004;?asc?????;;1:?len?6;?hex?0000000c56b3;?asc?????V?;;2:?len?7;?hex?7e000001da1d79;?asc?~?????y;;3:?len?4;?hex?80000004;?asc?????;;4:?len?4;?hex?80000004;?asc?????;;5:?len?4;?hex?80000004;?asc?????;;

這實際上就是semi update的核心理念,它能夠讓本應該阻塞的update語句繼續執行,即便session 1持有primary id a=6的這行記錄的鎖,也可以繼續。

3.2 unlock row特性

就是例子1中的測試

1、Update訪問一條數據,innodb層獲取row lock。

2、MySQL層根據where條件,如果是不需要的行,則直接unlock掉,這個操作的核心函數就是ha_innobase::unlock_row

而在Update上,我們也很容看到這種比較和過濾,下面是MySQL 過濾where條件的行

mysql_update:if?((!qep_tab.skip_record(thd,?&skip_record)?&&?!skip_record))?//跳過操作?是否符合查詢條件 table->file->unlock_row();?//如果是where條件過濾的直接跳到解鎖這步對比比較我們可以直接debug整數的比較函數如下: #0??Item_func_eq::val_int?(this=0x7fff2800ad28)?at?/opt/percona-server-locks-detail-5.7.22/sql/item_cmpfunc.cc:2506 #1??0x0000000000f4a17b?in?QEP_TAB::skip_record?(this=0x7fff9f1cdf78,?thd=0x7fff28012cc0,?skip_record_arg=0x7fff9f1ce0fe)?at?/opt/percona-server-locks-detail-5.7.22/sql/sql_executor.h:457 #2??0x0000000001626efa?in?mysql_update?(thd=0x7fff28012cc0,?fields=...,?values=...,?limit=18446744073709551615,?handle_duplicates=DUP_ERROR,?found_return=0x7fff9f1ce268,?updated_return=0x7fff9f1ce260)?at?/opt/percona-server-locks-detail-5.7.22/sql/sql_update.cc:816 這個地方可以看到兩個比較的值 (gdb)?p?val1 $12?=?2 (gdb)?p?val2 $13?=?2

另外在ha_innobase::unlock_row函數中為了適配semi update,也做了相應的邏輯如下,

switch?(m_prebuilt->row_read_type)?{case?ROW_READ_WITH_LOCKS:?//如果是加鎖了if?(!srv_locks_unsafe_for_binlog?//判定隔離級別為RC才做解鎖&&?m_prebuilt->trx->isolation_level>?TRX_ISO_READ_COMMITTED)?{break;}/*?fall?through?*/case?ROW_READ_TRY_SEMI_CONSISTENT://如果semi?update,TRY_SEMI才進行解鎖row_unlock_for_mysql(m_prebuilt,?FALSE);??mysql_updatebreak;case?ROW_READ_DID_SEMI_CONSISTENT://如果semi?update,為DID_SEMI那么就不做了,因為沒有鎖可以解了,semi?update?已經在引擎層解掉了m_prebuilt->row_read_type?=?ROW_READ_TRY_SEMI_CONSISTENT;break;}

這是因為對于semi update遇到row lock阻塞的時候直接就在阻塞后直接解鎖了,不需要回到MySQL層解鎖(如下文所述)。那么這個特性兩個重要影響就是如下:

  • 每行row lock加鎖是不可避免的,但是會在MySQL層判定后解鎖,那么最終這個事務加鎖的記錄就會很少,這會提高業務的并發,這一點是非常重要的,這種情況下show engine 最終看到的row lock 鎖信息就很少了。

  • 但是頻繁的lock/unlock rec導致LOCK_SYS這個mutex很容易成為熱點mutex。

我們可以簡單看一下unlock rec的函數lock_rec_unlock,這個函數一上來就可能看到加鎖LOCK_SYS,然后通過hash算法,在lock_sys_t中找到對用cell的頭節點,然后遍歷找到相應的block對應的lock_t結構,然后調用lock_rec_reset_nth_bit函數,解鎖相應的位圖結構(row lock所在的位置)。

3.3 semi update特性

就是例子2中的測試,這個特性一定要在出現了row lock阻塞后才會進行判定,是innodb層直接就解除了阻塞,如下,

1、Update 修改一行數據之前設置標記ROW_READ_TRY_SEMI_CONSISTENT

2、訪問一行數據,innodb層嘗試獲取row lock,如果被阻塞則觸發semi update判定,判定的規則包含

  • 不能為唯一性掃描(unique_search)

  • 必須為主鍵(index != clust_index)

  • 不能產生死鎖(Check whether it was a deadlock or not)

  • RC隔離級別或者innodb_locks_unsafe_for_binlog參數設置了(8.0移除了本參數)

  • update語句才可以

主鍵的非唯一性掃描,最常見的就是全表掃描了。

3、訪問本行修改前的old rec 記錄(row_sel_build_committed_vers_for_mysql),并且解除阻塞(lock_cancel_waiting_and_release),解除的時候,會將事務wait_lock設置為NULL,同時從 trx_lock中移除,lock_sys_t中的hash結構也會清除掉。實際上lock_cancel_waiting_and_release就是本特性的核心函數。及如下:

lock_cancel_waiting_and_release->lock_rec_dequeue_from_page?//lock_sys_t中的hash結構會清除,trx_lock中移除->lock_reset_lock_and_trx_wait?//wait_lock設置為NULL

4、返回old rec給mysql層,并且設置變量did_semi_consistent_read=true(導致設置標記ROW_READ_DID_SEMI_CONSISTENT)?

5、判定是否滿足where條件,如果不滿足就掃描下一行了,如果滿足再次進入innodb層進入阻塞狀態,這個時候ROW_READ_DID_SEMI_CONSISTENT標記已經設置不會再做semi update的判定了,同時如上文如果ROW_READ_DID_SEMI_CONSISTENT標記設置了就不會真正觸發unlock row操作。

和unlock row特性不同,unlock row 圍繞的核心是讓整個語句執行完成后加鎖的行更少,而semi update 圍繞的核心是出現了阻塞后update語句(觸發了全表掃描)是否能夠繼續,這是非常重要的不同點。

四、額外的問題

分析到這里,我們知道了本案例中是由于沒有使用到索引進行update語句出現了大量的lock rec和unlock rec 導致lock_sys_t 結構的mutex LOCK_SYS出現了熱點鎖,但是還有一個奇怪的問題如下:

image.png

注意到這里的row lock和lock struct 都是比較多的,為什么會這樣呢。

經過unlock row和semi update過后鎖定的行數應該是只有1行。

為了更方便的討論這部分,我們將涉及到的數據結構的元素畫個簡單的圖,同時講上面提到的lock_sys_t涉及的hash結構也畫一下,需要注意的是這些數據結構元素很多很多,這里只話了和問題相關的部分,涉及得很少。

這里需要注意幾點:

  • 對于這個rec_hash這個hash查找表的hash值來自于space_id和page_no

  • lock_t是所謂的lock struct,相關的屬性比如LOCK_X/LOCK_S,還有LOCK_REC_NOT_GAP/LOCK_GAP/LOCK_WAIT/LOCK_ORDINARY/LOCK_INSERT_INTENTION 等都是它的屬性,而不是某行記錄的屬性。言外之意如果獲取一個row lock,如果正常獲取就可以合并到現有page的lock_t中,如果阻塞了必須要新建lock_t,因為這個lock_t帶有屬性LOCK_WAIT。

  • 一個lock_t的bit map最多能夠容納下一個page的所有行的加鎖情況。

  • bit map才是實際的加鎖的體現,它附著在每一個lock_t結構上,innodb通過lock_t[1]快速的找到了他的位置,然后進行設置,在函數lock_rec_reset_nth_bit可以看到這種操作如下:

reinterpret_cast<byte*>(&lock[1])

好了回到上面的問題, row locks和lock struct這兩個輸出,實際上來自如下:

  • row locks:trx->lock->n_rec_locks 這個值是trx_lock_t上的一個統計值而已,在每個調用函數lock_rec_reset_nth_bit和lock_rec_set_nth_bit的末尾減少和增加,對應是解鎖和加鎖某一行操作。

  • lock struct: UT_LIST_GET_LEN(trx->lock.trx_locks) 這個值實際上就是上面我們看到的鏈表的長度,應該來說是比較準確的。

那么,雖然unlock row 釋放了rec lock也就是設置了其標記的bit位,但是lock_t結構本身沒有釋放,所以lock struct更多。

因為上鎖和解鎖通常要遍歷整個page所在lock_sys_t的cell鏈表上的所有lock struct,如果lock struct多那上LOCK_SYS mutex持有的時間就更長,也符合我們本次問題由于沒有用到索引,且并發執行大量的update導致的LOCK_SY mutex的spin。

但是row locks看起來就不那么準確了,隨后我做了一個測試,只做了少量的行,觸發了一次semi update,看到了結果也是2 row lock,如下:

表結構和數據: mysql>?show?create?table?testsemi40?\G ***************************?1.?row?***************************Table:?testsemi40 Create?Table:?CREATE?TABLE?`testsemi40`?(`a`?int(11)?NOT?NULL?AUTO_INCREMENT,`b`?int(11)?DEFAULT?NULL,`c`?int(11)?DEFAULT?NULL,`d`?int(11)?NOT?NULL,PRIMARY?KEY?(`a`),KEY?`b`?(`b`,`c`) )?ENGINE=InnoDB?AUTO_INCREMENT=7?DEFAULT?CHARSET=utf8mb4?COLLATE=utf8mb4_unicode_ci mysql>?select?*from?testsemi40; +---+------+------+----+ |?a?|?b????|?c????|?d??| +---+------+------+----+ |?2?|????2?|????2?|?0?| |?4?|????4?|????4?|?0?| |?6?|????6?|????6?|?0?| +---+------+------+----+ 3?rows?in?set?(0.00?sec) session 1: mysql>?begin; Query?OK,?0?rows?affected?(0.10?sec) mysql>?update?testsemi40?set?d=6?where?c=6; Query?OK,?1?row?affected?(0.00?sec) Rows?matched:?1??Changed:?1??Warnings:?0session2: mysql>?begin; Query?OK,?0?rows?affected?(0.10?sec) mysql>?update?testsemi40?set?d=2?where?c=2; Query?OK,?1?row?affected?(0.01?sec) Rows?matched:?1??Changed:?1??Warnings:?0show engine信息,session2上鎖的信息如下: ---TRANSACTION?808633,?ACTIVE?4?sec 2?lock?struct(s),?heap?size?1160,?2?row?lock(s),?undo?log?entries?1?(這里有2?row?locks) MySQL?thread?id?18,?OS?thread?handle?140735862867712,?query?id?381?localhost?root TABLE?LOCK?table?`test`.`testsemi40`?trx?id?808633?lock?mode?IX RECORD?LOCKS?space?id?9695?page?no?3?n?bits?72?index?PRIMARY?of?table?`test`.`testsemi40`?trx?id?808633?lock_mode?X(LOCK_X)?locks?rec?but?not?gap(LOCK_REC_NOT_GAP) Record?lock,?heap?no?2?PHYSICAL?RECORD:?n_fields?6;?compact?format;?info?bits?00:?len?4;?hex?80000002;?asc?????;;1:?len?6;?hex?0000000c56b9;?asc?????V?;;2:?len?7;?hex?21000001ec2701;?asc?!????'?;;3:?len?4;?hex?80000002;?asc?????;;4:?len?4;?hex?80000002;?asc?????;;5:?len?4;?hex?80000002;?asc?????;;

但是我順著show engine打印本事務的每個lock_t中的bit map加鎖結構如下:

斷點:lock_rec_print 大體輸出流程如下: lock_print_info_all_transactions 循環輸出所有的事務的信息?->lock_trx_print_locks?循環輸出當前事務的所有lock_t?行鎖信息->lock_rec_print?循環lock_t的位圖信息,打印出詳細的加鎖行我們只需要在lock_rec_print?函數中通過如下輸出 (gdb)?p?(&lock[1]) $21?=?(const?ib_lock_t?*)?0x2fd79c0 (gdb)?x/8bx?0x2fd79c0 0x2fd79c0:??????0x04????0x00????0x00????0x00????0x00????0x00????0x00????0x00 打印所有的lock_t結構就可以了

實際上這里只有一個實際上就只有1個lock_t(當然是rec_lock,不討論table_lock)結構,看到的加鎖信息就是0x04,轉二進制就是100,顯然就是1行加鎖了嘛,對應的heap no = 2這一行(heap no = 0和heap no = 1是innodb的page里面的2個偽記錄Infimum 和 Supremum)。利用工具blockinfo輸出可以確認如下:

(1)?INFIMUM?record?offset:99?heapno:0?n_owned?1,delflag:N?minflag:0?rectype:2 (2)?normal?record?offset:126?heapno:2?n_owned?0,delflag:N?minflag:0?rectype:0 (3)?SUPREMUM?record?offset:112?heapno:1?n_owned?5,delflag:N?minflag:0?rectype:3

這樣我們就確認了在semi update的方式下,row locks的這個計數器統計應該是出現問題的,有什么情況下不會調用lock_rec_reset_nth_bit函數來減少這個計數器呢?

實際這個問題就出現在semi update的核心函數lock_cancel_waiting_and_release上,解除等待時候是將整體lock_t結構給抹掉了,而MySQL層又不會調用unlock row,因為lock_t結構都沒有了,也就是核心減少計數器的函數lock_rec_reset_nth_bit并沒有調用。

因此這個trx->lock->n_rec_locks 計數器在semi update觸發的情況下只增加了沒減少。言外之意就是semi update在高并發下發生的次數越多,row locks的計數就越不準確。那么稍微修改一下代碼驗證一下(僅為驗證這種場景,這種修改可能并不可取),我使用在8.0.23上做了同樣測試結果一致,同時在8.0.23代碼上做的修改,增加2行如下:

void?lock_reset_lock_and_trx_wait(lock_t?*lock)?/*!<?in/out:?record?lock?*/ { ...@see?trx_lock_t::wait_lock_type?for?more?detailed?explanation.?*/lock->type_mode?&=?~LOCK_WAIT;?ut_ad(lock->trx->lock.n_rec_locks.load()?>?0);?//增加lock->trx->lock.n_rec_locks.fetch_sub(1,?std::memory_order_relaxed);??//增加

然后我們使用前面的方式繼續測試發現得到row lock值已經準確了如下:

---TRANSACTION?2740515,?ACTIVE?6?sec 2?lock?struct(s),?heap?size?1200,?1?row?lock(s),?undo?log?entries?1?(這里顯示正確了) MySQL?thread?id?9,?OS?thread?handle?140736352634624,?query?id?36?localhost?root?starting show?engine?innodb?status ---TRANSACTION?2740513,?ACTIVE?54?sec 2?lock?struct(s),?heap?size?1200,?1?row?lock(s),?undo?log?entries?1 MySQL?thread?id?8,?OS?thread?handle?140736353167104,?query?id?21?localhost?root

當然這么改可能是不合適的,因為這個函數調用者還很多,這里只是修改后驗證一下這個猜想。確實這種情況容易導致DBA誤判,實際上row lock 并沒有row locks統計出來的那么多,隨后給官方提交下BUG看看。

最后

這個問題處理起來還是比較簡單,但是背后還是有很多可以深挖的地方,本文主要使用的代碼是5.7.22,對于semi update下row locks不準的情況在8.0.28 也測試了,依舊存在這個問題。

另外在8.0中熱點鎖LOCK_SYS似乎做了拆分,也許情況會好一些,隨后也可以學習下這部分內容,看看官方如何拆鎖的。

全文完。


《深入淺出MGR》視頻課程

戳此小程序即可直達B站

https://www.bilibili.com/medialist/play/1363850082?business=space_collection&business_id=343928&desc=0



文章推薦:

  • 有事務沖突時節點怎么加入MGR集群

  • MySQL 的prepare使用中的bug解析過程

  • 為MySQL MGR實現簡單的負載均衡代理

  • MySQL show create table底層流程跟蹤

  • 4.直方圖介紹和使用|MySQL索引學習

  • 聯合索引、覆蓋索引及最左匹配原則|MySQL索引學習

  • MySQL表操作過程的基礎代碼解析

  • MySQL Update執行流程解讀

  • MySQL B+樹索引和哈希索引介紹


想看更多技術好文,點個“在看”吧!

總結

以上是生活随笔為你收集整理的Update高并发下变慢分析及semi-consistent read的全部內容,希望文章能夠幫你解決所遇到的問題。

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