久久精品人人爽,华人av在线,亚洲性视频网站,欧美专区一二三

MySQL中怎么處理讀寫鎖

121次閱讀
沒有評論

共計 4840 個字符,預計需要花費 13 分鐘才能閱讀完成。

自動寫代碼機器人,免費開通

這期內容當中丸趣 TV 小編將會給大家帶來有關 MySQL 中怎么處理讀寫鎖,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

 
1. 創建鎖
鎖的創建實際上就是初始化一個 RW 結構體(rw_lock_t),實際調用函數如下:
 
# define rw_lock_create(K, L, level)   \ 
  rw_lock_create_func((L),#L) 
 
在 rw_lock_create 上有三個參數,在實際場景鎖時只用到第 2 個參數
其中 K 表示 mysql_pfs_key_t,level 顯示當前的操作類型(起碼看起來是的,在文件 sync0sync.h 中定義),看起來 k 是為 performance schema 準備的,而 k 代表了當前操作所在的層次。
例如:purge 線程的讀寫鎖創建:
 
rw_lock_create(trx_purge_latch_key, 
  purge_sys- latch,SYNC_PURGE_LATCH); 
 
我們進去 rw_lock_create_func 看看到底是怎么創建的。
可以看到這個函數的邏輯其實很簡單:
lock- lock_word =X_LOCK_DECR;  // 關鍵字段
用于限制讀寫鎖的最大并發數,代碼里的注釋如下:
 
/* We decrement lock_word by this amountfor each x_lock. It is also the
start value for the lock_word, meaning thatit limits the maximum number
of concurrent read locks before the rw_lockbreaks. The current value of
0x00100000 allows 1,048,575 concurrentreaders and 2047 recursive writers.*/ 
 
在嘗試加鎖時會調用 rw_lock_lock_word_decr 減少 lock_word
  在初始化一系列變量后,執行:
 
lock- event = os_event_create(NULL); 
lock- wait_ex_event = os_event_create(NULL); 
os_event_create 用于創建一個系統信號,實際上最終創建的還是互斥量(os_fast_mutex_init((event- os_mutex)); 以及條件變量(os_cond_init((event- cond_var));)
最后將 lock 加入到全局鏈表 rw_lock_list 中
 
2. 加鎖
加鎖函數由宏定義,實際調用函數為:
1)寫鎖
 
# define rw_lock_x_lock(M)  \ 
  rw_lock_x_lock_func((M),0, __FILE__, __LINE__) 
 
當申請寫鎖時,執行如下步驟:
(1). 調用 rw_lock_x_lock_low 函數去獲取鎖,如果得到鎖,則 rw_x_spin_round_count += i 后直接返回,如果得不到鎖,繼續執行
(2).loop 過程中只執行一次 rw_x_spin_wait_count++
(3). 在毫秒級別的 loop 多次等待
 
while (i SYNC_SPIN_ROUNDS 
  lock- lock_word = 0) { 
  if(srv_spin_wait_delay) { 
   ut_delay(ut_rnd_interval(0, 
  srv_spin_wait_delay)); 
  } 
  i++; 
  } 
 
這里涉及到兩個系統變量:
innodb_sync_spin_loops(SYNC_SPIN_ROUNDS)
innodb_spin_wait_delay(srv_spin_wait_delay)
 
在 SYNC_SPIN_ROUNDS 循環里調用函數 ut_delay,這個函數很簡單,就是做了 delay*50 次空循環
 
Ut_delay(uint delay): 
  for(i = 0; i delay * 50; i++) { 
  j+= i; 
   UT_RELAX_CPU(); 
  } 
其中,UT_RELAX_CPU()會調用匯編指令來獨占 CPU,以防止線程切換
(4). 如果 loop 的次數等于 SYNC_SPIN_ROUNDS,調用 os_thread_yield(實際調用 pthread_yield,導致調用線程放棄 CPU 的占用)將線程掛起;否則挑到 1 繼續 loop
(5). 在 sync_primary_wait_array 里獲取一個 cell(占個坑?)。調用 sync_array_reserve_cell,看起來有 1000 個坑位(sync_primary_wait_array- n_cells)
(6). 再次調用 rw_lock_x_lock_low 函數嘗試獲取鎖,若成功獲得,則返回
(7). 調用 sync_array_wait_event 等待條件變量,然后返回 1 繼續 loop
具體的加鎖函數(rw_lock_x_lock_low)稍后分析
 
2) 讀鎖
 
# define rw_lock_s_lock(M)  \ 
  rw_lock_s_lock_func((M),0, __FILE__, __LINE__) 
 
