源码 连接mysql_MySql轻松入门系列————第一站 从源码角度轻松认识mysql整体框架图...
一:背景
1. 講故事
最近看各大技術(shù)社區(qū),不管是知乎,掘金,博客園,csdn基本上看不到有小伙伴分享sqlserver類的文章,看樣子這些年sqlserver沒落了,已經(jīng)后繼無人了,再寫sqlserver是不可能再寫了,這輩子都不會寫了,只能靠技術(shù)輸出mysql維持生活這樣子。
二:了解架構(gòu)圖
mysql最大的好處就是開源, 手握百萬源碼,有什么問題搞不定呢? 這一點要比sqlserver爽多了,不用再dbcc搗來搗去。
1. 從架構(gòu)圖入手
大家都知道做/裝修房子都要有一張圖紙,其實軟件也是一樣,只要有了這么一張圖紙,大方向就定下來了,再深入到細節(jié)也不會亂了方向,然后給大家看一下我自己畫的架構(gòu)圖,畫的不對請輕拍。
其實SqlServer,Oracle,MySql架構(gòu)都大同小異,MySql的鮮明特點就是存儲引擎做成了插拔式,這就牛逼了,現(xiàn)行最常用的是InnoDB,這就讓我有了一個想法,有一套業(yè)務(wù)準(zhǔn)備用 InMemory 模式跑一下,厲害了~
2. 功能點介紹
MySql其實就兩大塊,一塊是MySql Server層,一塊就是Storage Engines層。
<1> Client
不同語言的sdk遵守mysql協(xié)議就可以與mysqld進行互通。
<2> Connection/Thread Pool
MySql使用C++編寫,Connection是非常寶貴的,在初始化的時候維護一個池。
<3> SqlInterface,Parse,Optimizer,Cache
對sql處理,解析,優(yōu)化,緩存等處理和過濾模塊,了解了解即可。
<4> Storage Engines
負責(zé)存儲的模塊,官方,第三方,甚至是你自己都可以自定義實現(xiàn)這個數(shù)據(jù)存儲,這就把生態(tài)做起來了, 。
三: 源碼分析
關(guān)于怎么去下載mysql源碼,這里就不說了,大家自己去官網(wǎng)搗鼓搗鼓哈,本系列使用經(jīng)典的 mysql 5.7.14版本。
1. 了解mysql是如何啟動監(jiān)聽的
手握百萬行源碼,怎么找入口函數(shù)呢??? ,其實很簡單,在mysqld進程上生成一個dump文件,然后看它的托管堆不就好啦。。。
從圖中可以看到,入口函數(shù)就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下來就可以在源碼中全文檢索下。
<1> mysqld_main 入口函數(shù) => 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> 創(chuàng)建監(jiān)聽
int mysqld_main(int argc, char **argv) {//創(chuàng)建服務(wù)監(jiān)聽線程handle_connections_sockets(); }void handle_connections_sockets() {//監(jiān)聽連接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);//創(chuàng)建連接create_new_thread(thd); }//創(chuàng)建新線程處理處理用戶連接 static void create_new_thread(THD *thd){thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;//線程進了線程調(diào)度器MYSQL_CALLBACK(thread_scheduler, add_connection, (thd)); }至此mysql就開啟了一個線程對 3306 端口進行監(jiān)控,等待客戶端請求觸發(fā) add_connection 回調(diào)。
2. 理解mysql是如何處理sql請求
這里我以Insert操作為例稍微解剖下處理流程:
當(dāng)用戶有請求sql過來之后,就會觸發(fā) thread_scheduler的回調(diào)函數(shù)add_connection。
static scheduler_functions one_thread_per_connection_scheduler_functions= {0, // max_threadsNULL, // initinit_new_connection_handler_thread, // init_new_connection_threadcreate_thread_to_handle_connection, // add_connectionNULL, // thd_wait_beginNULL, // thd_wait_endNULL, // post_kill_notificationone_thread_per_connection_end, // end_threadNULL, // end };從 scheduler_functions 中可以看到,add_connection 對應(yīng)了 create_thread_to_handle_connection,也就是請求來了會觸發(fā)這個函數(shù),從名字也可以看出,用一個線程處理一個用戶連接。
<1> 客戶端請求被 create_thread_to_handle_connection 接管及調(diào)用棧追蹤
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))){} } //觸發(fā)回調(diào)函數(shù) handle_one_connection pthread_handler_t handle_one_connection(void *arg) {do_handle_one_connection(thd); } //繼續(xù)處理 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 繼續(xù)處理} } //繼續(xù)分發(fā) 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 xxxxmysql_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類型,繼續(xù)追。。。
//繼續(xù)執(zhí)行 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 重點是這個函數(shù)while ((error=table->file->ha_write_row(table->record[0]))){....}} }可以看到,調(diào)用鏈還是挺深的,追到 ha_write_row 方法基本上算是追到頭了,再往下的話就是 MySql Server 給 Storage Engine提供的接口實現(xiàn)了,不信的話繼續(xù)看唄。。。
<3> 繼續(xù)挖 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; }看到?jīng)]有,write_row是個虛方法,也就是給底層方法實現(xiàn)的,在這里就是給各大Storage Engines的哈。
3. 調(diào)用鏈圖
這么多方法,看起來有點懵懵的吧,我來畫一張圖,幫助大家理解下這個調(diào)用堆棧。
三:總結(jié)
大家一定要熟讀架構(gòu)圖,有了架構(gòu)圖從源碼中找信息就方便多了,總之學(xué)習(xí)mysql成就感還是滿滿的 。
總結(jié)
以上是生活随笔為你收集整理的源码 连接mysql_MySql轻松入门系列————第一站 从源码角度轻松认识mysql整体框架图...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 成都大熊猫基地行李寄存费用
- 下一篇: h5 一镜到底_这些一镜到底的H5还能怎