MySql輕鬆入門系列————第一站 從原始碼角度輕鬆認識mysql整體
一:背景
1. 講故事
最近看各大技術社群,不管是知乎,掘金,部落格園,csdn基本上看不到有小夥伴分享sqlserver類的文章,看樣子這些年sqlserver沒落了,已經後繼無人了,再寫sqlserver是不可能再寫了,這輩子都不會寫了,只能靠技術輸出mysql維持生活這樣子。
二:瞭解架構圖
mysql最大的好處就是開源, 手握百萬原始碼,有什麼問題搞不定呢? 這一點要比sqlserver爽多了,不用再dbcc搗來搗去。
1. 從架構圖入手
大家都知道做/裝修房子都要有一張圖紙,其實軟體也是一樣,只要有了這麼一張圖紙,大方向就定下來了,再深入到細節也不會亂了方向,然後給大家看一下我自己畫的架構圖,畫的不對請輕拍。
其實SqlServer,Oracle,MySql架構都大同小異,MySql的鮮明特點就是儲存引擎做成了插拔式,這就牛逼了,現行最常用的是InnoDB,這就讓我有了一個想法,有一套業務準備用 InMemory 模式跑一下,厲害了~~~
2. 功能點介紹
MySql其實就兩大塊,一塊是MySql Server層,一塊就是Storage Engines層。
<1> Client
不同語言的sdk遵守mysql協議就可以與mysqld進行互通。
<2> Connection/Thread Pool
MySql使用C++編寫,Connection是非常寶貴的,在初始化的時候維護一個池。
<3> SqlInterface,Parse,Optimizer,Cache
對sql處理,解析,最佳化,快取等處理和過濾模組,瞭解瞭解即可。
<4> Storage Engines
負責儲存的模組,官方,第三方,甚至是你自己都可以自定義實現這個資料儲存,這就把生態做起來了,????????。
三: 原始碼分析
關於怎麼去下載mysql原始碼,這裡就不說了,大家自己去官網搗鼓搗鼓哈,本系列使用經典的 mysql 5.7.14
版本。
1. 瞭解mysql是如何啟動監聽的
手握百萬行原始碼,怎麼找入口函式呢??? ????????????,其實很簡單,在mysqld程式上生成一個dump檔案,然後看它的託管堆不就好啦。。。
從圖中可以看到,入口函式就是 mysqld!mysqld_main+0x227
中的 mysqld_main
, 接下來就可以在原始碼中全文檢索下。
<1> mysqld_main 入口函式 => sql/main.cc
extern int mysqld_main(int argc, char **argv);
int main(int argc, char **argv)
{
return mysqld_main(argc, argv);
}
這裡大家可以用visualstudio開啟C++原始碼,使用檢視定義功能,非常好用。
<2> 建立監聽
int mysqld_main(int argc, char **argv)
{
//建立服務監聽執行緒
handle_connections_sockets();
}
void handle_connections_sockets()
{
//監聽連線
new_sock= mysql_socket_accept(key_socket_client_connection, sock,
(struct sockaddr *)(&cAddr), &length);
if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
thd->security_ctx->set_host((char*) my_localhost);
//建立連線
create_new_thread(thd);
}
//建立新執行緒處理處理使用者連線
static void create_new_thread(THD *thd){
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
//執行緒進了執行緒排程器
MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));
}
至此mysql就開啟了一個執行緒對 3306
埠進行監控,等待客戶端請求觸發 add_connection
回撥。
2. 理解mysql是如何處理sql請求
這裡我以Insert操作為例稍微解剖下處理流程:
當使用者有請求sql過來之後,就會觸發 thread_scheduler
的回撥函式add_connection
。
static scheduler_functions one_thread_per_connection_scheduler_functions=
{
0, // max_threads
NULL, // init
init_new_connection_handler_thread, // init_new_connection_thread
create_thread_to_handle_connection, // add_connection
NULL, // thd_wait_begin
NULL, // thd_wait_end
NULL, // post_kill_notification
one_thread_per_connection_end, // end_thread
NULL, // end
};
從 scheduler_functions
中可以看到,add_connection 對應了 create_thread_to_handle_connection
,也就是請求來了會觸發這個函式,從名字也可以看出,用一個執行緒處理一個使用者連線。
<1> 客戶端請求被 create_thread_to_handle_connection 接管及呼叫棧追蹤
void create_thread_to_handle_connection(THD *thd)
{
if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
handle_one_connection,(void*) thd))){}
}
//觸發回撥函式 handle_one_connection
pthread_handler_t handle_one_connection(void *arg)
{
do_handle_one_connection(thd);
}
//繼續處理
void do_handle_one_connection(THD *thd_arg){
while (thd_is_connection_alive(thd))
{
mysql_audit_release(thd);
if (do_command(thd)) break; //這裡的 do_command 繼續處理
}
}
//繼續分發
bool do_command(THD *thd)
{
return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
}
bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
{
switch (command) {
case COM_INIT_DB: .... break;
...
case COM_QUERY: //查詢語句: insert xxxx
mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析
break;
}
}
//sql解析模組
void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
{
error= mysql_execute_command(thd);
}
<2> 到這裡它的Parse,Optimizer,Cache都追完了,接下來看sql的CURD型別,繼續追。。。
//繼續執行
int mysql_execute_command(THD *thd)
{
switch (lex->sql_command)
{
case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;
//這個 insert 就是我要追的
case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
lex->update_list, lex->value_list,
lex->duplicates, lex->ignore);
}
}
//insert插入操作處理
bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
List<Item> &update_fields, List<Item> &update_values,
enum_duplicates duplic, bool ignore)
{
while ((values= its++))
{
error= write_record(thd, table, &info, &update);
}
}
//寫入記錄
int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
{
if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
{
// ha_write_row 重點是這個函式
while ((error=table->file->ha_write_row(table->record[0])))
{
....
}
}
}
可以看到,呼叫鏈還是挺深的,追到 ha_write_row
方法基本上算是追到頭了,再往下的話就是 MySql Server
給 Storage Engine
提供的介面實現了,不信的話繼續看唄。。。
<3> 繼續挖 ha_write_row
int handler::ha_write_row(uchar *buf)
{
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
}
//這是一個虛方法
virtual int write_row(uchar *buf __attribute__((unused)))
{
return HA_ERR_WRONG_COMMAND;
}
看到沒有,write_row
是個虛方法,也就是給底層方法實現的,在這裡就是給各大Storage Engines
的哈。????????????
3. 呼叫鏈圖
這麼多方法,看起來有點懵懵的吧,我來畫一張圖,幫助大家理解下這個呼叫堆疊。
三:總結
大家一定要熟讀架構圖,有了架構圖從原始碼中找資訊就方便多了,總之學習mysql成就感還是滿滿的????。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/756/viewspace-2825588/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- MySql輕鬆入門系列——第一站 從原始碼角度輕鬆認識mysql整體框架圖MySql原始碼框架
- MySql輕鬆入門系列——第二站 使用visual studio 對mysql進行原始碼級除錯MySql原始碼除錯
- MySQL8.0輕鬆搞定GTID主從複製MySql
- Flask入門很輕鬆 (一)Flask
- Flask入門很輕鬆(三)—— 模板Flask
- Webpack輕鬆入門(四)——HTML打包WebHTML
- 輕輕鬆鬆帶你入門Android Jetpack(含Jetpack Compose),容易肝不難!AndroidJetpack
- 04selenium爬蟲輕鬆入門爬蟲
- Webpack輕鬆入門(三)——圖片打包Web
- MySQL8.0輕鬆搞定GTID組複製MySql
- netty系列之:輕輕鬆鬆搭個支援中文的伺服器Netty伺服器
- 監控神器:Prometheus 輕鬆入門,真香!(上篇)Prometheus
- 監控神器:Prometheus 輕鬆入門,真香!(下篇)Prometheus
- SQL輕鬆入門(5):視窗函式SQL函式
- Apache Doris 輕鬆入門和快速實踐Apache
- substrate輕鬆學系列1:前言
- MySQL8.0輕鬆搞定GTID主主複製MySql
- 神器!使用Python 輕鬆識別驗證碼Python
- 4個角度輕鬆理解 Flink中的Watermark
- 輕鬆學會原始碼編譯Vim 8.0原始碼編譯
- 頭腦王者,輕輕鬆鬆上王者,憑自己的Python知識上王者Python
- Arduino :入門教學讓你輕鬆玩轉UI
- mysql 的這個痛點,用 elasticsearch 輕鬆解決MySqlElasticsearch
- 輕鬆看懂Java位元組碼Java
- 軟體工程入門-輕鬆理解依賴注入 (DI) 和 IoC 容器軟體工程依賴注入
- Python實戰案例彙總,帶你輕鬆從入門到實戰Python
- Apache Kafka安裝和使用(入門教程輕鬆學)ApacheKafka
- 輕鬆整合系列三:如何在 KubeBlocks 中配置引數模板|以 Oracle MySQL 為例BloCOracleMySql
- 快速修改網站原始碼,輕鬆掌握網站原始碼修改方法網站原始碼
- [Python教程]0基礎不用怕,從0到1輕鬆教你入門PythonPython
- kettle從入門到精通 第六十九課 ETL之kettle kettle cdc mysql,輕鬆實現實時增量同步MySql
- substrate輕鬆學系列5:編寫pallet的Rust前置知識Rust
- 解鎖 VS Code 更多可能性,輕鬆入門 WebViewWebView
- 輕鬆理解 Spring AOPSpring
- Python入門塔防小遊戲,開發步驟和原始碼,帶你輕鬆學pythonPython遊戲原始碼
- substrate輕鬆學系列4:substrate快速瞭解
- 雲脈文件雲識別APP:輕鬆識別潦草手寫體APP
- 大話RxJava:二、輕鬆學原始碼之基礎篇RxJava原始碼