最近看各大技術社區,無論是知乎,掘金,博客園,csdn基本上看不到有小夥伴分享sqlserver類的文章,看樣子這些年sqlserver沒落了,已經後繼無人了,再寫sqlserver是不可能再寫了,這輩子都不會寫了,只能靠技術輸出mysql維持生活這樣子。mysql
mysql最大的好處就是開源, 手握百萬源碼,有什麼問題搞不定呢? 這一點要比sqlserver爽多了,不用再dbcc搗來搗去。c++
你們都知道作/裝修房子都要有一張圖紙,其實軟件也是同樣,只要有了這麼一張圖紙,大方向就定下來了,再深刻到細節也不會亂了方向,而後給你們看一下我本身畫的架構圖,畫的不對請輕拍。sql
其實SqlServer,Oracle,MySql架構都大同小異,MySql的鮮明特色就是存儲引擎作成了插拔式,這就牛逼了,現行最經常使用的是InnoDB,這就讓我有了一個想法,有一套業務準備用 InMemory 模式跑一下,厲害了~~~緩存
MySql其實就兩大塊,一塊是MySql Server層,一塊就是Storage Engines層。架構
不一樣語言的sdk遵照mysql協議就能夠與mysqld進行互通。socket
MySql使用C++編寫,Connection是很是寶貴的,在初始化的時候維護一個池。函數
對sql處理,解析,優化,緩存等處理和過濾模塊,瞭解瞭解便可。sqlserver
負責存儲的模塊,官方,第三方,甚至是你本身均可以自定義實現這個數據存儲,這就把生態作起來了,🐮👃。源碼分析
關於怎麼去下載mysql源碼,這裏就不說了,你們本身去官網搗鼓搗鼓哈,本系列使用經典的 mysql 5.7.14
版本。post
手握百萬行源碼,怎麼找入口函數呢??? 😁😁😁,其實很簡單,在mysqld進程上生成一個dump文件,而後看它的託管堆不就好啦。。。
從圖中能夠看到,入口函數就是 mysqld!mysqld_main+0x227
中的 mysqld_main
, 接下來就能夠在源碼中全文檢索下。
extern int mysqld_main(int argc, char **argv); int main(int argc, char **argv) { return mysqld_main(argc, argv); }
這裏你們能夠用visualstudio打開C++源碼,使用查看定義功能,很是好用。
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
回調。
這裏我以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
,也就是請求來了會觸發這個函數,從名字也能夠看出,用一個線程處理一個用戶鏈接。
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); }
//繼續執行 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
提供的接口實現了,不信的話繼續看唄。。。
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
的哈。😁😁😁
這麼多方法,看起來有點懵懵的吧,我來畫一張圖,幫助你們理解下這個調用堆棧。
你們必定要熟讀架構圖,有了架構圖從源碼中找信息就方便多了,總之學習mysql成就感仍是滿滿的😁。