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設置為NULL4、返回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可以看到這種操作如下:
好了回到上面的問題, 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的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: uboot makefile分析之 ma
- 下一篇: exchange 2013 邮箱服务器主