数学建模社区-数学中国
标题:
MySql轻松入门系列————第一站 从源码角度轻松认识mysql整体框架图
[打印本页]
作者:
zhangtt123
时间:
2020-6-3 10:36
标题:
MySql轻松入门系列————第一站 从源码角度轻松认识mysql整体框架图
一:背景
7 J( W' A' E0 _% y" G
1. 讲故事
4 H8 i- g5 |% C/ H
最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
' b/ L8 E, S# s# ^
) k+ c) i. ?7 f/ `8 c5 L0 A0 W% O
二:了解架构图
; k8 O' U3 y) e3 N4 J9 i
mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
- c6 n% ^3 b* o* D% G P+ c8 }
+ v, y$ s q# b8 t9 {
1. 从架构图入手
- ]6 s0 U6 A S$ a# l
大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
, _, O6 Q* x& I7 C- } C: T2 t8 N
) Q5 U( V* {6 _
0 E6 t* h# w: w8 C3 K& ~1 \8 n
" X/ M" K+ W3 I
其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
% Y" l" U$ | ~! a1 i
% J* I; i7 ^6 u) R0 E
2. 功能点介绍
$ p& N* J5 `6 `7 C+ x& r# D2 G
MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
2 t' a/ E5 y: r6 @* Q' r
1 Z# z# h( q8 [2 @
<1> Client
7 A0 M+ C( B) A+ z# g! |
不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
; g ?$ n" {5 `3 m5 J/ b- q
" z; I4 R" L) q' ~3 G/ U
<2> Connection/Thread Pool
7 q) w) Z7 E1 F4 h! N8 y
MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
3 j* D4 T2 z: C( k% l2 o2 \1 W
2 z R+ r5 t' ]' a- e
<3> SqlInterface,Parse,Optimizer,Cache
' L( D/ T! S7 D* u, N3 S+ }1 k
对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
. f6 J* {9 a9 p5 D6 w% K Z
8 P' a# t' Q5 D; @. ^
<4> Storage Engines
1 n: W" _ ~5 N/ V) I3 F7 Q/ I9 f
负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。
; ~! I& {; h, }
; f5 F) h/ h* ^( t' U; B
三: 源码分析
( }+ D, l% F: Z/ d
关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
% V, }: H4 v8 I( w3 J3 B
V$ [( N5 o0 ~" H" e
1. 了解mysql是如何启动监听的
3 a# n# \3 A; ~; {# m
手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
1 y& [" F" B- k1 y. }" u
* O. J' m" @$ \8 q
6 u! L! U' P7 A
9 M% U+ x6 ^% M; o
从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
2 N( T! v }6 f) C) a
2 O$ P6 N1 E7 Q. [5 V( F* V
<1> mysqld_main 入口函数 => sql/main.cc
7 [+ A7 ~7 @4 r
" I) M# \, j' z5 t5 f9 T1 U: {
* t3 l: d, `5 p* G. [1 v
extern int mysqld_main(int argc, char **argv);
+ ?- y" J, m6 x( |; D
- `: b3 g( @# a! h/ E
int main(int argc, char **argv)
- d4 s7 g+ P" H/ `
{
6 B; Y# r3 n7 U. J; [. K2 X
return mysqld_main(argc, argv);
4 z* I c6 ^1 I U+ C
}
) J- M U( s5 Z" t& k! U2 Z5 I- v
* x) v4 F! s% A5 N% i& u8 C, N
! n2 z0 `, i4 r$ W. a3 O$ B
这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
( O( p' W, H) ]( I7 n
& i& T, V5 |% A
<2> 创建监听
; g/ {1 g0 G, A, ~, g1 \( I
! Y4 c3 y6 M- ` C
% S6 u, ?" p3 s8 ]' H
int mysqld_main(int argc, char **argv)
7 z- \& C# Z8 G$ l4 ^# J
{
. M, g& i# @6 G* @
//创建服务监听线程
+ i/ Y2 _9 ^' V$ z
handle_connections_sockets();
+ w! ?' ^! l2 i- ^- f
}
+ ^# Q( U# z9 A |
4 s9 U" ~- G) U3 @
void handle_connections_sockets()
+ P' H+ a k+ u( Q7 U) W0 h: t
{
% o$ r% a' t1 J$ z/ n
//监听连接
" w1 h5 g% V) ]( q
new_sock= mysql_socket_accept(key_socket_client_connection, sock,
# W3 o# ?: L8 c2 M8 u% z3 F% B+ e5 F
(struct sockaddr *)(&cAddr), &length);
7 n8 E9 l2 ` s0 }$ _/ e2 t
$ z* o( M! x, b
if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
) I5 J/ f* }! t6 a
thd->security_ctx->set_host((char*) my_localhost);
! H( X5 p+ J- E) a
- d1 k" T4 V7 _$ x" V
//创建连接
7 d: `" f* m9 J- S/ L% ?
create_new_thread(thd);
5 g% X# U1 B. H& ^2 `8 U
}
, k- N( b! W) M% [
2 J5 P$ u/ q# S5 C5 Z1 G! N! R
//创建新线程处理处理用户连接
/ Q/ |' V! ?0 Q( k/ n4 M/ v
static void create_new_thread(THD *thd){
5 l6 V& e5 D+ l* L
0 ^0 e& S3 {$ f+ N$ r/ p% F
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
+ p/ }/ b' Y3 B7 j8 U- u6 L
0 {2 l2 M. Q- w7 |
//线程进了线程调度器
' T8 o, G+ D+ A- |( F# S
MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));
! Q) i7 e) D$ v% i
}
% a$ ?+ \# z% W4 _( _& U# m8 N$ F$ s/ X
& y. J+ G+ Z- V5 j5 z$ w
. d2 m: ~& _6 z' f( r4 Q) T" A
至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
7 C& R. }4 ?( U# v( L7 E9 @4 N
5 B/ J' D3 g! E# M' v
: q q+ G/ \6 z& R
2. 理解mysql是如何处理sql请求
- Z5 ]# {5 Z. `# j# c
这里我以Insert操作为例稍微解剖下处理流程:
U. g$ e7 `4 H
% n! \; O' j; B5 k
当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
# G5 s( G0 e c
! R3 C. B9 q( d; p& H+ n
1 _/ l! p. |3 {0 `
static scheduler_functions one_thread_per_connection_scheduler_functions=
5 y% I$ F9 H$ _: I
{
" S+ W0 R/ g$ I) e8 I
0, // max_threads
) h& N y' a# v! j3 H4 W
NULL, // init
1 ?) A* e* K- q9 |+ Z
init_new_connection_handler_thread, // init_new_connection_thread
9 @9 l4 [, j7 q' @8 M" A
create_thread_to_handle_connection, // add_connection
- n( P0 V5 ^8 c6 _$ _5 R
NULL, // thd_wait_begin
: S( @9 c' z) c' k2 b' e* ^$ l
NULL, // thd_wait_end
9 |! ]9 n) G {
NULL, // post_kill_notification
9 x: Z4 J& H1 R8 K+ Q' S
one_thread_per_connection_end, // end_thread
: }% Y- }9 G2 P
NULL, // end
% D: X1 m/ ~( b( ] X* R
};
9 \! x, H+ n$ N+ j0 K0 U& l5 ~
+ Z3 }( e* D* H3 {3 q6 V4 l3 M
& @$ e2 r. O) f8 E$ E. K$ @5 v
从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
% c& a. D! r, n3 c/ K
: X& U# v: t2 `
<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
# c# b7 p8 x5 Q8 U f; x# d
3 @. M T! T6 k8 H9 K6 P, y
void create_thread_to_handle_connection(THD *thd)
+ x$ ~4 S* k1 X8 A
{
( q" z/ B6 Z: d: T% `; Q8 X
if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
+ R+ I6 F% y# Y6 L
handle_one_connection,(void*) thd))){}
* p4 u9 O$ O! R0 B: c
}
% D. N, Y4 e6 @8 f) B+ v% q i7 p4 P
//触发回调函数 handle_one_connection
8 k6 d! r& Q: g- h! ^2 D
pthread_handler_t handle_one_connection(void *arg)
9 B% D2 S( u5 S4 `1 v q( r7 p
{
2 f; _# {3 H7 P. j
do_handle_one_connection(thd);
: R1 e! ?$ ~1 P C* J* W h& a
}
8 }. X7 l9 k X5 V3 u* ]# g/ {
//继续处理
( z, G7 ~5 d; G9 f5 x
void do_handle_one_connection(THD *thd_arg){
% ~4 l, a& l7 }" T& }7 o
while (thd_is_connection_alive(thd))
+ d% r5 c5 }# [6 w7 T
{
3 q2 [2 R# h5 \( [2 G" V# w
mysql_audit_release(thd);
" X8 t' R# ?4 ]/ y! f0 a. u# `9 n
if (do_command(thd)) break; //这里的 do_command 继续处理
( Y! b* a' Z$ _" Q2 n ]
}
7 m% I& H0 W; P# A% g* T3 v
}
2 m) a9 w5 x: c
//继续分发
" N2 H7 A! A) g1 V
bool do_command(THD *thd)
7 n5 S1 b1 x/ |9 y3 A
{
/ j8 }0 ^6 D w6 T
return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
' P9 ^4 u& `8 M$ B' z
}
- [ p0 c, G C
bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
' b+ P1 I0 Q6 Y( Z' j, Y/ h3 i
{
6 X* B8 o& V9 b$ N6 W7 f
switch (command) {
, A8 j$ _9 X4 F* T7 {) [4 W5 \
case COM_INIT_DB: .... break;
# u4 a$ x ]9 V4 r0 b( {0 \
...
- G1 Q. |% r+ w. f5 u+ ^7 O
case COM_QUERY: //查询语句: insert xxxx
/ a# z; y- u; S9 C
mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析
) R* m( f2 t* U1 n
break;
; p2 {: R& ?6 r U# I8 f$ w' d
}
) k$ X9 @$ @& G; b! V
}
1 z0 b: ~" g Q x# K
//sql解析模块
+ N$ I; H6 N, P4 Y1 ~3 d4 ]& N O
void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
2 a" n5 \$ O+ M2 R9 U& H$ I2 x( T7 o+ x
{
7 H/ \; m0 S0 d
error= mysql_execute_command(thd);
4 R1 I' _# u+ n; u) {0 ]
}
& l6 ~+ w$ m1 Z$ V- l1 V, V
/ u4 E4 k1 F8 ^
8 K9 N, @( g0 p" D- A7 N9 [9 |
<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
; d( v9 U3 ?$ V0 x. y0 ^; S. t6 [
6 U: o. ?: u7 q' a6 T7 A" _
//继续执行
+ f2 s, @2 L- I& j0 e n- f2 O0 {
int mysql_execute_command(THD *thd)
1 F# h- y5 T; }6 l8 d
{
- ?4 Z! l. _' E+ _- }* }
switch (lex->sql_command)
& `" v" \3 }0 m) U
{
4 h9 O$ |0 I {" F0 P
case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;
& y2 D" K( g; @, o& W
; ?5 `% g% C4 S
//这个 insert 就是我要追的
6 U' f a" t8 m) }; E) P
case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
: H: `4 P& E: c$ U( I9 c
lex->update_list, lex->value_list,
4 c7 W& E5 t% }7 e& a
lex->duplicates, lex->ignore);
, v" h- {' f( e4 y" j+ J9 e& S, @3 W
}
( U% k% v% C6 _ H. R
}
0 G Q4 S/ X! m6 n& E9 Q
//insert插入操作处理
+ D, t. F+ w) X* O2 i9 E) L
bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
- J: ~4 d0 m; M% B( N$ Y$ y1 B
List<Item> &update_fields, List<Item> &update_values,
" Y4 T1 h! h' ^9 @
enum_duplicates duplic, bool ignore)
8 X, R5 n9 X/ o H8 l- \: k7 V
{
+ a, w5 F9 a* r" p& S" A
while ((values= its++))
6 k3 x4 c8 Q9 O
{
8 w n1 `7 x# x6 m8 a
error= write_record(thd, table, &info, &update);
0 ~8 B' ]' @7 k3 ]$ a! ^6 q2 u; H
}
M; U' ~8 B4 ^2 F% U
}
0 a" s2 _( K* A2 B: h+ F
//写入记录
# k0 G& y( s! S3 C. s
int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
" Q A/ _+ R" C6 e1 o
{
! U& r* D( t7 o* d( J
if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
3 ?4 l' b8 V+ K$ y; c% h6 a H2 z' q" }
{
+ B. A3 n o; y. a! m
// ha_write_row 重点是这个函数
7 O: v2 N' ~& g# U# p- d1 g6 P' Y
while ((error=table->file->ha_write_row(table->record[0])))
# ^+ `+ O% J8 N+ |" V$ I
{
2 M2 F" V# u. _
....
* w8 h6 H: Y7 c' a8 ]
}
" M; D6 ?! ~: t+ @$ b% x
}
4 Z/ T" r) K0 k+ U+ X! k
}
5 B. I1 |+ K; k0 b
1 V+ m- b1 ]9 [6 |' B R" L- q
! ?8 v- f" W) t, \ B6 q
; k% g( n2 b, F+ j/ G0 D
可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
2 P/ D/ i( d) v: B9 k( v y# G
+ s. M7 @3 t" c' L% x% u8 }( T
<3> 继续挖 ha_write_row
4 W' u; W7 l3 W: [7 u" q0 b( q' |3 C
: w& i) k1 T* Z2 g
int handler::ha_write_row(uchar *buf)
( t% H) h! \ r5 A" Z3 E: s
{
9 H. H3 N! ?- _5 G1 H+ }# p
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
! W8 b u3 ]; b, W2 m
}
: r8 z7 M9 C, v
2 H" p5 n0 @/ S+ i
//这是一个虚方法
4 d, R& ^2 U$ t! r
virtual int write_row(uchar *buf __attribute__((unused)))
" f& a$ G& U) P
{
( B" @+ F: C) }$ `
return HA_ERR_WRONG_COMMAND;
6 c6 N7 y) ]( g; ]+ w) B: j
}
3 w8 z7 [9 z( |
* \ ]- r$ M( T9 T' w4 \6 m
& T: n2 ^2 I" R6 q3 k. v! D
看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁
5 G- `3 P/ ]3 ?" D" m" _
, E. H/ F9 [' K5 P7 L% [, @
3. 调用链图
; y: t/ d& h8 x0 R4 o
这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
- H$ W' E: G- y7 p2 P5 U, e; w
( @6 ]! g8 @& S* e$ v2 K. S
4 O1 [2 G$ P& C
, L- E( Y/ O( ?2 ]+ p
三:总结
) r* c+ j, J! p8 j1 } I
大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。
/ Z- d: j: ?+ a1 u5 w5 x- F
————————————————
1 c0 A& @8 O# k' L' _1 X
版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
3 [4 G1 }5 a+ L! a
原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
2 |2 C$ t3 P2 `# n7 L3 Y2 i W
欢迎光临 数学建模社区-数学中国 (http://www.madio.net/)
Powered by Discuz! X2.5