数学建模社区-数学中国
标题:
MySql轻松入门系列————第一站 从源码角度轻松认识mysql整体框架图
[打印本页]
作者:
zhangtt123
时间:
2020-6-3 10:36
标题:
MySql轻松入门系列————第一站 从源码角度轻松认识mysql整体框架图
一:背景
. f$ V. O) U9 e' @. b4 [0 y; ?
1. 讲故事
/ `" Z4 N( o! b N' u/ V& d
最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
' o8 |2 I( {, N+ W2 \. c$ W
6 g; J% }7 s& z: Q% _* B
二:了解架构图
0 L3 V0 q. p$ C( _8 R" v7 m3 |
mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
% J0 D$ n. ?7 o$ L. |. w
4 K2 ^" X, h& R6 P, I" j
1. 从架构图入手
9 F' c8 C) I: \+ E% K6 b7 I+ d0 H0 c
大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
# L& U- j* p% @1 ]7 }
. O) {5 Z, d4 t) t' Q
% Z) H6 s6 O7 p! S5 s: m5 C/ q
S7 X' `1 u& v9 ~! _
其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
* c. u; {! X) R
( f- O. T) {. [! K
2. 功能点介绍
r% i9 B4 K! h/ c
MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
8 w" }) E7 G2 u m& g9 }
2 d7 _& g( y! u- @- B
<1> Client
* s) V+ y1 r; t) {2 g' R
不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
, |5 V: O ^1 [- T: m8 h
& z7 n0 ~* N' i4 R! O" D; p
<2> Connection/Thread Pool
" e) K1 L, {! i+ a
MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
& I# |! D- G$ X$ A0 R' s
9 c2 v' f" S! p- M( d; U: n/ i
<3> SqlInterface,Parse,Optimizer,Cache
- Z5 w, [3 G+ W3 L; |3 z
对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
5 ^$ }9 Y" X% E( V( ^0 M
( k% \/ h5 F Q# u0 y
<4> Storage Engines
. {2 h, c/ S' _6 J: T
负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。
% \0 r# i/ S& T+ H6 j( ?0 x& ]: Z
& s& r' W z) O
三: 源码分析
+ n+ ?$ K. {8 w* O4 g8 x
关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
& |! N, y+ [7 C6 w
. F0 U3 T, H4 Q& _9 X
1. 了解mysql是如何启动监听的
, P7 M% @2 `! m5 j& S
手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
& V; [- p } b A" K. g
2 U5 B# r+ F2 D' Q$ |
7 l- q1 M+ ^' L+ t( J6 R
3 H% l' z. j, z1 |4 {
从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
5 Y u$ D& K+ }
) n- K- ?3 ?3 y* j4 x. }
<1> mysqld_main 入口函数 => sql/main.cc
4 C5 \% H) Y" V g, D' i
) A4 S2 X* i8 v
6 e, L1 K9 I, x& ~
extern int mysqld_main(int argc, char **argv);
6 m( ^ R4 k+ _0 y
7 M2 X$ G" J! c0 o
int main(int argc, char **argv)
( l0 u C9 i- d- c; R7 Z4 a' T1 u
{
0 s; H+ J: G0 S9 ]# p
return mysqld_main(argc, argv);
$ I$ z7 @ z8 v- V' }2 B
}
. A/ E' c6 h- _, s, `6 ]; b& U
# b# O- {% Z5 t: V: ^, U
+ }. x9 L! ^2 Z" j* v" }( _
这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
, B7 ~$ i: D8 S+ E" _
+ {9 g8 z5 S3 _6 _: L7 `& E7 Z/ h
<2> 创建监听
6 f4 n$ k8 ^* u( o
% r& P! j( @% A Z" w9 ~8 T
3 I9 H2 z" K4 D8 t
int mysqld_main(int argc, char **argv)
! @( O4 x' ^; o3 G
{
1 M0 ?7 A; E8 v* O% J: y1 _& s
//创建服务监听线程
# Q) B( |9 U5 M& d) O
handle_connections_sockets();
: M' f* R4 ?+ G: j4 C9 T8 j Y- M
}
2 \: C1 A0 n& \1 L
) P; k6 H" R, |, y( S5 T
void handle_connections_sockets()
8 ^4 U3 \5 ]2 M% v( w( i
{
' s# Y+ J: i( g+ D7 }9 C. U
//监听连接
$ c6 y, [$ O" k& ^
new_sock= mysql_socket_accept(key_socket_client_connection, sock,
5 H0 i8 f# V$ ]2 ]( W# @3 w
(struct sockaddr *)(&cAddr), &length);
. V3 k- c5 q4 A8 _. K9 j
b L1 N/ \0 z9 x3 ?/ T9 M! A% [5 _
if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
6 a3 l h. }: e
thd->security_ctx->set_host((char*) my_localhost);
! R" U3 |7 j) G( G9 J; B s P
+ P/ u" ]. X" v# X
//创建连接
+ A0 V P" q& B" q; j; k
create_new_thread(thd);
5 C! x3 v1 G/ x* T- y2 y
}
' E6 f$ {. E+ d' P9 t9 B& c
" E5 s; R7 i! h9 h( O3 N
//创建新线程处理处理用户连接
' |! C/ [+ r U$ s* {3 {
static void create_new_thread(THD *thd){
i: R8 l- e7 I+ ]1 ~9 v8 X
/ b( ]6 B: F: c, D& Y
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
# l4 b8 Q2 Q4 o3 ~: v
) J+ F3 Q% z9 c* U
//线程进了线程调度器
! I3 E1 J6 t" i$ s+ \8 n
MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));
' K R# k- D# ~1 y: w+ c
}
2 b7 |9 [) |0 ~. d) n" P8 h+ ?
/ d# Q1 U% Z/ C- n& j
' T5 n" m! y3 U B; p; a
至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
, k7 x+ l; P6 z! z: R1 O
7 c1 ]8 M. {4 U. t+ q7 h1 y
]- c% I' Y4 {0 P0 b
2. 理解mysql是如何处理sql请求
* J: ?& y& I U* V G: R- H7 ~' f
这里我以Insert操作为例稍微解剖下处理流程:
9 b) _, B4 g' U
# e% M% i2 L8 o' `0 }' @" ~
当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
: A _$ p: r1 y
+ m* i% W7 P: H$ ^( W
) S+ ~4 z. b6 U6 Q# H/ Z
static scheduler_functions one_thread_per_connection_scheduler_functions=
8 v) Q) }* H' n% Z: g+ O
{
: @% |% w) f* V3 ?; \$ p, Z
0, // max_threads
& Y% F* w6 K: B
NULL, // init
! A" n5 ?6 K) }' {" y
init_new_connection_handler_thread, // init_new_connection_thread
2 E G% D5 z& \" Y/ `
create_thread_to_handle_connection, // add_connection
5 _ ]) q. u) v/ \% C0 i$ Y2 X0 q
NULL, // thd_wait_begin
- n# ?( M, ]4 B' L( A8 L) ]
NULL, // thd_wait_end
$ o' M# F& m0 a0 v
NULL, // post_kill_notification
6 f2 T7 }/ n* H, G- T( ?
one_thread_per_connection_end, // end_thread
; o- }" ^3 `8 F8 D Z2 h
NULL, // end
6 q' p) A+ E* E* d2 u1 W0 W
};
- N: Q/ u/ J# m
, r0 l8 t* g4 P
# X! g7 p, c+ Z' e8 r
从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
4 Z# Q' ~) r3 W" I2 ?+ S3 _) ?
. \+ J! ]$ |! f8 U* E* C) n
<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
; y( ?$ m2 \8 O4 d
8 B# |! X! Q- H! s. f9 y6 S
void create_thread_to_handle_connection(THD *thd)
" x2 l! _4 C, q4 [: B/ H
{
* g$ {- `, u% O' ^8 r' _
if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
' i. P) `. a T2 c
handle_one_connection,(void*) thd))){}
& S" o0 t) U6 }: f# V, i+ n" Z
}
( }. L. l6 s, f" N# I4 M
//触发回调函数 handle_one_connection
2 z+ E' p. m5 ^- k+ Q1 W4 b# N
pthread_handler_t handle_one_connection(void *arg)
! c, D0 o9 N) j) z6 w6 l
{
* z) {7 ]) |; g
do_handle_one_connection(thd);
: T) D6 @8 y3 P3 p
}
! s. Y7 {5 H& T7 K: r: L
//继续处理
$ Y( @: s5 N" `8 h
void do_handle_one_connection(THD *thd_arg){
( F0 B T+ u1 Y( M( ]
while (thd_is_connection_alive(thd))
7 [! N) a0 d% \ x' o) A6 u
{
$ X8 F( P7 m; u9 p0 B
mysql_audit_release(thd);
7 ?8 V$ U* b$ W: Y8 i
if (do_command(thd)) break; //这里的 do_command 继续处理
% Z9 b6 @2 N5 |0 _
}
4 W- { F/ \% ?9 d$ {' d7 J9 h5 s
}
9 V3 Y ?- M( `$ n# l7 F: f
//继续分发
$ i! H2 K X2 _6 M2 R# _
bool do_command(THD *thd)
: |/ O0 X, y1 F/ K
{
. M" Y8 ]; m1 f
return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
0 i5 J/ x/ H1 Y
}
; t6 l* ^3 u, e7 @% I: Q
bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
' K7 {' \" v& y# b! `9 ~, @0 H
{
; Q$ o. n) \: V p! r- H& s0 U1 o
switch (command) {
4 F" a% |, w* k! @
case COM_INIT_DB: .... break;
+ ~1 K8 Y( }8 t
...
, T, j' v- F O
case COM_QUERY: //查询语句: insert xxxx
2 c9 u' I4 K+ f8 ]6 w& ]' a( I" g
mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析
n9 ]2 @6 b8 q
break;
y7 G8 z* v5 d( `
}
2 o/ J3 `% s$ ~6 F# h- l6 n) O+ t
}
X- C; z& }) d1 }% T3 \1 p% i
//sql解析模块
5 z" d3 a/ [% ~3 A9 T( \7 i. }
void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
# M: u. v6 y- s! P% C
{
5 `8 t, ?9 n0 N# \7 G9 k" x
error= mysql_execute_command(thd);
$ M- |# _) Y' e/ Y6 z( i& _
}
3 ? L# h+ D4 r( g
" m4 d; r7 q9 Q2 d$ q7 q1 f, V; f
0 Y$ S6 f1 G4 w3 L8 @, ?- j% E% T
<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
) ]4 w4 M6 {& v
) p3 |% f. m+ `8 h/ s
//继续执行
& @( ]4 L! v9 k& Y: V
int mysql_execute_command(THD *thd)
" n) L# t G% d9 T) N( V& _( e1 `
{
0 ~. N. N$ F* W' c/ g, W! A2 x
switch (lex->sql_command)
* U1 v' G: O( [0 T% r
{
8 H7 B+ p: Q( ?
case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;
M6 s* H, K+ [! r: H
, a+ O5 }8 s9 ]! h: _, _6 y0 B
//这个 insert 就是我要追的
: K! P, Q6 v- l1 f
case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
: U n% \' f9 h6 ~) I
lex->update_list, lex->value_list,
1 {! n9 E+ b4 \6 ]: u' \
lex->duplicates, lex->ignore);
8 {8 c( }9 X+ ]
}
1 f) P' O) |* W9 U) y+ H4 b m" _2 M
}
! m$ |# n# p- {- r; _) a
//insert插入操作处理
- e* R! ^5 z% ^& v% s K
bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
! C+ U) S" d, R3 X( H
List<Item> &update_fields, List<Item> &update_values,
' ]4 a& L2 s( C9 M
enum_duplicates duplic, bool ignore)
8 [4 k- o8 n: k% r6 n2 A6 B6 J
{
9 I) q9 q, k; O+ t6 B: e
while ((values= its++))
' `) T! o2 Q. R4 H% ?
{
5 Z; a) m6 ^; g. l9 ^9 {
error= write_record(thd, table, &info, &update);
2 P6 \: {& g9 N- y: u
}
( D& s# x9 V1 q) C7 q3 R0 Y/ M
}
' f$ G" w/ [" c) k ~+ Z( d) L p
//写入记录
2 o3 P5 O+ C& v3 x" u9 H0 `
int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
* M2 B5 E7 R R# J( }3 X# x0 Q
{
" ~3 F, u& u2 e4 y+ g/ w/ c2 ~( H9 E
if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
( J, B" n: |- g7 w
{
, ]+ j U5 I, W5 U+ f3 n/ `9 G' S, s
// ha_write_row 重点是这个函数
0 x" O a3 c1 ]! Y
while ((error=table->file->ha_write_row(table->record[0])))
5 t+ `, ~) @1 M' d
{
d$ |5 k1 ~9 m/ m5 V! O7 l
....
9 O6 N# E ^9 L, C5 l% X% t$ i4 c
}
9 m9 L2 X; k, B d1 m
}
/ |2 L1 I+ N# Z
}
$ _% g2 U5 M8 B- y+ D
' `& @. \9 P6 B; |1 j' A9 V
- C) W6 U9 {! T) |, H3 v! i4 _
2 Q! _& h& P3 |4 j% C
可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
+ R# A0 F9 E. Q; X5 {
8 \) M, ^4 H# H* \# z) a0 y
<3> 继续挖 ha_write_row
, G/ _ [& y, S+ U: T# L' X
9 I. O5 x! q) d
int handler::ha_write_row(uchar *buf)
8 h) a9 R4 [# |1 p
{
: _' L, ]0 G0 G& d8 u4 |% x: d$ R
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
6 D O7 J2 Z+ u% X8 c) p) w+ b, E) ~8 ]
}
/ K. X" c8 i2 z& x
% d1 k0 A- t$ s) P& @0 w. g
//这是一个虚方法
3 \$ @% X7 N4 _. K& }- K
virtual int write_row(uchar *buf __attribute__((unused)))
6 {4 d" E; e: }
{
) h7 f6 B, |- v* Y" Y
return HA_ERR_WRONG_COMMAND;
" m. d5 v& A, X+ J7 I3 k' f; w2 v
}
) @) ^- h# j! y$ a# _# V. \8 p
! F3 ~# f8 l C- P# T. k
! @% }' u0 E- q/ I
看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁
6 Y5 \" n4 U3 t' M* P
* f" g6 D: q$ _1 J5 ?! U& a
3. 调用链图
' d7 |3 {# c$ S4 H
这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
( Q/ \3 K/ Q) I4 P4 G6 c3 W9 ~# ^( Y9 f
7 c S! s0 q1 K, r3 q6 {
$ Z: u% l3 ^, j ]+ w8 _& H+ {! b
2 e9 i j0 ^$ L& S
三:总结
# {" O% f& g7 i4 C7 ~
大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。
$ H* ?: N; h( ~ |+ s
————————————————
0 G0 k$ I+ }$ o
版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
0 G9 O1 O" q' d: n
原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
5 r3 `/ H$ V5 b M
欢迎光临 数学建模社区-数学中国 (http://www.madio.net/)
Powered by Discuz! X2.5