主要模塊及數據流
經過多年的發展,mysql的主要模塊已經穩定,基本不會有大的修改。本文將對MySQL的整體架構及重要目錄進行講述。
- 源碼結構(MySQL-5.5.0-m2)
- BUILD: 內含在各個平台、各種編譯器下進行編譯的腳本。如compile-pentium-debug表示在pentium架構上進行編譯的腳本。
- Client: 客戶端工具,如mysql, mysqladmin之類。
- Cmd-line-utils: readline, libedit工具。
- Config: 給aclocal使用的配置文件。
- Dbug: 提供一些調試用的宏定義。
- Extra: 提供innochecksum,resolveip等額外的小工具。
- Include: 包含的頭文件
- Libmysql: 庫文件,生產libmysqlclient.so。
- Libmysql_r: 線程安全的庫文件,生成libmysqlclient_r.so。
- Libservices: 5.5.0中新加的目錄,實現了打印功能。
- Man: 手冊頁。
- Mysql-test: mysqld的測試工具一套。
- Mysys: 為跨平台計,MySQL自己實現了一套常用的數據結構和算法,如string, hash等。
- Netware: 在netware平台上進行編譯時需要的工具和庫。
- Plugin: mysql以插件形式實現的部分功能。
- Pstack: 異步棧追蹤工具。
- Regex: 正則表達式工具。
- Scripts: 提供腳本工具,如mysql_install_db等
- Sql: mysql主要代碼,將會生成mysqld文件。
- Sql-bench: 一些評測代碼。
- Sql-common: 存放部分服務器端和客戶端都會用到的代碼。
- Storage: 存儲引擎所在目錄,如myisam, innodb, ndb等。
- Strings: string庫。
- Support-files: my.cnf示例配置文件。
- Tests: 測試文件所在目錄。
- Unittest: 單元測試。
- Vio: virtual io系統,是對network io的封裝。
- Win: 給windows平台提供的編譯環境。
- Zip: zip庫工具
- 主要數據結構
- THD 線程描述符(sql/sql_class.h)
-
包含處理用戶請求時需要的相關數據,每個連接會有一個線程來處理,在一些高層函數中,此數據結構常被當作第一個參數傳遞。
NET net; // 客戶連接描述符
Protocol *protocol; // 當前的協議
Protocol_text protocol_text; // 普通協議
Protocol_binary protocol_binary; // 二進制協議
HASH user_vars; //用戶變量的hash值
String packet; // 網絡IO時所用的緩存
String convert_buffer; // 字符集轉換所用的緩存
struct sockaddr_in remote; //客戶端socket地址THR_LOCK_INFO lock_info; // 當前線程的鎖信息
THR_LOCK_OWNER main_lock_id; // 在舊版的查詢中使用
THR_LOCK_OWNER *lock_id; //若非main_lock_id, 指向游標的lock_id
pthread_mutex_t LOCK_thd_data; //thd的mutex鎖,保護THD數據(thd->query, thd->query_length)不會被其余線程訪問到。Statement_map stmt_map; //prepared statements和stored routines 會被重復利用
int insert(THD *thd, Statement *statement); // statement的hash容器
class Statement::
LEX_STRING name;
LEX *lex; //語法樹描述符
bool set_db(const char *new_db, size_t new_db_len)
void set_query(char *query_arg, uint32 query_length_arg);
{
pthread_mutex_lock(&LOCK_thd_data);
set_query_inner(query_arg, query_length_arg);
pthread_mutex_unlock(&LOCK_thd_data);
} - NET 網絡連接描述符(sql/mysql_com.h)
-
網絡連接描述符,對內部數據包進行了封裝,是client和server之間的通信協議。
Vio *vio; //底層的網絡I/O socket描述符
unsigned char *buff,*buff_end,*write_pos,*read_pos; //緩存相關
unsigned long remain_in_buf,length, buf_length, where_b;
unsigned long max_packet,max_packet_size; //當前值;最大值
unsigned int pkt_nr,compress_pkt_nr; //當前(未)壓縮包的順序值
my_bool compress; //是否壓縮
unsigned int write_timeout, read_timeout, retry_count; //最大等待時間
unsigned int *return_status; //thd中的服務器狀態
unsigned char reading_or_writing;
unsigned int last_errno; //返回給客戶端的錯誤號
unsigned char error; - TABLE 數據庫表描述符(sql/table.h)
-
數據庫表描述符,分成TABLE和TABLE_SHARE兩部分。
handler *file; //指向這張表在storage engine中的handler的指針
THD *in_use;
Field **field;
uchar *record[2];
uchar *write_row_record;
uchar *insert_values;
key_map covering_keys;
key_map quick_keys, merge_keys;
key_map keys_in_use_for_query;
key_map keys_in_use_for_group_by;
key_map keys_in_use_for_order_by;
KEY *key_info;HASH name_hash; //數據域名字的hash值
MEM_ROOT mem_root; //內存塊
LEX_STRING db;
LEX_STRING table_name;
LEX_STRING table_cache_key;
enum db_type db_type //當前表的storage engine類型
enum row_type row_type //當前記錄是定長還是變長
uint primary_key;
uint next_number_index; //自動增長key的值
bool is_view ;
bool crashed; - FIELD 字段描述符(sql/field.h)
-
域描述符,是各種字段的抽象基類。
uchar *ptr; // 記錄中數據域的位置
uchar *null_ptr; // 記錄 null_bit 位置的byte
TABLE *table; // 指向表的指針
TABLE *orig_table; // 指向原表的指針
const char **table_name, *field_name;
LEX_STRING comment;
key_map key_start, part_of_key, part_of_key_not_clustered;
key_map part_of_sortkey;
enum utype { NONE,DATE,SHIELD,NOEMPTY,CASEUP,PNR,BGNR,PGNR,YES,NO,REL,
CHECK,EMPTY,UNKNOWN_FIELD,CASEDN,NEXT_NUMBER,INTERVAL_FIELD,
BIT_FIELD, TIMESTAMP_OLD_FIELD, CAPITALIZE, BLOB_FIELD,
TIMESTAMP_DN_FIELD, TIMESTAMP_UN_FIELD, TIMESTAMP_DNUN_FIELD};
…..
virtual int store(const char *to, uint length,CHARSET_INFO *cs)=0;
inline String *val_str(String *str) { return val_str(str, str); } - Utility API Calls 各種API
-
各種核心的工具,例如內存分配,字符串操作或文件管理。標准C庫中的函數只使用了很少一部分,C++中的函數基本沒用。
void *my_malloc(size_t size, myf my_flags) //對malloc的封裝
size_t my_write(File Filedes, const uchar *Buffer, size_t Count, myf MyFlags) //對write的封裝 - Preprocessor Macros 處理器宏
-
Mysql中使用了大量的C預編譯,隨編譯參數的不同最終代碼也不同。
#define max(a, b) ((a) > (b) ? (a) : (b)) //得出兩數中的大者
do
{
char compile_time_assert[(X) ? 1 : -1]
__attribute__ ((unused));
} while(0)
使用gcc的attribute屬性指導編譯器行為
• Global variables 全局變量
• configuration settings
• server status information
• various data structures shared among threads
主要包括一些全局的設置,服務器信息和部分線程間共享的數據結構。struct system_status_var global_status_var; //全局的狀態信息
struct system_variables global_system_variables; //全局系統變量
- 主要調用流程
- MySQL啟動
-
主要代碼在sql/mysqld.cc中,精簡后的代碼如下:
int main(int argc, char **argv) //標准入口函數
MY_INIT(argv[0]); //調用mysys/My_init.c->my_init(),初始化mysql內部的系統庫
logger.init_base(); //初始化日志功能
init_common_variables(MYSQL_CONFIG_NAME,argc, argv, load_default_groups) //調用load_defaults(conf_file_name, groups, &argc, &argv),讀取配置信息
user_info= check_user(mysqld_user);//檢測啟動時的用戶選項
set_user(mysqld_user, user_info);//設置以該用戶運行
init_server_components();//初始化內部的一些組件,如table_cache, query_cache等。
network_init();//初始化網絡模塊,創建socket監聽
start_signal_handler();// 創建pid文件
mysql_rm_tmp_tables() || acl_init(opt_noacl)//刪除tmp_table並初始化數據庫級別的權限。
init_status_vars(); // 初始化mysql中的status變量
start_handle_manager();//創建manager線程
handle_connections_sockets();//主要處理函數,處理新的連接並創建新的線程處理之 - 監聽接收鏈接
-
主要代碼在sql/mysqld.cc中,精簡后的代碼如下:
THD *thd;
FD_SET(ip_sock,&clientFDs); //客戶端socket
while (!abort_loop)
readFDs=clientFDs;
if (select((int) max_used_connection,&readFDs,0,0,0) error && net->vio != 0 &&
!(thd->killed == THD::KILL_CONNECTION))
{
if(do_command(thd)) //處理客戶端發出的命令
break;
}
end_connection(thd);
} - 預處理連接
-
thread_count++;//增加當前連接的線程
thread_scheduler.add_connection(thd);
for (;;) {
lex_start(thd);
login_connection(thd); // 認證
prepare_new_connection_state(thd); //初始化thd描述符
while(!net->error && net->vio != 0 &&
!(thd->killed == THD::KILL_CONNECTION))
{
if(do_command(thd)) //處理客戶端發出的命令
break;
}
end_connection(thd);
} - 處理
-
do_command在sql/sql_parse.cc中:讀取客戶端傳遞的命令並分發。
NET *net= &thd->net;
packet_length= my_net_read(net);
packet= (char*) net->read_pos;
command= (enum enum_server_command) (uchar) packet[0]; //從net結構中獲取命令
dispatch_command(command, thd, packet+1, (uint) (packet_length-1));//分發命令
在dispatch_command函數中,根據命令的類型進行分發。
thd->command=command;
switch( command ) {
case COM_INIT_DB: ...;
case COM_TABLE_DUMP: ...;
case COM_CHANGE_USER: ...;
….
case COM_QUERY: //如果是查詢語句
{
alloc_query(thd, packet, packet_length)//thd->set_query(query, packet_length);
mysql_parse(thd, thd->query(), thd->query_length(), &end_of_stmt);
// 解析查詢語句
….
}
在mysql_parse函數中,lex_start(thd);
if (query_cache_send_result_to_client(thd, (char*) inBuf, length) sql_command
在mysql_execute_command中,根據命令類型,轉到相應的執行函數。switch (lex->sql_command) {
LEX *lex= thd->lex;
TABLE_LIST *all_tables;
case SQLCOM_SELECT:
check_table_access(thd, lex->exchange ? SELECT_ACL | FILE_ACL : SELECT_ACL, all_tables, UINT_MAX, FALSE); //檢查用戶權限
execute_sqlcom_select(thd, all_tables); //執行select命令
break;case SQLCOM_INSERT:
{ res= insert_precheck(thd, all_tables) //rights
mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
lex->update_list, lex->value_list,
lex->duplicates, lex->ignore);
break;
在execute_sqlcom_select函數中,res= open_and_lock_tables(thd, all_tables)//directly and indirectly
res= handle_select(thd, lex, result, 0);
handle_select在sql_select.cc中,調用mysql_select ,在mysql_select中,join->prepare();//Prepare of whole select (including sub queries in future).
join->optimize();//global select optimisation.
join->exec();//
在mysql_insert函數中,open_and_lock_tables(thd, table_list)
mysql_prepare_insert(); //prepare item in INSERT statment
while ((values= its++))
write_record(thd, table ,&info);//寫入新的數據
在write_record函數中,table->file->ha_write_row(table->record[0])
ha_write_row在Handler.cc中,只是一個接口
write_row(buf); //調用表存儲所用的引擎
-
當客戶端鏈接上mysql服務端時,系統為其分配一個鏈接描述符thd,用以描述客戶端的所有信息,將作為參數在各個模塊之間傳遞。一個典型的客戶端查詢在MySQL的主要模塊之間的調用關系如下圖所示:
當mysql啟動完畢后,調用handle_connection_sockets等待客戶端連接。當客戶端連接上服務器時,服務處理函數將接受連 接,會為其創建鏈接線程,並進行認證。如認證通過,每個連接線程將會被分配到一個線程描述符thd,可能是新創建的,也可能是從 cached_thread線程池中復用的。該描述符包含了客戶端輸入的所有信息,如查詢語句等。服務器端會層層解析命令,根據命令類型的不同,轉到相應 的sql執行函數,進而給傳遞給下層的存儲引擎模塊,處理磁盤上的數據庫文件,最后將結果返回。執行完畢后thd將被加入cached_thread中。