探索MySQL源代码之SQL历险记
本文从一个select语句的执行过程出发,遍历MySQL的多个几子系统。 先放图一张, 按图索骥开始我们的历险. 当客户端连接上MySQL服务端之后,发出请求之前,服务端的线程是阻塞在do_command(sql/parse.cc)里的my_net_read函数中(就是socket里的read). 当客户端键
本文从一个select语句的执行过程出发,遍历MySQL的多个几子系统。
先放图一张, 按图索骥开始我们的历险.
当客户端连接上MySQL服务端之后,发出请求之前,服务端的线程是阻塞在do_command(sql/parse.cc)里的my_net_read函数中(就是socket里的read).
当客户端键入sql语句(本文例子select * from zzz)发送到服务端之后, my_net_read返回, 并从tcpbuffer中读取数据写入到packet这个字符串.
<ol class="dp-sql"><li class="alt"><span><span>packet_length= my_net_read(net); </span></span></li></ol>
packet的第一个字节是个标志位, 决定数据包是查询还是命令,成功,或者出错。
接下来就进入dispatch_command(sql/sql/parse.cc)这个函数, 数据类型不再需要.
<ol class="dp-sql"><li class="alt"><span><span>return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1)); </span></span></li></ol>
进入dispatch_command, 我们可见
<ol class="dp-sql"><li class="alt"><span><span>statistic_increment(thd->status_var.questions, &LOCK_status); </span></span></li></ol>
这个就是show status questions的值累加.
接下的mysql_parse(sql/sql_parse.cc), 该函数是sql语句解析的总路口.
进入该函数后首先碰到的是query_cache_send_result_to_client,故名思义这个函数是在QCache里查询是否有相同的语句, 有则立即从QCache返回结果, 于是整个sql就结束了.
QCache里不存在的sql则继续前进来到parse_sql(sql/sql_parse.cc),这个函数主要就是调用了MYSQLparse. 而MYSQLparse其实就是bison/yacc里的yyparse(^_^),
<ol class="dp-sql"><li class="alt"><span><span>#define yyparse MYSQLparse </span></span></li></ol>
是的开始解析sql了. 关于词法分析和语法匹配简单说几下.
对于一条像select * from zzz的语句首先进入词法分析,找到2个token(select, from), 然后根据token进行语法匹配, 规则在sql/yacc.yy里, 我把几个匹配到的pattern和action贴出来.
<ol class="dp-sql"> <li class="alt"><span><span class="keyword">select</span><span>: </span></span></li> <li><span>select_init </span></li> <li class="alt"><span>{ </span></li> <li><span>LEX *lex= Lex; </span></li> <li class="alt"><span>lex->sql_command= SQLCOM_SELECT; </span></li> <li><span>} </span></li> <li class="alt"><span>; </span></li> <li><span>/* Need select_init2 <span class="keyword">for</span><span> subselects. */ </span></span></li> <li class="alt"><span>select_init: </span></li> <li><span>SELECT_SYM select_init2 </span></li> <li class="alt"><span>| <span class="string">'('</span><span> select_paren </span><span class="string">')'</span><span> union_opt </span></span></li> <li><span>; </span></li> <li class="alt"><span>select_paren: </span></li> <li><span>SELECT_SYM select_part2 </span></li> <li class="alt"><span>{ </span></li> <li><span>LEX *lex= Lex; </span></li> <li class="alt"><span>SELECT_LEX * sel= lex->current_select; </span></li> <li><span>..... </span></li> <li class="alt"><span>select_from: </span></li> <li><span><span class="keyword">FROM</span><span> join_table_list where_clause group_clause having_clause </span></span></li> <li class="alt"><span>opt_order_clause opt_limit_clause procedure_clause </span></li> <li><span>{ </span></li> <li class="alt"><span><span class="keyword">Select</span><span>->context.table_list= </span></span></li> <li><span><span class="keyword">Select</span><span>->context.first_name_resolution_table= </span></span></li> <li class="alt"><span>(TABLE_LIST *) <span class="keyword">Select</span><span>->table_list.</span><span class="keyword">first</span><span>; </span></span></li> <li><span>} </span></li> <li class="alt"><span>.... </span></li> <li><span>select_item_list: </span></li> <li class="alt"><span>select_item_list <span class="string">','</span><span> select_item </span></span></li> <li><span>| select_item </span></li> <li class="alt"><span>| <span class="string">'*'</span><span> </span></span></li> <li><span>{ </span></li> <li class="alt"><span>THD *thd= YYTHD; </span></li> <li><span>Item *item= new (thd->mem_root) </span></li> <li class="alt"><span>Item_field(&thd->lex->current_select->context, </span></li> <li><span><span class="op">NULL</span><span>, </span><span class="op">NULL</span><span>, </span><span class="string">"*"</span><span>); </span></span></li> <li class="alt"><span>if (item == <span class="op">NULL</span><span>) </span></span></li> <li><span>MYSQL_YYABORT; </span></li> <li class="alt"><span>if (add_item_to_list(thd, item)) </span></li> <li><span>MYSQL_YYABORT; </span></li> <li class="alt"><span>(thd->lex->current_select->with_wild)++; </span></li> <li><span>} </span></li> <li class="alt"><span>; </span></li> <li><span>select_item: </span></li> <li class="alt"><span>remember_name select_item2 remember_end select_alias </span></li> <li><span>{ </span></li> <li class="alt"><span>THD *thd= YYTHD; </span></li> <li><span>DBUG_ASSERT($1 </span></li> <li class="alt"><span>if (add_item_to_list(thd, $2)) </span></li> <li><span>MYSQL_YYABORT; </span></li> <li class="alt"><span>if ($4.str) </span></li> <li><span>... </span></li> </ol>
可以看到action里最关键的就是add_item_to_list 和table_list的赋值.
解析后对于需要查询的表(zzz)和字段(*)这些信息都写入到thd->lex这个结构体里了.
例如其中thd->lex->query_tables就是表(zzz)的状况, thd->lex->current_select->with_wild 是表示该语句是否使用了*等等.
<ol class="dp-sql"> <li class="alt"><span><span>(gdb) p *thd->lex->query_tables </span></span></li> <li><span>$7 = {next_local = 0x0, next_global = 0x0, prev_global = 0x855a458, db = 0x85a16b8 <span class="string">"test"</span><span>, alias = 0x85a16e0 </span><span class="string">"zzz"</span><span>, </span></span></li> <li class="alt"><span>table_name = 0x85a16c0 <span class="string">"zzz"</span><span>, schema_table_name = 0x0, </span><span class="keyword">option</span><span> = 0x0, on_expr = 0x0, prep_on_expr = 0x0, cond_equal = 0x0, </span></span></li> </ol>
sql解析完了, 优化呢? 别急接着往下看.
接着进入mysql_execute_command函数,这个函数是所有sql命令的总入口.
由于是这个sql是一个select, 于是execute_sqlcom_select就是我们下个要执行的函数,又然后进入了mysql_select(^_^怒了如此复杂).
mysql_select 就是优化器的模块, 这个模块代码比较复杂. 我们可以清楚看到创建优化器,优化,执行的3个步骤, 优化细节不表.
<ol class="dp-sql"> <li class="alt"><span><span>if (!(</span><span class="op">join</span><span>= new </span><span class="op">JOIN</span><span>(thd, fields, select_options, result))) </span></span></li> <li><span>... </span></li> <li class="alt"><span>if ((err= <span class="op">join</span><span>->optimize())) </span></span></li> <li><span>... </span></li> <li class="alt"><span><span class="op">join</span><span>-></span><span class="keyword">exec</span><span>(); </span></span></li> </ol>
结束了优化,我们要具体执行join->exec(),该函数实际进入的是JOIN::exec()(sql_select.cc)。
exec()首先向客户端发送字段title的函数send_fields, 没数据但字段也是要的。
然后再进入do_select(),根据表的存储引擎跳入到引擎具体的实现(zzz是myisam表)。
这里mi_scan就是myisam引擎扫描文件的函数,再看到
<ol class="dp-sql"> <li class="alt"><span><span>(gdb) p info->filename </span></span></li> <li><span>./test/zzz </span></li> </ol>
这不就是zzz表对应的物理文件吗。
通过一系列的mi函数访问磁盘拿到数据之后,会通过send_data发送数据给client,并从dispatch_command返回.最后在net_end_statement结束整个sql。
一个简单的select语句背后的执行过程是非常复杂的,上面的步骤都只是点到就止。
ps: 在sql_yacc.yy可见MySQL对于Oracle中常用的dual表的嘲讽。

ホットAIツール

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

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

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









次の手順でphpmyadminを開くことができます。1。ウェブサイトコントロールパネルにログインします。 2。phpmyadminアイコンを見つけてクリックします。 3。MySQL資格情報を入力します。 4.「ログイン」をクリックします。

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

Redisは、単一のスレッドアーキテクチャを使用して、高性能、シンプルさ、一貫性を提供します。 I/Oマルチプレックス、イベントループ、ノンブロッキングI/O、共有メモリを使用して同時性を向上させますが、並行性の制限、単一の障害、および書き込み集約型のワークロードには適していません。

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

データベースとプログラミングにおけるMySQLの位置は非常に重要です。これは、さまざまなアプリケーションシナリオで広く使用されているオープンソースのリレーショナルデータベース管理システムです。 1)MySQLは、効率的なデータストレージ、組織、および検索機能を提供し、Web、モバイル、およびエンタープライズレベルのシステムをサポートします。 2)クライアントサーバーアーキテクチャを使用し、複数のストレージエンジンとインデックスの最適化をサポートします。 3)基本的な使用には、テーブルの作成とデータの挿入が含まれ、高度な使用法にはマルチテーブル結合と複雑なクエリが含まれます。 4)SQL構文エラーやパフォーマンスの問題などのよくある質問は、説明コマンドとスロークエリログを介してデバッグできます。 5)パフォーマンス最適化方法には、インデックスの合理的な使用、最適化されたクエリ、およびキャッシュの使用が含まれます。ベストプラクティスには、トランザクションと準備された星の使用が含まれます

Apacheはデータベースに接続するには、次の手順が必要です。データベースドライバーをインストールします。 web.xmlファイルを構成して、接続プールを作成します。 JDBCデータソースを作成し、接続設定を指定します。 JDBC APIを使用して、接続の取得、ステートメントの作成、バインディングパラメーター、クエリまたは更新の実行、結果の処理など、Javaコードのデータベースにアクセスします。

phpmyadminを使用してmysqlに接続する方法は? phpmyadminにアクセスするためのURLは、通常、http:// localhost/phpmyadminまたはhttp:// [サーバーIPアドレス]/phpMyAdminです。 MySQLユーザー名とパスワードを入力します。接続するデータベースを選択します。 [接続]ボタンをクリックして、接続を確立します。

MySQLの「ユーザーのアクセス拒否」を解決する方法エラー:1。ユーザーの許可を確認して、データベースに接続します。 2。パスワードをリセットします。 3.リモート接続を許可します。 4。更新権限。 5.データベースサーバーの構成(Bind-Address、Skip-Grant-Tables)を確認します。 6.ファイアウォールルールを確認します。 7.MySQLサービスを再起動します。ヒント:データベースをバックアップした後に変更を加えます。
