探索MySQL源代码之SQL历险记

Jun 07, 2016 pm 04:05 PM
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表的嘲讽。


このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

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

AI Clothes Remover

AI Clothes Remover

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

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

Video Face Swap

Video Face Swap

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

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

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

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

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

phpmyadminを開く方法 phpmyadminを開く方法 Apr 10, 2025 pm 10:51 PM

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

MySQL:世界で最も人気のあるデータベースの紹介 MySQL:世界で最も人気のあるデータベースの紹介 Apr 12, 2025 am 12:18 AM

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

単一のスレッドレディスの使用方法 単一のスレッドレディスの使用方法 Apr 10, 2025 pm 07:12 PM

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

なぜMySQLを使用するのですか?利点と利点 なぜMySQLを使用するのですか?利点と利点 Apr 12, 2025 am 12:17 AM

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

MySQLの場所:データベースとプログラミング MySQLの場所:データベースとプログラミング Apr 13, 2025 am 12:18 AM

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

Apacheのデータベースに接続する方法 Apacheのデータベースに接続する方法 Apr 13, 2025 pm 01:03 PM

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

phpmyAdmin接続mysql phpmyAdmin接続mysql Apr 10, 2025 pm 10:57 PM

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

MySQLの解決策は、「ユーザーのために拒否されたアクセス」という問題に遭遇します MySQLの解決策は、「ユーザーのために拒否されたアクセス」という問題に遭遇します Apr 11, 2025 pm 05:36 PM

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

See all articles