数学建模社区-数学中国
标题:
MySql轻松入门系列————第一站 从源码角度轻松认识mysql整体框架图
[打印本页]
作者:
zhangtt123
时间:
2020-6-3 10:36
标题:
MySql轻松入门系列————第一站 从源码角度轻松认识mysql整体框架图
一:背景
1 u; ?6 v4 Z" x# V; k
1. 讲故事
$ w8 g' Q" N5 k
最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
. s* ?# G: v# c
C3 X3 F0 h: y; ] ~1 P& \ w
二:了解架构图
! o' N5 C o! M3 l7 ~0 [ \. B
mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
+ s: `& V$ R! l; ]) T& P q
# R0 z/ ?( D7 G
1. 从架构图入手
4 {" {6 u! o, R; g8 c7 j4 g
大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
2 b8 C) E4 _7 G8 V1 k% T
9 H$ {5 m( F1 S
- `( u* `2 Q% l% f" ~0 T
, J7 r W2 T- W% ?3 _' O/ D
其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
! l( L" @2 g; `* Q0 {
/ a U4 e9 _4 Q
2. 功能点介绍
7 m O- v |3 _7 c v2 R/ G
MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
1 u1 ]0 S6 b8 ~+ G5 I8 n5 g
6 R y: S8 s: m
<1> Client
( h3 s( I; J- M
不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
2 n3 M& Y. N1 W' T5 j
: y/ u! s( p% K+ ?0 e; H
<2> Connection/Thread Pool
* F: `' r1 Z8 L" n( ?
MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
6 f0 a0 X$ @1 _- ^9 d) h
7 ?7 f4 S4 U5 v- S3 A% s \
<3> SqlInterface,Parse,Optimizer,Cache
V+ D6 _; [. v! K8 B* j; g
对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
' [7 P+ p0 h2 \. z- g6 @
* U+ E- w) ^! ^! w7 y
<4> Storage Engines
9 s/ K( b- f1 T v* q- W
负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。
2 T' n# o T: J! y, H) s
/ w% {9 h0 D. L( n& |7 V
三: 源码分析
& W& U, h h0 K0 Q8 ~3 E" F" j& `
关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
5 q0 e$ c$ Y4 Q/ x
g/ }0 @, N$ ]6 K |# h5 l
1. 了解mysql是如何启动监听的
, W3 r. ]$ V& M- t: A% J
手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
4 ]0 o7 v/ l6 x9 X% P, m5 q, N7 A
8 y3 b2 x$ G% D9 _5 I0 k
1 T" `& M- L' L; w1 q
9 k* \% I, ]4 A$ F" H
从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
2 T/ m/ y$ }4 q7 a* T
! L% X* L0 W6 v; h2 N
<1> mysqld_main 入口函数 => sql/main.cc
- J: _7 f# n8 d4 k; L) h5 @
& @# v" A* j8 Y2 k
4 K" ]3 g* Y! V# o# H! Q1 @
extern int mysqld_main(int argc, char **argv);
9 I2 K: J0 i* z8 c7 m
& w( ~: n; x; D3 P6 E
int main(int argc, char **argv)
: Y; x+ A: [ V J4 }. S: _
{
6 j+ O, ~# g/ a6 P/ Q
return mysqld_main(argc, argv);
* i- @% w1 P/ i; D+ Z+ F% ^
}
q3 a c2 s" a
: r7 |1 q3 Z) W5 x9 Y, g1 m
, Y' E& q6 g. n8 T
这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
# F% z6 S3 N4 l& u1 r$ f
1 q5 y2 p: {: E) M& V+ e) v
<2> 创建监听
2 q0 O& f# f$ _3 H" m/ M
" `% T. K. w8 n* [, j
* c2 ?4 l! I! I- E& q
int mysqld_main(int argc, char **argv)
+ Q1 q+ C) F: K g$ Y3 l ?
{
- Q1 G1 d" `6 u
//创建服务监听线程
% K, a& y8 h$ \9 S
handle_connections_sockets();
+ C. ]' O7 f% m' g3 Z; M* X0 T
}
8 z: Z) R1 n3 S1 r0 V+ m2 Q
+ z% H. r$ P' d7 O* l
void handle_connections_sockets()
" j. s. p; v! H8 y$ v) C9 A8 p
{
( T3 V" H6 }' H5 R# z
//监听连接
% f1 n. @' l6 n3 H" r, b( {4 G- `
new_sock= mysql_socket_accept(key_socket_client_connection, sock,
" C' o4 f0 R3 Z; r( X- e8 \! F
(struct sockaddr *)(&cAddr), &length);
7 s; @& V. \0 |
# b6 L% D/ I. c
if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
3 u7 }# E2 t2 j! l3 ]( H2 f$ f
thd->security_ctx->set_host((char*) my_localhost);
/ ` _1 U8 |4 }
+ @; c Y. M. b
//创建连接
( F1 H. ^# R- t" r4 D9 u0 v" S
create_new_thread(thd);
% g) E7 E; o: f
}
& _3 f, P( [6 U8 g3 _* @ Y# }* a
& w. W4 P5 ], d$ l' S
//创建新线程处理处理用户连接
& t$ m0 u- M( A( |0 V
static void create_new_thread(THD *thd){
" Z6 g( l- e5 o+ U
8 H& F% k: @5 T
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
9 h5 U: R: {/ z. Z: I) E0 e
% k: c) l8 y: _9 Y6 Y/ w
//线程进了线程调度器
$ D- L% M4 u2 F4 Z$ h% {! x
MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));
+ b1 @2 X1 ^+ w2 Q5 P
}
6 P2 L3 h) y7 U1 C
; k7 n/ N9 J1 U& u t8 l
0 L( w0 E' P! n5 I3 O5 o/ M5 X
至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
6 x% N9 C4 f+ w+ H6 u5 v. C
& e- |* ?( M7 k- t: h0 ?7 l V r
# d! Y, h1 x* k4 j2 x s% y- O. U
2. 理解mysql是如何处理sql请求
4 _$ ]# u6 }- G" s5 \
这里我以Insert操作为例稍微解剖下处理流程:
3 R6 M# d+ U3 x8 _
- p$ O$ e" k. T8 q$ p T3 i
当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
! _% m: v; H& x6 Z7 V" D9 Y
6 l* o& D5 F6 e8 C
# _4 }* A( D# g' @: p
static scheduler_functions one_thread_per_connection_scheduler_functions=
' B2 Q8 r2 c+ Q$ Z& i2 d! q; s- o
{
- i d+ r1 ^$ a' U2 X2 ?
0, // max_threads
2 F4 N; k+ a$ ]8 k$ \
NULL, // init
$ E. \' |( }" ?7 _: x6 M5 P
init_new_connection_handler_thread, // init_new_connection_thread
" J6 C+ t5 E: a" }
create_thread_to_handle_connection, // add_connection
! _4 P+ b7 k( G4 ^3 g Z
NULL, // thd_wait_begin
; o. c( k9 W8 U. ]0 f
NULL, // thd_wait_end
$ d! l: |: Q6 i! i r
NULL, // post_kill_notification
- Z& j) n% _ l8 p6 o, S
one_thread_per_connection_end, // end_thread
$ {5 s3 V h# N5 \, t
NULL, // end
& N4 A0 S/ O' X1 X0 v
};
w& I- L w/ d% I3 _: ^, `
: ^$ t$ _0 P2 ]% E' K9 } R- E
8 r1 q6 p5 s: @! a4 i' ~- s# L
从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
8 f4 j: J" t, ]7 o
6 |* }4 K1 W, c$ a C% r0 h0 P
<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
, i0 ~0 {) B2 o
8 H; S3 \/ ?2 U! | t% n' W* r
void create_thread_to_handle_connection(THD *thd)
: b7 P; z4 j, a3 p
{
) [" g) \0 P, v* l& u
if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
9 c, X% O$ D4 g. I- q- i- }
handle_one_connection,(void*) thd))){}
! g Z7 E- l' J- Z: r( C! \
}
8 h4 r% f1 |& g% p& [
//触发回调函数 handle_one_connection
2 w0 V |6 Z) j& q
pthread_handler_t handle_one_connection(void *arg)
% Q* O) ~9 f4 k8 K T
{
2 E$ y8 `. ]( W/ l& Y, Y1 u
do_handle_one_connection(thd);
* B+ }% S) ?. A/ s. }* S" A) W
}
# t: X$ ]- \$ [- a5 {5 \7 R4 x
//继续处理
0 v$ |# X6 O* J
void do_handle_one_connection(THD *thd_arg){
% g0 |- t, `" J
while (thd_is_connection_alive(thd))
, ]5 H& d0 E0 d" A
{
5 h$ `# Z2 C. E; z) A, L: v
mysql_audit_release(thd);
3 T4 ~! T! z u2 p$ @7 [
if (do_command(thd)) break; //这里的 do_command 继续处理
) `1 y' n7 q) H7 o
}
o. `/ O+ q& [. Q8 Z' E
}
) E) }( z/ W) @. T8 @
//继续分发
/ L/ ]( j. {% E$ P1 t3 w
bool do_command(THD *thd)
* @) s% q5 t1 X) s
{
" E% s Y' \) ]* h) x( X
return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
9 k# s5 C8 [: |: T' @/ h
}
4 ~+ ?/ d3 }& ]4 |* O
bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
4 G/ A" |3 X, P% R( A5 k
{
8 h4 l5 {% `+ j) r
switch (command) {
; X, G( k/ W$ X. y& N
case COM_INIT_DB: .... break;
& R* u/ o: X8 f& C
...
$ x8 p9 ]' J- q/ _% Q$ ]% g
case COM_QUERY: //查询语句: insert xxxx
3 a r1 R3 K- G: N' i
mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析
# Q4 X6 d1 e4 }8 p
break;
/ G6 t% r+ i9 X$ ~( S* J
}
1 _; N" z8 {0 |
}
- a0 @5 i7 I: D
//sql解析模块
% \. |7 ~0 g& w* i6 ?2 L
void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
& T, A9 l+ J4 o0 I* C+ K
{
, A5 c* M7 _# k( s
error= mysql_execute_command(thd);
" Q( Z9 c* W/ X6 d- t: I
}
- Q) f* U- _4 E7 P8 ?" n
0 f9 k( ^& p2 M3 G# o
' m! a: o, `3 {$ o, B! |
<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
# N. B' w0 ~) ?1 s
' i; [! _( ~9 D1 _6 C
//继续执行
" O' `5 T1 |/ A# j4 t* J% L
int mysql_execute_command(THD *thd)
% R% D3 m t3 F: W, _, b
{
; e& { _& i, `8 B8 e8 W8 d/ Y
switch (lex->sql_command)
1 [8 y" u$ a! E2 v( _' o5 x
{
5 S4 P5 ?3 l# C6 L/ d8 |! q1 H
case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;
7 R6 r8 E8 Z u. N) n; H
6 b. p5 R$ q( ]: W& |5 X' E! ?
//这个 insert 就是我要追的
9 K# p3 W# Q6 G* K! w
case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
3 @8 |) }5 j0 n7 l6 ~2 @! \
lex->update_list, lex->value_list,
* x! C* ^' R% G8 `/ B W
lex->duplicates, lex->ignore);
6 F- h' p: j# g O8 D
}
& C$ a! y/ [6 }, f
}
% p5 @# _7 I% [$ B# ^
//insert插入操作处理
2 s$ |" d" S; c. Q% F7 q
bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
, Q6 ]. U: r/ q6 T6 U' I9 ?
List<Item> &update_fields, List<Item> &update_values,
; @4 R* q3 a) g. m
enum_duplicates duplic, bool ignore)
/ g; x' e& g" [4 e
{
; w7 s& n- l6 ]
while ((values= its++))
) \5 I6 S" d7 O* R7 I. U
{
" k. y7 _. w) X) Y" B3 ~
error= write_record(thd, table, &info, &update);
' \7 b6 W |# V
}
( t# I4 h# G+ _( m/ f5 T
}
- e2 ] E0 H" S1 \7 z" R
//写入记录
$ w( }) G( }/ S% T
int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
& j8 M; E6 M6 Z4 F/ c- t/ m
{
3 H$ C2 D0 d& b6 E& z
if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
" C- k/ y( Q9 h' G5 w) K$ k
{
0 @5 a; W2 R" T
// ha_write_row 重点是这个函数
7 R& q3 v5 ^% @: [3 _& b8 S+ g( F& z
while ((error=table->file->ha_write_row(table->record[0])))
% W8 \) z* Z! i1 |/ `
{
. Y: e B5 E& M3 k; X) o+ k
....
; v4 ?$ x& A1 m8 n; p- |
}
; G" U0 \6 z5 [* P$ }& T' z/ C9 q( ~
}
y& k+ Y8 K2 y3 c% c5 Q3 I
}
& m9 c% T, u. ?& k2 H. `& o
' J: e: K9 x; e: Z
& ?) S6 `. D" A" Y2 @
) a: T2 N8 x, v: Z
可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
! N/ i f6 Q$ T2 T3 }5 D4 q( [7 {
; p8 {( e7 ?) A& G
<3> 继续挖 ha_write_row
; `" }- s7 h U: x
* X' W$ e; ~0 F+ p3 ^
int handler::ha_write_row(uchar *buf)
; g! q* J$ D+ @4 [1 f: [
{
9 e# I" U9 n! L. l8 }- f
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
H5 k6 K- _' P) c1 p
}
9 n) i% L3 M" w
" [6 a& L) t2 N! b
//这是一个虚方法
- M0 d: u5 t3 m; r! N) e) C5 o
virtual int write_row(uchar *buf __attribute__((unused)))
8 H9 {9 r$ K) Z$ @& n" g4 B. W
{
1 Q2 k8 `& V5 a/ @3 n- A) J
return HA_ERR_WRONG_COMMAND;
# ^1 q+ k. L3 w6 ^* V, O6 m
}
; d0 O( y/ N b
, S, }4 R3 c) N4 F. t( n2 h
; }% o: b y7 _: O8 H9 {) y! s" J
看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁
4 C$ U3 ~0 w; j, r4 Y( ~- c) q/ y
9 y3 u6 C/ q3 z; N6 B: ~; B
3. 调用链图
5 e' w% U e( G4 y8 f
这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
. Q3 f4 D# {+ u7 l5 c
[8 [+ z4 C6 ]7 K% e" S
% B! j6 H' j! {2 L
/ d+ G- O2 Z, w* N9 u
三:总结
. L n/ ]; O2 r# D
大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。
; Z- E* p- }- {
————————————————
8 S1 u1 b$ {- v* `* g
版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
0 m1 T( J% ] l1 `/ P
原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
6 Y9 U, l3 _! p
欢迎光临 数学建模社区-数学中国 (http://www.madio.net/)
Powered by Discuz! X2.5