mysql in优化_MySQL 探秘: 1 整体架构
新開坑,計劃做一系列專輯。由于 MySQL 源碼太龐大,不可能面面俱到,先從丁奇《MySQL 實戰 45 講》[1] 案例開始入手,case by case 來做分享。同時強烈推薦丁奇的課,真的是受益匪淺,感謝感謝~~
最新版本己經是 MySQL 8.0 了,和我當初使用 5.5 差距非常大,增加很多實用功能也做了很多優化。對于我個人來講,很陌生,也是重新學習的過程。
案例
本次分享一個 SQL 是如何執行的,先看這個案例,如果訪問不存在的字段,那么報錯是在 MySQL 哪個層呢?
select?a,b?from?mytest.test?where?k?=1;另外剛畢業時面試百度,問我 mvcc 是在哪一層的,直接就蒙逼了:(
整體架構
mysql arch
提到 MySQL 架構這個圖是必看的,為了支持不同的存儲引擎,分為兩層:Server 層和存儲引擎層。做過 DBA 的都知道,redo, undo log 這些是事務相關的,都屬于引擎層,具體就是 innodb 的,而 binlog 是所有引擎通用的,所以在 Server 層。目前 MySQL 大部分公司只會用過 innodb, 并且也是默認引擎。
tidb arch
同時我們也可以看下最近幾年新興的 TiDB[2] , 整體分層結構并無太大區別,但是因為底層引擎是分布式的 TiKV[3], 所以生成執行計劃與執行器執行還是有很大區別的,并且執行時還要考濾 rpc 超時等等。
Server
Server 層具體包括連接器,查詢緩存,分析器,優化器與執行器。具體看一下每個模塊的作用,后面會用 debug 方式查看
1. 連接器
負責處理新建連接,用戶名密碼認證,接收發送請求等功能。在 thread_handling 默認是 one-thread-per-connection 一個新建連接會新啟動一個線程。
root@myali:~/mysql#?ps?-T?-p?10477?|?wc?-l28
root@myali:~/mysql#?bin/mysql?-uroot?-h127.0.0.1
mysql>?show?full?processlist;
+----+------+-----------------+------+---------+------+----------+-----------------------+
|?Id?|?User?|?Host????????????|?db???|?Command?|?Time?|?State????|?Info??????????????????|
+----+------+-----------------+------+---------+------+----------+-----------------------+
|??2?|?root?|?localhost:58506?|?NULL?|?Query???|????0?|?starting?|?show?full?processlist?|
+----+------+-----------------+------+---------+------+----------+-----------------------+
1?row?in?set?(0.00?sec)
mysql>?exit
Bye
root@myali:~/mysql#?ps?-T?-p?10477?|?wc?-l
29
可以看到線程個數隨之變化,當然 MySQL 這么做肯定低效,所以開啟線程池或是線程復用 thread_cache_size, 為了測試我把這個參數關掉了。
以前在 ganji 的時候,業務 php 經常會有連接失敗的情況,是個不錯的分析 case 啊:(
2. 查詢緩存
很古老的東西了,最新版本也己廢棄,原因在于效率太低,Query_cache 屬于表級別的,任何更新都會使之失效。
3. 分析器
以前在 ganji 做 SQL 自動上線[4] 時就用到了這塊技術,使用 lex&yacc 技術將一條 sql 解析成 ast 抽象語法樹,然后對之進行 sql 審核后再上線。基本大公司的上線平臺也是同樣套路。
至于 MySQL 這塊也差不多,將 sql 識別成 MySQL 認可的語法,然后交給下一層去執行。據說 TiDB 的 sql parser 實現效率最高,很多 go 的庫也都在使用,以后有機會我也試用下。
4. 優化器
經過 parser 后的 SQL,就知道要獲取什么樣的數據了,但是我們知道 SQL 是一種聲明式語言,不會指導數據庫如何去獲取數據,那么在執行器執行前,就要經過優化器去分析一波,選擇哪個索引合適。這一塊 MySQL 和 TiDB 區別還是很大的,以后有機會再細看。
5. 執行器
MySQL 通過分析器知道 client 要做什么,通過優化器知道 MySQL 該怎么做,于是就進入了執行器階段,開始執行語句。
這一層會對用戶是否有查詢權限進行校驗,如果沒有返回報錯。有的話調用 Innodb 引擎層接口獲取數據,循環獲取滿足條件的行數據。
案例
回到文章開頭的案例,如果字段不存在肯定報錯,這個屬于哪層呢?
cmake?.?-DCMAKE_INSTALL_PREFIX=/root/my-mysql8?\-DMYSQL_DATADIR=/root/my-mysql8/data?\
-DWITH_BOOST=/root/my-mysql8/boost/boost_1_70_0?\
-DSYSCONFDIR=/etc?\
-DEFAULT_CHARSET=utf8mb4?\
-DDEFAULT_COLLATION=utf8mb4_general_ci?\
-DENABLED_LOCAL_INFILE=1?\
-DWITH_DEBUG=1?\
-DSYSCONFDIR=/root/my-mysql8/etc?\
-DEXTRA_CHARSETS=all
先編譯 MySQL,記住一定要加 WITH_DEBUG 選項
nohup?sh?mysql.server?start?--debug??&然后啟動并打開調式模式。
bin/mysql?-uroot?-h127.0.0.1?-e?"select?*?from?mytest.test?where?a=1"trace 文件在 /tmp/mysqld.trace, 我們直接截取 sql 執行的報錯部分展示出來
T@3:?|?|?|?|?>handle_queryT@3:?|?|?|?|?|?THD::enter_stage:?'init'?/root/mysql-5.7.23/sql/sql_select.cc:121
T@3:?|?|?|?|?|?>PROFILING::status_change
T@3:?|?|?|?|?|?<:status_change>T@3:?|?|?|?|?|?>SELECT_LEX::prepare
T@3:?|?|?|?|?|?|?opt:?(null):?starting?struct
T@3:?|?|?|?|?|?|?opt:?join_preparation:?starting?struct
T@3:?|?|?|?|?|?|?opt:?select#:?1
T@3:?|?|?|?|?|?|?>SELECT_LEX::setup_conds
T@3:?|?|?|?|?|?|?|?info:?thd->mark_used_columns:?1
T@3:?|?|?|?|?|?|?|?>find_field_in_table_ref
T@3:?|?|?|?|?|?|?|?|?enter:?table:?'test'??field?name:?'k'??item?name:?'k'??ref?0x7fd620007aa8
T@3:?|?|?|?|?|?|?|?|?>find_field_in_table
T@3:?|?|?|?|?|?|?|?|?|?enter:?table:?'test',?field?name:?'k'
T@3:?|?|?|?|?|?|?|?|?T@3:?|?|?|?|?|?|?|?T@3:?|?|?|?|?|?|?|?>find_field_in_table_ref
T@3:?|?|?|?|?|?|?|?|?enter:?table:?'test'??field?name:?'k'??item?name:?'k'??ref?0x7fd620007aa8
T@3:?|?|?|?|?|?|?|?|?>find_field_in_table
T@3:?|?|?|?|?|?|?|?|?|?enter:?table:?'test',?field?name:?'k'
T@3:?|?|?|?|?|?|?|?|?T@3:?|?|?|?|?|?|?|?T@3:?|?|?|?|?|?|?|?>check_grant_column
T@3:?|?|?|?|?|?|?|?|?enter:?table:?test??want_privilege:?1
T@3:?|?|?|?|?|?|?|?T@3:?|?|?|?|?|?|?|?>my_error
T@3:?|?|?|?|?|?|?|?|?my:?nr:?1054??MyFlags:?0??errno:?2
T@3:?|?|?|?|?|?|?|?|?>my_message_sql
T@3:?|?|?|?|?|?|?|?|?|?error:?error:?1054??message:?'Unknown?column?'k'?in?'where?clause''
T@3:?|?|?|?|?|?|?|?|?|?>mysql_audit_acquire_plugins
T@3:?|?|?|?|?|?|?|?|?|?
可以看到字段不存在的報錯是在 mysql_parse 之后,在 prepare 函數里做的檢查,這一塊屬于 optimize 優化器層前的預處理
另外非常有意思的是,字段不存在的報錯竟然是在檢查權限 check_grant_column 函數中。感興趣的可以自行 debug 看下 trace 文件,可以幫助快速定位源碼與流程。
select 完整流程
通過 trace 文件追蹤了 mysql 源碼,大致清楚了整體流程。比較關鍵的數據結構是 THD, 每個連接一個實例。這個結構光定義就 3300 行代碼,還不包括方法實現... 囧:(
小結
這次分享參考了《MySQL 實戰 45 講》[1] 第一節,評論的內容也更精彩,推薦大家圍觀。以后面還會分享更多關于 MySQL 的內容,如果感興趣,可以關注并轉發(:
參考資料
[1]《MySQL 實戰 45 講》: https://time.geekbang.org/column/intro/139,
[2]tidb 源碼分析: https://pingcap.com/blog-cn/tidb-source-code-reading-2/,
[3]tikv 是什么: https://pingcap.com/blog-cn/#TiKV,
[4]sql 自動上線: https://myslide.cn/slides/9070,
總結
以上是生活随笔為你收集整理的mysql in优化_MySQL 探秘: 1 整体架构的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 指定的文件不是虚拟磁盘 没有快照_vmw
- 下一篇: hql取满足条件最新一条记录_MySql