這個函數定義在 sync0rw.ic 里,函數也很簡單,如下:
 
  if (rw_lock_s_lock_low(lock, pass, file_name, line)) { 
  return; /* Success */ 
  }else { 
  /* Did not succeed, try spin wait */ 
  rw_lock_s_lock_spin(lock, pass, file_name, line); 
  return; 

 
這里首先調用 rw_lock_s_lock_low 進行加鎖,如果加鎖不成功,則調用 rw_lock_s_lock_spin 進行等待,rw_lock_s_lock_spin 的代碼邏輯與 rw_lock_x_lock_func 有些相似,這里不再贅述。
在 rw_lock_s_lock_spin 里會遞歸的調用到 rw_lock_s_lock_low 函數;
 
看起來實際的加鎖和解鎖操作是通過對計數器來控制的,
(1)在函數 rw_lock_s_lock_low 中
rw_lock_lock_word_decr (lock, 1),對 lock- lock_word 減去 1
減數成功返回 true,否則返回 false
這部分的邏輯還是很簡單的。
 
(2)在函數 rw_lock_x_lock_low 中,調用:
rw_lock_lock_word_decr(lock, X_LOCK_DECR),對 lock- lock_word 減去 X_LOCK_DECR
減數成功后,執行:
 
rw_lock_set_writer_id_and_recursion_flag(lock,pass ? FALSE : TRUE)來設置: 
lock- writer_thread = s_thread_get_curr_id() 
lock- recursive = TRUE 
 
然后調用 rw_lock_x_lock_wait 函數等待 lock- lock_word=0,也就是說等待所有的讀鎖退出。
 
看到一個比較有意思的現象,在.ic 的代碼里看到使用了宏
INNODB_RW_LOCKS_USE_ATOMICS,這是跟 gcc 的版本相關的,通過使用 gcc 的內建函數來實現原子操作。
 
3. 解鎖
解鎖操作包括解除讀鎖(#define rw_lock_s_unlock(L) rw_lock_s_unlock_gen(L, 0))和解除寫鎖操作(#definerw_lock_x_unlock(L) rw_lock_x_unlock_gen(L, 0))
實際調用函數為 rw_lock_s_unlock_func 和 rw_lock_x_unlock_func
 
1)解除讀鎖(rw_lock_s_unlock_func)
增加計數 rw_lock_lock_word_incr(lock, 1)
 
2)解除寫鎖(rw_lock_x_unlock_func)
執行如下操作
(1) 如果是最后一個遞歸調用鎖的線程,設置 lock- recursive= FALSE; 代碼里的注釋如下:
 
/* lock- recursive flag also indicatesif lock- writer_thread is
  valid or stale. If we are the last of the recursive callers
  then we must unset lock- recursive flag to indicate that the
  lock- writer_thread is now stale.
  Note that since we still hold the x-lock we can safely read the
  lock_word. */ 
 
(2)增加計數 rw_lock_lock_word_incr(lock,X_LOCK_DECR) == X_LOCK_DECR,這時候需要向等待鎖的線程發送信號:
 
if (lock- waiters) { 
  rw_lock_reset_waiter_flag(lock); 
  os_event_set(lock- event); 
  sync_array_object_signalled(sync_primary_wait_array); 

 
os_event_set 函數會發送一個 pthread_cond_broadcast 給等待的線程
 
4. 監控讀寫鎖
為了防止 mysqld 被 hang 住導致的長時間等待 rw 鎖,error 監控線程會對長時間等待的線程進行監控。這個線程每 1 秒 loop 一次
(os_event_wait_time_low(srv_error_event, 1000000, sig_count);)
函數入口:srv_error_monitor_thread
函數 sync_array_print_long_waits()用于處理長時間等待信號量的線程,流程如下:
1. 查看 sync_primary_wait_array 數組中的所有等待線程。
– 大于 240 秒時,向錯誤日志中輸出警告,設置 noticed = TRUE;
– 大于 600 秒時,設置 fatal =TRUE;
2. 當 noticed 為 true 時,打印出 innodb 監控信息,然后 sleep30 秒
3. 返回 fatal 值
 
當函數 sync_primary_wait_array 返回 true 時,對于同一個等待線程還會有十次機會,也就是 300 + 1*10(監控線程每次 loop sleep 1s)秒的時間;如果挺不過去,監控線程就會執行一個斷言失敗:
 
if (fatal_cnt 10) { 
  fprintf(stderr, 
  InnoDB:Error: semaphore wait has lasted  
  %lu seconds\n  
  InnoDB:We intentionally crash the server,  
  because it appears to be hung.\n , 
  (ulong) srv_fatal_semaphore_wait_threshold); 
 
  ut_error; 
  } 
 
ut_error 是一個宏:
 
#define ut_error  assert(0) 
斷言失敗導致 mysqld crash
  在函數 srv_error_monitor_thread 里發現一個比較有意思的參數 srv_kill_idle_transaction,對應的系統變量為 innodb_kill_idle_transaction,用于清理在一段時間內的空閑事務。這個變量指定了空閑事務的最長時間。

上述就是丸趣 TV 小編為大家分享的 MySQL 中怎么處理讀寫鎖了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注丸趣 TV 行業資訊頻道。

向 AI 問一下細節

正文完
 
丸趣
版權聲明:本站原創文章,由 丸趣 2023-12-04發表,共計4840字。
轉載說明:除特殊說明外本站除技術相關以外文章皆由網絡搜集發布,轉載請注明出處。
評論(沒有評論)
主站蜘蛛池模板: 定边县| 建瓯市| 库车县| 沐川县| 吴忠市| 云林县| 五河县| 阿克陶县| 巴塘县| 新巴尔虎左旗| 吉林市| 博客| 宜春市| 新河县| 奉新县| 安康市| 宁安市| 乡宁县| 上杭县| 三亚市| 西藏| 济宁市| 吴堡县| 财经| 怀柔区| 文成县| 弋阳县| 广安市| 资阳市| 札达县| 杂多县| 个旧市| 昌都县| 宿迁市| 常山县| 高邑县| 美姑县| 芮城县| 蓬溪县| 芜湖县| 资阳市|