MySql源码分析
文章目錄
- 1.MySQL源碼
- 1. 主函數(shù)sql/mysqld.cc中,代碼如下:
- 2.監(jiān)聽連接: sql/mysqld.cc - handle_connections_sockets:
- 3. 創(chuàng)建連接 sql/mysqld.cc create_new_thread/create_thread_to_handle_connection:
- 4. 線程調度器thread_scheduler - create_thread_to_handle_connection
- 5.handle_one_connection
- 6.執(zhí)行語句 sql/sql_parse.cc - do_command函數(shù)
- 7.指令分發(fā) sql/sql_parse.cc定義dispatch_command
- 8.sql/sql_parse.cc mysql_parse函數(shù)負責解析SQL
- 9.執(zhí)行命令 mysql_execute_command
- 10.接下來sql/sql_insert.cc中mysql_insert函數(shù)
- 11.接著看真正寫數(shù)據(jù)的函數(shù)write_record (在sql/sql_insert.cc),精簡代碼如下:
- 2.請求數(shù)據(jù)流
源碼才是王道。
1.MySQL源碼
1. 主函數(shù)sql/mysqld.cc中,代碼如下:
//標準入口函數(shù) int main(int argc, char **argv) {//調用mysys/My_init.c->my_init(),初始化mysql內部的系統(tǒng)庫MY_INIT(argv[0]);//初始化日志功能logger.init_base(); //讀取配置文件信息load_defaults(conf_file_name, groups, &argc, &argv);//檢測啟動時的用戶選項 user_info = check_user(mysqld_user);//設置以該用戶運行set_user(mysqld_user, user_info);//初始化內部的一些組件,如table_cache, query_cache等。init_server_components();//初始化網(wǎng)絡模塊,創(chuàng)建socket監(jiān)聽network_init();// 創(chuàng)建pid文件start_signal_handler();// 初始化mysql中的status變量init_status_vars();//創(chuàng)建manager線程start_handle_manager();//主要處理函數(shù),處理新的連接并創(chuàng)建新的線程處理handle_connections_sockets(); }2.監(jiān)聽連接: sql/mysqld.cc - handle_connections_sockets:
pthread_handler_t handle_connections_sockets(void *arg __attribute__((unused))) {// unix_socket在network_init中被打開FD_SET(unix_sock,&clientFDs); // abort_loop是全局變量,在某些情況下被置為1表示要退出。while (!abort_loop) { // 需要監(jiān)聽的socketreadFDs=clientFDs; // select異步監(jiān)聽select((int) max_used_connection,&readFDs,0,0,0); // 接受請求new_sock = accept(sock, my_reinterpret_cast(struct sockaddr *)(&cAddr),&length);// 創(chuàng)建mysqld任務線程描述符,它封裝了一個客戶端連接請求的所有信息thd= new THD; // 網(wǎng)絡操作抽象層vio_tmp=vio_new(new_sock, VIO_TYPE_SOCKET, VIO_LOCALHOST); // 初始化任務線程描述符的網(wǎng)絡操作my_net_init(&thd->net,vio_tmp)); // 創(chuàng)建任務線程create_new_thread(thd); } }3. 創(chuàng)建連接 sql/mysqld.cc create_new_thread/create_thread_to_handle_connection:
static void create_new_thread(THD *thd) {NET *net=&thd->net;// 看看當前連接數(shù)是不是超過了系統(tǒng)配置允許的最大值,如果是就斷開連接。if (connection_count >= max_connections + 1 || abort_loop) { close_connection(thd, ER_CON_COUNT_ERROR, 1);delete thd;}++connection_count;// 將新連接加入到thread_scheduler的連接隊列中。thread_scheduler.add_connection(thd); }4. 線程調度器thread_scheduler - create_thread_to_handle_connection
void create_thread_to_handle_connection(THD *thd) {//看當前工作線程緩存(thread_cache)中有否空余的線程if (cached_thread_count > wake_thread) { thread_cache.append(thd);// 有的話則喚醒一個線程來用pthread_cond_signal(&COND_thread_cache);} else {threads.append(thd);//沒有可用空閑線程則創(chuàng)建一個新的線程pthread_create(&thd->real_id,&connection_attrib, handle_one_connection, (void*) thd))); } }5.handle_one_connection
pthread_handler_t handle_one_connection(void *arg) {// 初始化線程預處理操作thread_scheduler.init_new_connection_thread(); //載入一些Session級變量setup_connection_thread_globals(thd); for (;;) { //初始化LEX詞法解析器lex_start(thd); // 進行連接身份驗證login_connection(thd); // 初始化線程Status,即show status看到的prepare_new_connection_state(thd); // 處理命令do_command(thd); //沒事做了關閉連接,丟入線程池end_connection(thd); } }6.執(zhí)行語句 sql/sql_parse.cc - do_command函數(shù)
bool do_command(THD *thd) {NET *net= &thd->net;//讀取客戶端發(fā)送的報文packet_length = my_net_read(net);packet = (char*) net->read_pos;// 解析客戶端傳過來的命令類型command = (enum enum_server_command) (uchar) packet[0]; // 根據(jù)命令類型進行分發(fā)dispatch_command(command, thd, packet+1, (uint) (packet_length-1)); }7.指令分發(fā) sql/sql_parse.cc定義dispatch_command
bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length) {NET *net = &thd->net;thd->command = command; switch (command) { //判斷命令類型case COM_INIT_DB: ...;case COM_TABLE_DUMP: ...;case COM_CHANGE_USER: ...;...case COM_QUERY: //如果是Queryalloc_query(thd, packet, packet_length); //從網(wǎng)絡數(shù)據(jù)包中讀取Query并存入thd->querymysql_parse(thd, thd->query, thd->query_length, &end_of_stmt); //送去解析} }8.sql/sql_parse.cc mysql_parse函數(shù)負責解析SQL
void mysql_parse(THD *thd, const char *inBuf, uint length, const char ** found_semicolon) {//初始化線程解析描述符lex_start(thd); // 看query cache中有否命中,有就直接返回結果,否則進行查找if (query_cache_send_result_to_client(thd, (char*) inBuf, length) <= 0) { Parser_state parser_state(thd, inBuf, length); // 解析SQL語句 parse_sql(thd, & parser_state, NULL); // 執(zhí)行語句mysql_execute_command(thd); } }9.執(zhí)行命令 mysql_execute_command
int mysql_execute_command(THD *thd) {// 解析過后的SQL語句的語法結構LEX *lex= thd->lex; // 該語句要訪問的表的列表TABLE_LIST *all_tables = lex->query_tables; switch (lex->sql_command) {...case SQLCOM_INSERT://insert語句insert_precheck(thd, all_tables);mysql_insert(thd, all_tables, lex->field_list, lex->many_values, lex->update_list, lex->value_list, lex->duplicates, lex->ignore);break; ... case SQLCOM_SELECT: // select 語句// 檢查用戶對數(shù)據(jù)表的訪問權限check_table_access(thd, lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL, all_tables, UINT_MAX, FALSE); // 執(zhí)行select語句execute_sqlcom_select(thd, all_tables); break;} }10.接下來sql/sql_insert.cc中mysql_insert函數(shù)
bool mysql_insert(THD *thd,TABLE_LIST *table_list, // 該INSERT要用到的表List<Item> &fields, // 使用的項....) {// 這里的鎖只是防止表結構修改open_and_lock_tables(thd, table_list); mysql_prepare_insert(...);//里面還有trigger,錯誤,view之類的雜七雜八的東西,我們都忽略foreach value in values_list {write_record(...);} }11.接著看真正寫數(shù)據(jù)的函數(shù)write_record (在sql/sql_insert.cc),精簡代碼如下:
int write_record(THD *thd, TABLE *table,COPY_INFO *info) { // 寫數(shù)據(jù)記錄//如果是REPLACE或UPDATE則替換數(shù)據(jù)if (info->handle_duplicates == DUP_REPLACE || info->handle_duplicates == DUP_UPDATE) { table->file->ha_write_row(table->record[0]);table->file->ha_update_row(table->record[1], table->record[0]));} else {table->file->ha_write_row(table->record[0]);} } //存儲引擎抽象的Handler API ! int handler::ha_write_row(uchar *buf) { write_row(buf); // 調用具體的實現(xiàn)binlog_log_row(table, 0, buf, log_func)); // 寫binlog }2.請求數(shù)據(jù)流
總結
- 上一篇: ArcGIS中利用DEM制作山体阴影立体
- 下一篇: ArcGIS教程 - 5 地图可视化