MYSQL的启动
主程序是在mysqld.cc这个文件,入口是win_main,或者是mysqld_main()这个函数
初始化,包括pthreads线程和系统库。在line #4359附近的地方。
if (my_init()) // init my_sys library & pthreads
{
sql_print_error("my_init() failed.");
flush_error_log_messages();
return 1;
}
第二步,读配置文件
if (load_defaults(MYSQL_CONFIG_NAME, load_default_groups, &argc, &argv))
{
flush_error_log_messages();
return 1;
}
第三步:登记Performance Schema
第四步:初始化init_error_log() 和 query_logger.init()
定位data_dir目录,即数据文件路径。
如果启用了binlog,则必须提供server-id
初始化init_SSL()和初始化网络 network_init()
产生一个PID文件。
=========
每个线程处理命令的函数为:
bool do_command(THD *thd)
{
//从网络协议中,获得待执行的命令
rc= thd->get_protocol()->get_command(&com_data, &command);
//派遣命令
return_value= dispatch_command(thd, &com_data, command);
}
bool dispatch_command(THD *thd, const COM_DATA *com_data,
enum enum_server_command command)
{
//开始执行命令
/* DTRACE instrumentation, begin */
MYSQL_COMMAND_START(thd->thread_id(), command,
(char *) thd->security_context()->priv_user().str,
(char *) thd->security_context()->host_or_ip().str);
}
如何排查一个查询语句的表:
0:006> dv
thd = 0x00000000`0e2c4b90
parser_state = 0x00000000`0e2c4b90
0:006> dt thd
Local var @ 0xf80ec60 Type THD*
0x00000000`0e2c4b90
+0x000 __VFN_table : 0x00000001`40a507b8
+0x008 __VFN_table : 0x00000001`40a50810
+0x010 free_list : 0x00000000`0dfd0e08 Item
+0x018 mem_root : 0x00000000`0e2c7268 st_mem_root
+0x020 state : 3 ( STMT_CONVENTIONAL_EXECUTION )
+0x028 m_reprepare_observers : Prealloced_array<Reprepare_observer *,4,1>
+0x068 open_tables : (null)
+0x070 temporary_tables : (null)
+0x078 derived_tables : (null)
+0x080 lock : (null)
+0x088 extra_lock : (null)
+0x090 locked_tables_mode : 0 ( LTM_NONE )
+0x094 state_flags : 0
+0x098 mdl_context : MDL_context
+0x180 mark_used_columns : 1 ( MARK_COLUMNS_READ )
+0x184 want_privilege : 0xbfffffff
+0x188 lex : 0x00000000`0e2c6888 LEX
0:006> dt 0x00000000`0e2c6888 LEX
mysqld!LEX
+0x008 sql_command : 0 ( SQLCOM_SELECT )
+0x010 query_tables : 0x00000000`0dfd11c0 TABLE_LIST
0:006> dt 0x00000000`0dfd11c0 TABLE_LIST
mysqld!TABLE_LIST
+0x000 next_local : (null)
+0x008 next_global : (null)
+0x010 prev_global : 0x00000000`0e2c6898 -> 0x00000000`0dfd11c0 TABLE_LIST
+0x018 db : 0x00000000`0dfd1738 "sakila"
+0x020 table_name : 0x00000000`0dfd0930 "country"
+0x028 alias : 0x00000000`0dfd11b8 "country"
+0x030 target_tablespace_name : st_mysql_const_lex_string
+0x040 schema_table_name : (null)
+0x048 option : (null)
thd有一个成员变量:是用于存放查询字符串的。
+0x198 m_query_string : st_mysql_const_lex_string
查询方式如下:在MYSQL_PARSE()的地方,稍微往下走几步。否则显示不出来。
0:006> dv
thd = 0x00000000`0e2c4b90
parser_state = 0x00000000`0e2c4b90
0:006> dt thd+0x198 st_mysql_const_lex_string
mysqld!st_mysql_const_lex_string
+0x000 str : 0x00000000`0dfcfc18 ""
+0x008 length : 0xdfcfbd0
0:006> db 0x00000000`0dfcfc18
00000000`0dfcfc18 00 fe fc 0d 00 00 00 00-73 65 6c 65 63 74 20 63 ........select c
00000000`0dfcfc28 6f 75 6e 74 72 79 5f 69-64 2c 20 63 6f 75 6e 74 ountry_id, count
00000000`0dfcfc38 72 79 20 66 72 6f 6d 20-63 6f 75 6e 74 72 79 20 ry from country
00000000`0dfcfc48 77 68 65 72 65 20 63 6f-75 6e 74 72 79 20 6c 69 where country li
00000000`0dfcfc58 6b 65 20 27 61 6e 25 27-20 6c 69 6d 69 74 20 35 ke 'an%' limit 5
00000000`0dfcfc68 00 00 00 00 00 00 00 00-c0 64 a5 40 01 00 00 00 .........d.@....
00000000`0dfcfc78 00 00 00 00 00 00 00 00-68 ff fc 0d 00 00 00 00 ........h.......
00000000`0dfcfc88 50 ff fc 0d 00 00 00 00-00 00 00 00 00 00 00 00 P...............
数据库记录中的页(Page)信息。
这个记录是逻辑页:
0:006> dt dtuple_t
mysqld!dtuple_t
+0x000 info_bits : Uint8B
+0x008 n_fields : Uint8B
+0x010 n_fields_cmp : Uint8B
+0x018 fields : Ptr64 dfield_t
+0x020 n_v_fields : Uint8B
+0x028 v_fields : Ptr64 dfield_t
+0x030 tuple_list : ut_list_node<dtuple_t>
/********************************************************************//**
Positions a cursor on the first record in an index and reads the corresponding row to buf.
@return 0, HA_ERR_END_OF_FILE, or error code */
int
ha_innobase::index_first(
uchar* buf) /*!< in/out: buffer for the row */
{
DBUG_ENTER("index_first");
ha_statistic_increment(&SSV::ha_read_first_count);
int error = index_read(buf, NULL, 0, HA_READ_AFTER_KEY);
/* MySQL does not seem to allow this to return HA_ERR_KEY_NOT_FOUND */
if (error == HA_ERR_KEY_NOT_FOUND) {
error = HA_ERR_END_OF_FILE;
}
DBUG_RETURN(error);
}
MySQL的SELECT语句执行在innobase ow ow0sel.cc这个文件中。
Do_select(); 查询入口函数
| Sub_SELECT(); 查询部分JOIN的记录。循环调用ha_innobase::rnd_next()和evaluate_join_record() 获取并处理该部分的每条记录。
|| evaluate_join_record(): 处理一条查询记录。
|| rr_sequential(); 调用ha_innobase::rnd_next()读取下一条记录。
||| ha_innobase::rnd_next(): 读取下一条记录。
|||| ha_innobase::general_fetch(); 从给定的索引位置获取下一条或上一条记录
||||| row_search_for_mysql():从数据库中查询一条记录。以下分为六个阶段分别处理各个部分。
|||||| 第一阶段: 释放自适应hash索引的锁
||||||| rw_lock_get_writer() 函数用于获取读写锁,如果获取失败,释放目前的读写锁
|||||| 第二阶段:从预读的cache中获取记录
||||||| row_sel_pop_cached_row_for_mysql(); 函数用于从cache中读取一行记录。
|||||||| row_sel_copy_cached_field_for_mysql(); 函数读取每个字段。
|||||| 第三阶段:使用自适应hash索引快速查找
||||||| row_sel_try_search_shortcut_for_mysql() 函数使用hash索引获取聚集索引的记录
|||||||| row_sel_store_mysql_rec() 函数将获取的innobase格式行记录转化为mysql格式
||||||||| row_sel_field_store_in_mysql_format() 函数将innobase格式的行记录中的每个字段转化为mysql格式
|||||| 第四阶段:打开并恢复索引的游标位置
||||||| sel_restore_position_for_mysql(); 恢复索引的游标位置
|||||||| btr_pcur_restore_position_func(): 恢复一个持久化游标的位置
||||||||| btr_cur_get_index(); 获取索引
||||||||| buf_page_optimistic_get()
||||||||| btr_pcur_get_rec() 获取持久化游标的记录
|||||||||| btr_cur_get_rec(); 获取当前游标位置的记录
||||||||| rec_get_offsets_func(): 获取记录中每个字段的偏移
|||||||| btr_pcur_move_to_next(); 移动持久化游标到下一条记录
|||||| 第五阶段:查找匹配的记录
||||||| page_rec_is_infimum(): 查看当前记录是否是该页的infinum记录。Infinum记录表示比任何键值都小的记录
||||||| page_rec_is_supernum(); 查看当前记录是否是该页的supermum记录,supermum记录表示比任何键值都大的记录。
||||||| rec_get_next_offs(): 获取相同页中下一条记录的偏移量。
||||||| rec_get_offsets_func(): 获取记录中每个字段的便宜
||||||| rec_offs_validate() 验证记录的偏移量
||||||| row_sel_store_mysql_rec() 函数将获取的innobase格式的行记录转化为mysql格式
|||||||| row_sel_field_store_in_mysql_format() 函数将innobase格式的行记录中的每个字段转化为mysql格式
|||||||| btr_pcur_store_position*(: 存储游标的位置
||||||||| btr_pcur_get_block(): 获取持久化游标的缓冲块
||||||||| btr_pcur_get_page_cur(): 获取持久化游标的页的游标
||||||||| page_cur_get_rec(): 获取游标位置的记录
||||||||| dict_index_copy_rec_order_prefix(): 拷贝记录
|||||||||| rec_copy_prefix_to_buf(): 拷贝记录的字段到缓存buffer中
|||||||||| dict_index_get_nth_field(): 获取第n个字段的起始地址。
|||||||||| dict_field_get_col(): 获取第n个字段的值
|||||| 第六阶段:移动游标到下一个索引记录
||||||| btr_pcur_move_to_next(): 移动持久化游标到下一条记录
||||||| mtr_commit(): 提交事务