数学建模社区-数学中国
标题:
MySql轻松入门系列————第一站 从源码角度轻松认识mysql整体框架图
[打印本页]
作者:
zhangtt123
时间:
2020-6-3 10:36
标题:
MySql轻松入门系列————第一站 从源码角度轻松认识mysql整体框架图
一:背景
H+ a* t* z5 z/ P n
1. 讲故事
v- q1 { `9 L/ q1 N% T0 b
最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
. |8 k: P+ q2 S6 N
1 a# C" T( D/ K
二:了解架构图
8 p( a+ b3 ?- W$ Y/ b
mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
6 S$ X* N4 A [( h
, U/ Q' ?8 y$ ^+ a
1. 从架构图入手
# [! C4 F, X- x
大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
& v# H7 Z+ o& K8 [8 b; n
) V. r/ [1 P& [1 O& Q- r
& s; [+ L8 T" Y' o: D- D3 l
4 j$ u0 T5 _1 e) {) h3 C* Y. Q0 R
其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
5 k8 k5 s5 @8 W2 s5 _3 v$ s
, C% J3 Q1 Y9 K ~5 w+ z! x' ~
2. 功能点介绍
* B% D0 g$ n. P( D6 N% s
MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
& ^- s5 X$ e8 |+ F- v2 F o
6 K; M3 p4 s8 F
<1> Client
7 P: I T' r& K# c
不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
# U/ w6 R! |, X$ X; _' z
0 n& l. O8 ]3 `
<2> Connection/Thread Pool
x7 M* \+ z/ `/ ?1 I" S
MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
k0 ?3 b1 O3 t9 [& |# c2 q
4 Q w# A' b! N2 c7 n
<3> SqlInterface,Parse,Optimizer,Cache
2 u! ]( q- T: k! z3 L }0 J
对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
; v* v* d+ l' \1 O8 N6 b9 Q7 V
/ J. A) L5 d# S
<4> Storage Engines
+ B+ S& Y3 l4 a- _0 _0 K6 W
负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。
: ~, b" K7 H1 @5 G
6 a) s% Q8 T4 G! s/ D) }* E' G- D, I
三: 源码分析
( y. v. r D- m% U
关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
1 Y7 N3 ^0 i* m2 w6 m2 F
j, E7 l. [$ H% C& X
1. 了解mysql是如何启动监听的
% H, p% L! J3 J6 a4 Y
手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
+ X; |( h5 u3 c r( x* b9 g
9 N8 F1 ^5 H2 I v' d9 k' C
& j: R' E0 X; [% t2 h/ ]
, S& h* i. c, n; v2 c
从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
( W( i" j# b* J7 W l2 ?7 V5 y7 z
* s5 S) q8 }7 }, b, o
<1> mysqld_main 入口函数 => sql/main.cc
3 T X" ~ V5 ~4 @- |
! J6 u0 t! S8 L( P
" v; f% `/ ~1 {! P, C4 f: ]
extern int mysqld_main(int argc, char **argv);
$ ^4 f; w: J/ g( W3 k
% g% m% l* Z% K7 S
int main(int argc, char **argv)
* }- M9 Y/ j4 _+ r
{
3 [9 d8 B5 l6 q/ x
return mysqld_main(argc, argv);
8 m* O( l: b, H k
}
2 a5 d1 B p: ]4 O0 F$ ~! @8 S
6 M; x- j/ [4 f2 z, V" r
: r; i/ Z. Y) |5 S6 h5 k, G
这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
2 ]/ P X) a6 A* [* E) e
& R+ o5 k+ A! ~; n9 W R
<2> 创建监听
) [' p7 | C$ M' V/ w4 @
4 {8 l/ L: ]- Q* A
. w! w; P! L( _3 N1 j
int mysqld_main(int argc, char **argv)
7 I' C+ X s0 ]# Y: u# v5 B
{
* ?9 r! u8 [4 v8 K4 W* g
//创建服务监听线程
4 A1 |$ ?; N) I, g
handle_connections_sockets();
0 B. c% x! t$ b4 ]8 c+ ]/ L
}
8 M& F8 C E8 a0 \8 \9 T
+ Z+ O y6 _- e4 V
void handle_connections_sockets()
) y% e, Y1 u5 ?$ @9 O Z
{
4 D% `" R7 Y/ z9 ]
//监听连接
1 c/ e0 Z! I0 @8 h
new_sock= mysql_socket_accept(key_socket_client_connection, sock,
6 M1 ~; f! {5 C* }
(struct sockaddr *)(&cAddr), &length);
9 `2 K, t) {! z0 Z$ v3 F
3 `; K7 W' R+ Y t3 r
if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
5 e2 z7 k' V6 i: s
thd->security_ctx->set_host((char*) my_localhost);
* r; J. [. ?! G( L- k4 U* W
) |- L* m+ }+ ~4 A* Q# b+ y6 q( B
//创建连接
; g! `; W8 \2 B4 Z; C7 z
create_new_thread(thd);
4 |$ p4 p/ F8 D) w6 c/ L
}
% l3 M% u! n% e2 h) Q
: p1 c. x0 H, l5 C# {
//创建新线程处理处理用户连接
/ G1 o ~( S( m& _6 @
static void create_new_thread(THD *thd){
, H: {: N+ {1 O7 e
; W- K0 v# K" v8 i% U" X3 O* j& G
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
- \: j$ g- v7 }
! b+ X/ q/ O, Q) Z
//线程进了线程调度器
% w% a) h2 j" R+ s5 V; w q, H
MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));
5 b- y7 m! d* l: ?. ]* |, \
}
0 D+ A. T# S0 m" N3 {* D8 s
& i, B' ^+ F0 h- G6 R
0 m/ `9 w( f- J& V D
至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
; A% ]( q! L5 ]* N
1 v$ l9 k+ T6 ^8 P' d7 g0 H: M
7 K7 S% v; d8 p) S2 A
2. 理解mysql是如何处理sql请求
7 v% i, q7 F! I
这里我以Insert操作为例稍微解剖下处理流程:
0 x+ A: ?! a1 v* v3 l) O3 B
g/ S/ R& N) p
当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
2 w$ L' A5 y! y% S
9 K8 ~& U( x j1 }
' t- P) S; G6 ?* m
static scheduler_functions one_thread_per_connection_scheduler_functions=
4 m; k2 q, \# f) x4 l' e/ E& `' ?
{
/ e+ `2 ~- I; s4 g& }2 m6 Z+ P
0, // max_threads
7 W0 G- U' T0 S* k1 x+ S% B* N5 V
NULL, // init
( H6 s% S, i1 x5 m l# u9 u
init_new_connection_handler_thread, // init_new_connection_thread
; n) j0 q- p! M; O! u# N# b" }
create_thread_to_handle_connection, // add_connection
! T0 X, N5 X, B9 q
NULL, // thd_wait_begin
9 `" b6 U; p: }
NULL, // thd_wait_end
; s9 J7 \. o; ^6 o
NULL, // post_kill_notification
0 q8 M% R; z X S1 T6 W1 K5 D
one_thread_per_connection_end, // end_thread
4 s0 C9 e l& N2 S* S
NULL, // end
k0 ^6 @ a l* D) P2 x7 d: Y
};
# P0 m e& G3 U. ]4 Q. {4 v/ M' n
( c3 o. K1 F' F* u; [' M4 \$ K
; \) W3 `. R. x" `
从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
8 k* m6 _" L" p. }/ c; t" U: f
/ @' L/ k* u2 r( X0 P
<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
% N8 Y( b$ ~) H7 X+ n
5 }% l* x' e% P, ~9 u. i- t* Y
void create_thread_to_handle_connection(THD *thd)
3 ?2 ~- E3 l; G: V: }& T" R
{
5 F/ r- ?. I3 T! b* Q0 {9 u8 q
if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
8 n% K. T0 Y% f- b7 Y' L, ~ _1 x
handle_one_connection,(void*) thd))){}
- _- b v+ n; i7 B: M0 I0 r6 l3 h
}
; p% S' g9 ?& V+ L0 r5 R, O ~
//触发回调函数 handle_one_connection
9 J9 R) z/ P) c Z" \* F
pthread_handler_t handle_one_connection(void *arg)
" u( R; e8 K) G7 @& |8 }# R
{
' \. e! D3 x" d& ] c% `4 I5 a* m
do_handle_one_connection(thd);
4 K: c3 Q0 _! D& D! }% C# d5 y
}
* t' C4 {& Y: H, s0 m3 f5 ]* a
//继续处理
% H. u9 v% z$ p/ a" h2 W
void do_handle_one_connection(THD *thd_arg){
% ^ q- E; G" C2 ]* W. b- R
while (thd_is_connection_alive(thd))
( B5 i/ u: e& b& i9 @/ k
{
+ G0 i, K/ d* V" X/ G" |
mysql_audit_release(thd);
5 f7 c4 @' o7 Y4 l0 a2 }) u8 \! X
if (do_command(thd)) break; //这里的 do_command 继续处理
1 ~4 R, _, ] N7 P7 E
}
# b! L% `5 V) ~7 K4 L+ {% m
}
' }; h# Q. @; f% Q" e- P
//继续分发
5 k( Y. d! G7 A5 j% l5 w: P6 n! h
bool do_command(THD *thd)
$ Y) w& o/ O4 J; v7 h
{
- U9 D9 M( U0 j; c
return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
8 G1 k9 h( N) \. a, I1 G
}
0 [) \4 [- g1 m. C, f/ k9 ~
bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
2 o2 o' h9 R" X8 [2 I" @
{
2 N& g* t6 z' Y. g& s
switch (command) {
: H5 n ?2 H p
case COM_INIT_DB: .... break;
3 t `- w1 b) m( o0 x4 _+ y8 `; }
...
% N1 a$ X* u( O4 ]
case COM_QUERY: //查询语句: insert xxxx
) A1 k7 } X% @9 a" t/ q% R; U
mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析
/ S( t6 V, K9 |. g) v. |3 H5 j
break;
) c: P7 M; d" |6 ^+ i
}
, i+ f( W3 l3 d9 n5 Y0 r
}
4 i* J3 ?* i2 H: _
//sql解析模块
2 J& S Z3 }8 @
void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
8 D9 F, [7 U( X* U
{
3 ^* _8 _& C6 f" i' U
error= mysql_execute_command(thd);
' X N. a' Y* Q) m' ?- k: f
}
* m* Q6 k% U( q2 s8 d1 O
* u v9 M; _, q( G: j
# s3 M3 j' `# S. Q6 t
<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
6 b2 R) x) C9 D$ l$ v& k
3 q& {& K C: |) B" [. _
//继续执行
9 j' h2 E) W$ a8 Y
int mysql_execute_command(THD *thd)
8 D: ?4 ]9 w/ i9 m
{
2 C9 g2 ~% J+ Y% l& |6 [2 c
switch (lex->sql_command)
3 K g& {1 l" z+ S. F9 H
{
/ c) ?6 ?: s; e. a
case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;
$ U0 x0 W2 D" t; i2 [% Z5 U0 o
+ ?% h1 [& p( }9 U- [; G% z
//这个 insert 就是我要追的
$ p4 B/ j$ p1 [& J1 Z+ C6 u$ [
case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
( l9 G5 p! n4 ]5 l2 U. V5 I
lex->update_list, lex->value_list,
1 s" O) J* A" V
lex->duplicates, lex->ignore);
# m }( o% }& J+ u8 @0 E0 f- f2 q
}
$ t5 I( p2 U( B! U
}
( @( R1 C+ } m. _) v# M o
//insert插入操作处理
* ^0 t8 b' L7 f& ]
bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
* s, y9 ?5 P" c& B0 E5 @! h% p
List<Item> &update_fields, List<Item> &update_values,
$ l0 t: ?" S5 K! h
enum_duplicates duplic, bool ignore)
8 n. Y9 s& H! c# G" ~4 h
{
3 o7 G# k9 b" t3 P/ O
while ((values= its++))
( H5 L& N: e9 x3 h: F2 Q
{
' n1 D }9 \7 H; G$ p' B: s
error= write_record(thd, table, &info, &update);
7 v& h3 n0 F' f+ D
}
# ?9 h. k$ y7 z) S3 j+ I
}
- [' o( t& o* L
//写入记录
! A N* S6 v2 J( z' A( r
int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
7 x1 k2 V1 V' _/ A0 f2 D% f
{
2 U+ P: R# o7 _0 E* U: g5 y* m, M4 c
if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
5 [8 ^$ b& H0 f: N
{
" i0 T- p3 T9 Q8 X) {0 {, |
// ha_write_row 重点是这个函数
( N! c# b, f. s# q+ A. u
while ((error=table->file->ha_write_row(table->record[0])))
, h$ c7 @3 P8 l2 J. K$ r
{
1 E* t& y0 _% Q% ^8 p0 ^) Y0 r
....
8 o1 d( ]7 z. K0 r/ M/ b
}
3 ? u# T/ e% A
}
$ l1 |1 L+ c3 u7 }" m
}
4 N( t8 B& A" X5 G
7 \) m6 D8 ^( e K0 |
& j' n- w' x, `: f
1 j9 x7 h! G q! P) F/ g2 a3 {
可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
* l' w- W, i0 o
x _1 r3 B! J( s l1 C( q5 f$ z
<3> 继续挖 ha_write_row
+ p. f* M3 V+ l( a* i
7 \9 C' T. t6 s! p6 g6 E
int handler::ha_write_row(uchar *buf)
& w: Y# O' [6 M0 a
{
1 l5 f% |( S( E6 U! J
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
& F& u& O" _6 n8 ]7 W. ?; l
}
9 s2 Q! e0 L; J2 Z s$ z+ n6 l
0 j5 s( ~6 f7 K+ P0 h
//这是一个虚方法
! ^! U, L. _0 w0 Q' S" @
virtual int write_row(uchar *buf __attribute__((unused)))
5 S& }- p& | |6 _% _. i: t, }# N
{
! F3 f3 H4 |0 T5 }
return HA_ERR_WRONG_COMMAND;
- i1 u- s' C7 Q# P' D% K: E h1 O
}
# _1 g% M5 L, G
1 Z6 H" t6 b8 P8 V
) ^; ]% E. g" Z& \+ J
看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁
( S h/ S" E( E: |. M$ Z4 K1 R
% r- S4 A M6 |" b9 N
3. 调用链图
( a7 I3 ?6 Y7 U0 z5 K4 U$ _; r
这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
0 y T. w4 w/ Z
$ D, r% m6 u, P/ K& ~
9 h* U6 D5 k- ?. |/ d
# ~- H8 A, t. i% H0 b% j$ [: U T9 b
三:总结
- `! C# P; T. z: `9 G" _0 @
大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。
+ D3 N. }7 c& `) k( N; P3 m
————————————————
* |2 [1 J4 X! g
版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
, {9 p, O; E7 k+ X4 ^$ O
原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
+ U: Q1 c: S7 l: l
欢迎光临 数学建模社区-数学中国 (http://www.madio.net/)
Powered by Discuz! X2.5