数学建模社区-数学中国
标题:
MySql轻松入门系列————第一站 从源码角度轻松认识mysql整体框架图
[打印本页]
作者:
zhangtt123
时间:
2020-6-3 10:36
标题:
MySql轻松入门系列————第一站 从源码角度轻松认识mysql整体框架图
一:背景
7 ^9 u+ C9 k; U
1. 讲故事
0 E4 m# G e4 E
最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
- q7 @7 G$ _( q7 Y5 ?' f! X
* a; K V2 Q& `3 J; X6 s. T, w9 R
二:了解架构图
/ X; _# K, U% X1 p3 D5 D
mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
/ L6 |, ^) L8 ~4 @7 C$ p
) \9 S$ o# b8 {$ w0 \( |" t
1. 从架构图入手
- m6 H/ D( F- d, c
大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
- F* B6 \# `, Y% A4 h
. K/ G) @. t- V# t- o
- t! o& e4 w5 r* e
2 K+ c' C; j9 \7 \
其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
/ |: O9 _) r# k7 b- L4 Q; ~3 @5 ^% b% n
* L9 T- C' ?" i, J
2. 功能点介绍
1 V0 \. w% w. Y m. g& U
MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
/ H& M- Q/ f; A* q8 u
* F6 F' m' i+ X
<1> Client
3 [) B1 v Z" d( ^8 a1 v1 v
不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
" a2 f. }: ^0 \2 l9 P2 _2 e8 t
( t! o9 l; I8 T" k0 L
<2> Connection/Thread Pool
/ I. h3 ^1 E( q9 S+ c% z) U
MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
( l) m* ^! \0 e3 k
( h: I/ G0 U& P) a3 {/ L: ]# p) B6 ~
<3> SqlInterface,Parse,Optimizer,Cache
3 ?6 J+ O. |% `! q1 T
对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
0 l& R5 a% L: k C
! ?& ~" Z& q! n- n% I% M
<4> Storage Engines
& E6 f# o n7 I- h4 P2 t, B$ B* U
负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。
* k' v) s- }+ h9 F0 j" Y% X4 o a
, d. Q" @; l" W: ^, o4 r
三: 源码分析
" U( d% ^& x' R$ q; G9 f
关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
a8 j% c) n8 A# f( y# ?& N
$ k* [5 Q3 k' Q$ _
1. 了解mysql是如何启动监听的
, `' k/ P; Z8 z. T
手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
" S' T! G" V9 ]4 m2 ^+ c
1 g/ ? j/ d3 N% R9 u
1 R* H* a0 j2 i+ H
+ _; q ~8 X1 {$ V$ }* X& t
从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
- V2 b- I7 H( `5 D; c
[. c( M. r/ M5 ^7 _( n; B$ _6 a
<1> mysqld_main 入口函数 => sql/main.cc
( h E2 g+ t7 E5 a
% z: K+ K7 C. H' {$ L/ r0 @+ {& v6 k
, W1 a+ }# r; P4 N2 [ R
extern int mysqld_main(int argc, char **argv);
% A: ^9 o2 G3 b5 l) c
( O9 B0 a" P1 D ~; t0 u' u( H
int main(int argc, char **argv)
) u- w R/ t* E8 z* J Q
{
- q( ~) K+ Q% H
return mysqld_main(argc, argv);
) w! R+ I' l; e
}
# B W6 o0 `: Y" ^; t/ F. v: ^
2 F( g& }" o' n6 E, g* }! C8 w
1 ?. t0 D7 X) P2 E) v5 Z
这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
$ l0 x$ @. l1 a
4 X0 \2 @: l( ~- e7 O) b- @
<2> 创建监听
2 \; l, `; ~! x) k6 Y) J/ V
# W, s1 p& E0 W; q' A' A
+ v9 |, J& f" G6 h
int mysqld_main(int argc, char **argv)
* c; M2 c5 d1 O1 ]8 a$ e
{
& o0 A, L2 ^3 i. A9 |4 u
//创建服务监听线程
# C0 G: E, v! r
handle_connections_sockets();
/ J9 d) P6 P7 A6 A: @2 E
}
( e0 ]& J: m; b8 A% P9 M* D
# j( x) ]' a* G1 m7 v
void handle_connections_sockets()
" v) f8 y$ j H- ]; z
{
+ R3 z3 M6 z/ U7 t
//监听连接
1 g1 u5 \) k0 W4 J& E3 u+ u4 H
new_sock= mysql_socket_accept(key_socket_client_connection, sock,
0 F, G, E- l& X% a& Y
(struct sockaddr *)(&cAddr), &length);
% N+ j) Q y, l0 w
, ^, M( \2 L) n+ E; h
if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
0 U: Z5 j" o4 a0 }# w
thd->security_ctx->set_host((char*) my_localhost);
9 @$ r0 m0 Z; ]" f; `) J
. ]& |3 Y$ U7 A
//创建连接
1 z, [0 ^! _# t9 s% [+ k5 c
create_new_thread(thd);
' [, }* ~: N6 k6 q' c" B
}
, Z/ n1 O ]. ?
" F3 \- [- k4 j( @6 s9 O
//创建新线程处理处理用户连接
" V0 J, O3 z$ p- [
static void create_new_thread(THD *thd){
, q2 V% Z2 g9 U( d `# m/ K
; q- Z2 z$ ^, C% X4 y) c# Z: A! P
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
. R6 c2 _& U1 ]: G
+ n2 V( D+ R% u
//线程进了线程调度器
$ F) y; j6 W1 q2 w6 x; i
MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));
, b1 o* I$ F7 @+ V: S* U
}
2 a1 f- z& ^5 A# h# A
( j8 x. R- I4 X4 ?
4 @ j w+ u1 ]
至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
* @) a* N4 {# w) l' ~
0 ]! [8 H! w0 ~5 u
7 `4 f! O5 ~, z, q! ]# K
2. 理解mysql是如何处理sql请求
( @3 B8 z6 F7 l9 p
这里我以Insert操作为例稍微解剖下处理流程:
/ b% O/ h) i; W3 T. Z3 u2 W
6 @' K- a% { |# ]# o6 S% `5 n
当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
- ~, c: S1 K# \( D e3 A
' F/ m! B, h6 f* Z& I
+ k# x: a# I( v& k
static scheduler_functions one_thread_per_connection_scheduler_functions=
% C1 J5 M& p# I- o& ?3 r9 D" ]
{
; r y g6 r# r) `
0, // max_threads
6 T4 Y( L! F5 v. o/ S' y
NULL, // init
, E# k% _7 h2 Y3 r! z1 b
init_new_connection_handler_thread, // init_new_connection_thread
. v, u" B, p+ ?) L0 k
create_thread_to_handle_connection, // add_connection
* t$ `9 k' n, e; `+ @) S1 U
NULL, // thd_wait_begin
% p8 l6 E" w- I! u7 W8 E: V" a
NULL, // thd_wait_end
: `& Y$ q9 O a1 \* W) N5 g9 c
NULL, // post_kill_notification
3 D. u6 f' x R5 q
one_thread_per_connection_end, // end_thread
0 K k7 _( o3 Q4 S. ~$ f2 ~( \
NULL, // end
" U3 I) B# K2 N
};
! n* g/ y( d. a0 l- Y
; _8 j ~( V* r `% Y* }% c
0 e, i; o" G2 O$ F% M# k% a
从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
3 E' ^' I* F8 C" F' [ X6 `+ k
6 i R: S# l# i! k8 `8 G0 t( `
<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
) [7 X& ^% z) l
/ D5 u! O, K0 `6 N& T# S
void create_thread_to_handle_connection(THD *thd)
" g" K4 t! i$ X+ B7 M) ?9 r
{
0 B( \" `9 \$ [ O% ~7 ]- L, R
if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
) d3 {! U. q3 A. D( m
handle_one_connection,(void*) thd))){}
( `( K1 A8 @( T& E
}
( h4 u6 R5 C) W3 W& B
//触发回调函数 handle_one_connection
' w* _1 T2 B) s; p* S
pthread_handler_t handle_one_connection(void *arg)
" D# x( _( l4 X4 ` Z. F8 ]
{
2 w: r2 h, v2 `& f
do_handle_one_connection(thd);
7 N1 _7 L' e S$ b- I
}
8 K& Z, D& i0 n1 B
//继续处理
* d8 Q6 {$ Y T/ g: K2 i
void do_handle_one_connection(THD *thd_arg){
7 |% Z4 r F! S" h- ]& n
while (thd_is_connection_alive(thd))
; h! @" b8 c9 t( m3 a( C
{
" g4 X% ], }. P3 {' h% q) `" D5 `% l. M
mysql_audit_release(thd);
' E/ c- Q0 g: i
if (do_command(thd)) break; //这里的 do_command 继续处理
; A) _6 J w5 S5 Q. x6 q
}
2 w8 j9 r! h5 _6 {
}
2 H7 c! X! s! R( x0 V# J& D
//继续分发
9 \0 }5 ]9 Y7 z5 x! v
bool do_command(THD *thd)
0 ~, v) I5 V I- H @! O! p# a2 ^
{
; A2 `; h4 ^ r& i4 E
return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
0 P, `7 b6 Z1 g; B9 [
}
4 O6 U! U6 t. k# r* e
bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
1 t9 k/ ~: j% O/ X# ?
{
# G- M2 e: j( v& \# u0 E, Z
switch (command) {
6 r$ t6 P. k& d2 h
case COM_INIT_DB: .... break;
8 N* m* W7 G0 H6 |2 V( f* |
...
1 ?8 J' M, ]( V) [
case COM_QUERY: //查询语句: insert xxxx
; {* e4 F) u3 I# H" d k* w: N
mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析
! {5 o, p+ T% x+ \2 m5 ?& d5 z
break;
: \ I& v5 a$ E* W) r( N6 l1 L
}
" g1 D: W; _: r D" w v
}
) |: X+ l; y$ I/ i( M& o# R
//sql解析模块
9 L! B" u; L- j7 D. I6 F( d* R
void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
- T" I1 e6 I& Y
{
+ h3 s1 W+ W F
error= mysql_execute_command(thd);
2 ?# t( ]9 Y! j w( C; ^
}
2 b" Z* T6 v3 ?1 ]6 x* _' N1 ~. X
& s; M6 z! c" U5 }3 U e8 w7 c
( d8 ?; v% J F' x" X' Q
<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
* c* g; N c3 C8 M
, U+ U% s/ ]7 m/ Y5 J3 q( b
//继续执行
4 }3 q! Q. G6 ^( h& B
int mysql_execute_command(THD *thd)
& ~; w6 Y# }% B, D: \& [: n
{
$ V3 A+ e% Z$ u0 |& g. J* P, _' K% k# l2 ]
switch (lex->sql_command)
( _& Y& u2 Q% x7 J+ J6 H& U9 G) d2 {
{
) M4 _0 R+ }2 _& g, r# `
case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;
% T+ N9 u3 F2 ]: A8 u5 Q
% R( s" ^! u8 S `* z+ J
//这个 insert 就是我要追的
8 V8 W0 | h ^2 G+ K0 r
case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
+ [2 d! E: ]' w- F7 p7 |
lex->update_list, lex->value_list,
" [) s/ z( s; ^8 I
lex->duplicates, lex->ignore);
' A7 G3 }; X& t$ h0 L
}
- s5 [6 m- I$ y
}
" b" ^6 H# l# m" u; t! S3 B5 x* }
//insert插入操作处理
6 i6 u8 u& g7 A$ M5 n- `+ ^3 X$ r1 n
bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
- I5 |" {: p3 |. m5 P; `9 q
List<Item> &update_fields, List<Item> &update_values,
: w) T& B' f) Y2 p* h) i/ w
enum_duplicates duplic, bool ignore)
( h' i4 u) C1 X; }+ \* `
{
& b" p. [ {0 j0 T6 q
while ((values= its++))
- f, |! Z* }3 `, k6 H/ f* R8 k
{
# ~* g: ?# Z0 `9 ~8 L. J' n
error= write_record(thd, table, &info, &update);
; _) J, N4 C, u; u; k
}
$ r6 G9 F8 E& ?; ]. a
}
' P: K" N K6 q- ^" {, Y- L9 V
//写入记录
0 e9 ^- @; f" e( `
int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
) Q# [" ?$ `# `7 n k# ~
{
- o3 G4 f$ M" K) M+ l- k; s
if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
0 c% O6 S: l+ V+ W
{
' h, ~8 ]6 ?, V* j+ r+ {4 C$ u
// ha_write_row 重点是这个函数
- f0 K% G+ x) P7 B
while ((error=table->file->ha_write_row(table->record[0])))
4 l6 r+ q; j( T1 ~9 O+ T0 j* l
{
" t3 X7 P( ?/ h% R6 z
....
% [9 T6 t$ {' M5 @% t/ v; o3 X
}
3 n: W7 a1 E/ X: {/ G, t; W, A
}
4 D" }6 X0 x" Q/ B# ]4 h. l* `4 a
}
" P) V$ L; Q$ x9 F& E1 t
, u6 X. H" k) B; ?: o% ^
$ ]2 l% t' S1 }3 L: Y6 Q
% O* d1 q7 e7 C# s9 [
可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
& \% _1 [1 o [& m
+ }. l3 _) L- Q c! c4 C0 _
<3> 继续挖 ha_write_row
9 M7 P" a4 r6 {# t
; o( X! g7 L" N$ G
int handler::ha_write_row(uchar *buf)
/ v+ D w1 q9 S! L, ~$ ^- W y
{
4 ]+ q, v! z5 ~8 q. @4 ~' ?( Z
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
3 N6 P$ j. r7 E' ^
}
, \& ]- u5 K) F/ l0 Z% M9 G) Q+ S& M. o
" ~8 A: }6 P- l! U: f4 a
//这是一个虚方法
1 g5 p$ O6 t9 j
virtual int write_row(uchar *buf __attribute__((unused)))
% J6 J ~' `4 z9 F: v% P Y* s4 v m
{
+ E, x7 l d7 s" X5 n
return HA_ERR_WRONG_COMMAND;
0 F8 r& s( G3 k# o7 P4 S
}
+ s+ x1 u8 j8 e
[- r7 m3 P; E$ \
% `+ J/ @' [) D% `( O1 N3 I
看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁
! ^$ n- E, J) `2 _8 e+ u, N6 o
$ Q: U; M* B7 L5 K: R; E/ c+ P/ }
3. 调用链图
2 z0 P! K+ K' f* |$ ^; ?, I/ J
这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
7 O, U0 Y( Y+ j0 I+ {- c6 b) G9 A
8 ~4 U. n6 k b _* {1 a; V8 V3 r
) G3 c7 Z2 Z) }& S, j# M d( i
3 w7 D* A9 U7 h
三:总结
$ D* _& d& k% @$ I9 [" O
大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。
+ h, B& |3 Q+ |2 ?3 P
————————————————
' K$ ~# w3 Q5 T9 m0 F; [
版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
9 n* g: d/ n% Y/ |( f
原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
. p+ n" o/ E0 z$ J
欢迎光临 数学建模社区-数学中国 (http://www.madio.net/)
Powered by Discuz! X2.5