bitsCN.com
转载请署名:印风
-----------------------------------------------------------
最近碰到一个问题,线上一台机器在等待信号量时间过长,mysql的监控线程认为此时mysqld已经hang住了,于是自杀重启。这里涉及到一个有趣的问题,也就是mysql如何对读写锁进行处理。
主要包括三个部分:
1. 建锁
2. 加锁
3. 解锁
4. 监控锁
以下内容基于Percona5.5.18进行分析
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 && lock->lock_word 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 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,用于清理在一段时间内的空闲事务。这个变量指定了空闲事务的最长时间。具体实现分析,且听下回分解
作者 记录成长之路 bitsCN.com

MySQLはオープンソースのリレーショナルデータベース管理システムであり、主にデータを迅速かつ確実に保存および取得するために使用されます。その実用的な原則には、クライアントリクエスト、クエリ解像度、クエリの実行、返品結果が含まれます。使用法の例には、テーブルの作成、データの挿入とクエリ、および参加操作などの高度な機能が含まれます。一般的なエラーには、SQL構文、データ型、およびアクセス許可、および最適化の提案には、インデックスの使用、最適化されたクエリ、およびテーブルの分割が含まれます。

MySQLは、データストレージ、管理、クエリ、セキュリティに適したオープンソースのリレーショナルデータベース管理システムです。 1.さまざまなオペレーティングシステムをサポートし、Webアプリケーションやその他のフィールドで広く使用されています。 2。クライアントサーバーアーキテクチャとさまざまなストレージエンジンを通じて、MySQLはデータを効率的に処理します。 3.基本的な使用には、データベースとテーブルの作成、挿入、クエリ、データの更新が含まれます。 4.高度な使用には、複雑なクエリとストアドプロシージャが含まれます。 5.一般的なエラーは、説明ステートメントを介してデバッグできます。 6.パフォーマンスの最適化には、インデックスの合理的な使用と最適化されたクエリステートメントが含まれます。

MySQLは、そのパフォーマンス、信頼性、使いやすさ、コミュニティサポートに選択されています。 1.MYSQLは、複数のデータ型と高度なクエリ操作をサポートし、効率的なデータストレージおよび検索機能を提供します。 2.クライアントサーバーアーキテクチャと複数のストレージエンジンを採用して、トランザクションとクエリの最適化をサポートします。 3.使いやすく、さまざまなオペレーティングシステムとプログラミング言語をサポートしています。 4.強力なコミュニティサポートを提供し、豊富なリソースとソリューションを提供します。

INNODBのロックメカニズムには、共有ロック、排他的ロック、意図ロック、レコードロック、ギャップロック、次のキーロックが含まれます。 1.共有ロックにより、トランザクションは他のトランザクションが読み取らないようにデータを読み取ることができます。 2.排他的ロックは、他のトランザクションがデータの読み取りと変更を防ぎます。 3.意図ロックは、ロック効率を最適化します。 4。ロックロックインデックスのレコードを記録します。 5。ギャップロックロックインデックス記録ギャップ。 6.次のキーロックは、データの一貫性を確保するためのレコードロックとギャップロックの組み合わせです。

MySQLクエリのパフォーマンスが低いことの主な理由には、インデックスの使用、クエリオプティマイザーによる誤った実行計画の選択、不合理なテーブルデザイン、過剰なデータボリューム、ロック競争などがあります。 1.インデックスがゆっくりとクエリを引き起こし、インデックスを追加するとパフォーマンスが大幅に向上する可能性があります。 2。説明コマンドを使用してクエリ計画を分析し、オプティマイザーエラーを見つけます。 3.テーブル構造の再構築と結合条件を最適化すると、テーブルの設計上の問題が改善されます。 4.データボリュームが大きい場合、パーティション化とテーブル分割戦略が採用されます。 5.高い並行性環境では、トランザクションの最適化とロック戦略は、ロック競争を減らすことができます。

データベースの最適化では、クエリ要件に従ってインデックス作成戦略を選択する必要があります。1。クエリに複数の列が含まれ、条件の順序が固定されている場合、複合インデックスを使用します。 2。クエリに複数の列が含まれているが、条件の順序が修正されていない場合、複数の単一列インデックスを使用します。複合インデックスは、マルチコラムクエリの最適化に適していますが、単一列インデックスは単一列クエリに適しています。

MySQLスロークエリを最適化するには、slowquerylogとperformance_schemaを使用する必要があります。1。LowerQueryLogを有効にし、しきい値を設定して、スロークエリを記録します。 2。performance_schemaを使用してクエリの実行の詳細を分析し、パフォーマンスのボトルネックを見つけて最適化します。

MySQLとSQLは、開発者にとって不可欠なスキルです。 1.MYSQLはオープンソースのリレーショナルデータベース管理システムであり、SQLはデータベースの管理と操作に使用される標準言語です。 2.MYSQLは、効率的なデータストレージと検索機能を介して複数のストレージエンジンをサポートし、SQLは簡単なステートメントを通じて複雑なデータ操作を完了します。 3.使用の例には、条件によるフィルタリングやソートなどの基本的なクエリと高度なクエリが含まれます。 4.一般的なエラーには、SQLステートメントをチェックして説明コマンドを使用することで最適化できる構文エラーとパフォーマンスの問題が含まれます。 5.パフォーマンス最適化手法には、インデックスの使用、フルテーブルスキャンの回避、参加操作の最適化、コードの読み取り可能性の向上が含まれます。


ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

AtomエディタMac版ダウンロード
最も人気のあるオープンソースエディター

ZendStudio 13.5.1 Mac
強力な PHP 統合開発環境

DVWA
Damn Vulnerable Web App (DVWA) は、非常に脆弱な PHP/MySQL Web アプリケーションです。その主な目的は、セキュリティ専門家が法的環境でスキルとツールをテストするのに役立ち、Web 開発者が Web アプリケーションを保護するプロセスをより深く理解できるようにし、教師/生徒が教室環境で Web アプリケーションを教え/学習できるようにすることです。安全。 DVWA の目標は、シンプルでわかりやすいインターフェイスを通じて、さまざまな難易度で最も一般的な Web 脆弱性のいくつかを実践することです。このソフトウェアは、

WebStorm Mac版
便利なJavaScript開発ツール

Safe Exam Browser
Safe Exam Browser は、オンライン試験を安全に受験するための安全なブラウザ環境です。このソフトウェアは、あらゆるコンピュータを安全なワークステーションに変えます。あらゆるユーティリティへのアクセスを制御し、学生が無許可のリソースを使用するのを防ぎます。
