数学建模社区-数学中国
标题:
MySql轻松入门系列————第一站 从源码角度轻松认识mysql整体框架图
[打印本页]
作者:
zhangtt123
时间:
2020-6-3 10:36
标题:
MySql轻松入门系列————第一站 从源码角度轻松认识mysql整体框架图
一:背景
( ?7 e% D9 }. ]- v9 A3 _* C
1. 讲故事
8 l6 h, C( C* z, U
最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
4 ^; ?5 J; I4 H3 d% c; `
+ r, l; k3 a+ ?1 k; n
二:了解架构图
- y5 g8 r5 V" F+ o! A
mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
+ J$ O" @5 x# M8 a" \ Q/ J% ?
! V7 e+ u! U z& q2 L# a
1. 从架构图入手
$ p# b. E$ U3 u+ J2 ]/ K5 S+ i
大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
2 J; L: S0 p/ v
1 d9 `8 S8 \+ N* L4 B: O
9 n& P, A+ E- c% W
7 K: o4 L3 k0 t& L6 v1 f9 l/ A
其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
3 l* f7 f1 C/ l0 V8 {
+ G) _; w" `+ f9 O# \0 w) i
2. 功能点介绍
( I* x3 U5 {+ t+ R; L& m
MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
/ P. l2 U" k t
1 n6 \. i6 w* ]& H# J( o- k; `- I& n
<1> Client
& S( Q1 E( y% G. v
不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
7 b2 [. T# c d( x) Z, P; F; w
* u4 T% I1 R+ Z" M& q' v5 b( k, B
<2> Connection/Thread Pool
) |; q& W5 O) B+ n$ x" M% }' m$ r
MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
9 `8 V- `$ Y" O3 C: T7 u
9 u. T' G( a3 w) m( @3 k
<3> SqlInterface,Parse,Optimizer,Cache
; W+ Q; v3 e2 H% z
对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
8 J1 X+ c, A9 I
3 v6 y- p8 [7 d
<4> Storage Engines
" ~# C' @9 D4 ?( } _3 Q
负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。
4 O" ?3 X! x1 g; I
: q, y: m) I: |7 `; G: H; S7 I/ H4 U
三: 源码分析
+ ^9 ~- E1 Q `- ~( E7 |' X
关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
1 m8 V* g4 u2 a( s7 y+ D' \0 d
; b$ t( _: T* {6 e; _7 _' o
1. 了解mysql是如何启动监听的
) z3 J0 k1 \+ @2 b3 j
手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
( t- x* y7 x7 l8 d. b8 z0 M/ B
T; p. W/ m8 N3 x4 B+ ~7 ` y
1 n& f+ ]% x, D9 Q- J7 Q8 D
0 I1 Z& v# H5 H$ a s% ~# x
从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
% j/ c# R1 O1 X7 F
. @6 ^1 j5 b, E0 E/ M
<1> mysqld_main 入口函数 => sql/main.cc
6 J4 P4 y+ V5 C& P2 O% \
. }6 W& P; _' l1 ]' e4 }# ]
0 a( p" i& i' J( d0 M
extern int mysqld_main(int argc, char **argv);
* r9 v8 U) [8 N3 p1 K6 h/ q0 A
, i0 S* C, L; Q s1 U5 I4 {( R
int main(int argc, char **argv)
; O/ x+ A1 _, h, w! P
{
, z/ V( q# e7 f4 f& M1 u2 I
return mysqld_main(argc, argv);
. e \* \6 w5 Z3 ~+ X& ^) c8 F
}
5 \' Y7 p. `3 H) Y( J. N/ y* U
) V; }* b' d; a K
* j! J% p* J: w( ^2 @
这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
; P* A4 d: v% H/ X* l
0 |+ ^0 f) e7 Z# d5 m7 [ J9 E
<2> 创建监听
. s' S+ t$ K9 T1 v1 {+ F' A
, j( G" Y) K' o# U, h
& P- J; \( V) a+ {5 W0 s& K T
int mysqld_main(int argc, char **argv)
$ O6 I, e' z% H' t! s. w
{
: K9 k, d$ R/ m9 u: o- q2 V" l5 B
//创建服务监听线程
( c3 F* g$ U/ Z' g( w6 V0 B
handle_connections_sockets();
% {( A+ X8 _* ~ R' K% t4 E
}
; |$ [/ J1 p4 o! d! \! y0 f9 [
. N; K' |. N3 K y1 _% L/ T9 m/ j
void handle_connections_sockets()
2 T5 a3 @$ _4 k* v0 _4 K8 h
{
2 I1 O" M6 k- A6 u
//监听连接
! g+ {3 o6 b: I/ F* {# |$ Q+ U
new_sock= mysql_socket_accept(key_socket_client_connection, sock,
5 c. }# @$ ]/ Q2 s U1 _1 E
(struct sockaddr *)(&cAddr), &length);
V0 B% X1 R+ ]7 T: F$ \
3 K* {' [) o' l, e. A! v$ b
if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
, f; s3 P3 d( Q$ h+ P+ z x' i
thd->security_ctx->set_host((char*) my_localhost);
2 _2 l; b& h' s9 z, c
1 Q$ ^$ d0 }+ f( v, }! Z( B
//创建连接
) h0 O: n8 m @( C" a% s8 |7 P& C! Y
create_new_thread(thd);
6 C7 h0 B' L& _ D' ^( X* G/ l9 E
}
0 D; X$ r& \, N
# @; u, T* y) R7 I- s
//创建新线程处理处理用户连接
( R- D% I) t' ?0 c4 _
static void create_new_thread(THD *thd){
" ^0 i) w& K" O9 t
1 r; C1 j* b l6 D+ m
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
k0 d M( ]5 ]( y/ r
* R$ ~" }3 i" }/ C5 w& E6 b1 ~
//线程进了线程调度器
8 r2 l6 k2 `% I0 g/ I% n( J s. h
MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));
( i w( [ q! s, ~8 W7 w U: y) G
}
r, m, d7 V" D. b* a
' {4 v3 y, k. [4 ~
; i+ S8 P# X8 p- f b7 @5 Q
至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
L0 p! j6 G. Z: ~' f! c) z
# k% [5 a. m. {# Q8 ^2 S7 X
# N" n5 ?' ?- E* V: G
2. 理解mysql是如何处理sql请求
: S5 c8 _& n7 e. Q9 W7 B
这里我以Insert操作为例稍微解剖下处理流程:
5 ]2 y5 L8 F5 F1 x- _
5 P" V; U# r/ i+ W/ l) d
当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
7 ^1 j* B8 ^7 I# T% d: d, n
' X4 N6 m2 q3 N6 p
& O: c3 c7 |4 t; @5 J6 ?5 v
static scheduler_functions one_thread_per_connection_scheduler_functions=
. |$ J* ?8 B3 B* J
{
! g$ ~3 W6 t1 |5 r B: A
0, // max_threads
3 { T: v( w* X+ q0 [( [4 ?
NULL, // init
4 q- b4 V& a+ m
init_new_connection_handler_thread, // init_new_connection_thread
5 U5 s, e4 v0 e( s4 }; A: g9 ^9 h/ m
create_thread_to_handle_connection, // add_connection
7 ~; j/ S% G5 O" R) c+ W2 w
NULL, // thd_wait_begin
$ U D: {' u6 x/ t0 u% K
NULL, // thd_wait_end
7 c- |; _. m! @
NULL, // post_kill_notification
. [+ ]$ H: O) C: i: R _! Q7 z
one_thread_per_connection_end, // end_thread
. }% X, F8 d$ \1 P6 t7 y$ F
NULL, // end
1 v- z5 |! c) u D. a3 i+ F" s
};
7 q# e% }- L- j H1 `5 Q% k# d
, X6 D' D8 }/ t N$ ]' G' p w
% `3 m. R/ f) v- E
从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
' E c/ @* r' p; p9 h
& E0 Q2 |$ p; q m1 f4 ^
<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
* Y2 P d2 ]/ K! T: B! {; a; M$ W4 z
- a0 `/ y+ w. D+ W6 [6 }
void create_thread_to_handle_connection(THD *thd)
% Q# a! ^) i! j# ^6 \- s
{
3 A; e1 t% e. S+ V ]# e
if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
6 k5 z2 {: |6 F+ W: A+ ^; X
handle_one_connection,(void*) thd))){}
' v3 }1 {) M' }" y9 H. g
}
+ l H( j7 M3 h( d" B
//触发回调函数 handle_one_connection
! [* l1 h. t( d
pthread_handler_t handle_one_connection(void *arg)
- n4 N7 i% X z5 I
{
% d' T- y# t7 q. h7 a9 P* j# d
do_handle_one_connection(thd);
# G$ ]- ] _6 h9 d
}
' v2 j3 y% M1 N
//继续处理
& G1 u# W: w; a- g; z1 M
void do_handle_one_connection(THD *thd_arg){
3 h( f# V: ]' y5 I* _6 i j1 T. O5 U
while (thd_is_connection_alive(thd))
! ?3 { E5 G2 t! h
{
! I( d3 r) L4 G n
mysql_audit_release(thd);
& H# V" `/ `/ N+ x
if (do_command(thd)) break; //这里的 do_command 继续处理
% w' ^# `, F' o/ U; j9 `
}
* B, ` l' m$ M9 J
}
. e$ ~# A* v( C9 F
//继续分发
8 @* I; F0 _0 l; x; V- }( l% M
bool do_command(THD *thd)
6 h% T1 M! x0 [* _& ?; w7 y
{
! h. a9 L, g' a
return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
7 K9 w6 z2 I% K1 p& `# s/ e) W* w
}
0 b3 D2 X5 z" i( \* m* [
bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
3 E4 M; Q' r6 R
{
' H" ~! v* A1 @5 C
switch (command) {
. ]9 T$ r, F* z/ v# ]
case COM_INIT_DB: .... break;
, J' V! c1 x# l/ v- r2 K
...
! r7 K6 @% ^; ]& l3 R2 I1 ?
case COM_QUERY: //查询语句: insert xxxx
( W e W c7 u' i/ G
mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析
`; a7 @4 ^5 O! ~0 H3 P* n- q
break;
; S6 ~2 y0 w Q$ w3 ]. {
}
6 x( O( ]* g5 {& A; X; S
}
0 p6 L7 B% p9 K$ n
//sql解析模块
5 D. ~0 I6 R$ P; [! j! J
void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
" H. m' c( X# q
{
8 Z1 h8 K+ w8 q3 B' d( m0 P
error= mysql_execute_command(thd);
9 J; g9 [: L4 O: c8 Y. ^: [3 z. U
}
) a2 B) O" k7 P
/ W! m: r% L/ ]7 m; ]
' k5 @: N& V- j, d$ m8 [9 y# {, N
<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
3 c4 G, ?' ?9 x; D+ V" V
) Z6 T& y. G$ ]9 j* s) L* Z7 ~; F5 j; r
//继续执行
" ?8 _# N# ?3 k2 U7 o5 l
int mysql_execute_command(THD *thd)
5 t9 {) |2 z6 x6 `% k+ r' j* X
{
" m) y) \; t- g/ L
switch (lex->sql_command)
( D/ p0 X+ S9 ~/ v1 Y
{
! C/ h4 }0 w' r6 V( e8 ^ g
case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;
6 D) Y" N; B. d' f! |% K2 c& X
1 U9 B- j. ^, c
//这个 insert 就是我要追的
3 i7 F2 [3 a8 i6 U
case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
, \1 O: R" w$ R, } d H
lex->update_list, lex->value_list,
E4 t7 D% C8 ~
lex->duplicates, lex->ignore);
/ B3 p, O& {2 U1 ?* }7 @: r9 \/ h i' s
}
; a$ `6 J8 }, ^& g* `
}
2 @4 x, m( P0 k- y0 y1 g' W
//insert插入操作处理
4 x8 i- Z8 T9 f
bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
7 S- w3 A; K' y1 \1 Q( G
List<Item> &update_fields, List<Item> &update_values,
1 p A: K% F' n# O" ]
enum_duplicates duplic, bool ignore)
[- F3 m" N$ {' U# a8 I( f
{
* P# A4 a# O# r
while ((values= its++))
: l7 P4 _: N: Z* ]7 p2 a
{
( J0 E. \+ d% v
error= write_record(thd, table, &info, &update);
: ~: k+ k" U4 t; z- N& {
}
* ?- E8 }! T6 l# x8 |" V# q
}
/ u& V7 k. O1 [3 E" Z
//写入记录
2 c+ y% f: t/ s
int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
; w! |; \( Q) O$ e( R# I! u
{
3 q. ?9 ~! b4 m# Z! s
if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
- n) C- y! C3 @: D) P$ V3 \7 V
{
2 k: Z- Q8 O# [ `9 Y4 K5 D
// ha_write_row 重点是这个函数
7 o0 r+ N' s1 _ v6 a
while ((error=table->file->ha_write_row(table->record[0])))
4 t$ p' r1 u0 ?, a
{
# w3 \/ p) {: e2 Q3 U. U! p, g
....
5 b' K. P1 v) U" w c8 G1 v
}
$ h$ e1 N8 U z1 W* t8 u- k
}
8 _; Q, R7 L" S J. ~% U# |/ e
}
8 J( F" \4 A4 \% p9 v \' n6 u% K
) F0 l% C- n% n
* n. A) @. ^4 w9 {: ]( b
( r# ^" J* q# P" v' ~' O
可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
+ s, F2 W( V! W' b/ L( Y
L1 K4 Y+ L0 h3 E5 h" |+ |
<3> 继续挖 ha_write_row
( o3 T$ b# C8 a4 Q8 h+ ~
- d! }+ ?( t4 o$ E/ P( k9 B1 F
int handler::ha_write_row(uchar *buf)
4 K: W! [0 n8 _) W) I- J
{
( F/ R* K$ Q# E) s* w( t0 ~
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
/ C+ Z( }! g/ A3 s5 N
}
" z: F+ s' U o3 C% Q. F' ~8 g
$ D h. ?6 X; @( V' \! {- l' D
//这是一个虚方法
% Y: e9 K% {+ s; @
virtual int write_row(uchar *buf __attribute__((unused)))
, Z* V* o+ y7 i+ N) \1 C9 C
{
, T2 _ W4 ~( D7 |5 M' F8 K9 R5 k3 k: ^0 R* B
return HA_ERR_WRONG_COMMAND;
: P1 |2 f& J6 k
}
4 ?; ]+ P: _5 {, P0 V% A9 z9 g v
+ u6 W8 Z# g' }! o3 E- u: @7 P; S
9 W& q( A, Z3 B- g3 F
看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁
1 D: r5 f8 p& y& T8 U- C0 }+ U
4 s7 o- C7 R- f
3. 调用链图
5 R: R. m7 y: X- l2 T
这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
: U |0 K8 E, C; t2 F
7 C1 |/ n3 O( y& `/ K0 [
/ _0 }6 _# v. G8 T/ b' [
: Z9 R: R0 q- W
三:总结
p4 ^% H0 E' W& a
大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。
6 \% b, P/ y( y6 c* M
————————————————
% k h% ~. U& Z# `' H
版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
* [& |7 \+ n W" b! U
原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
; T0 X2 k' C, M. M" h
欢迎光临 数学建模社区-数学中国 (http://www.madio.net/)
Powered by Discuz! X2.5