数学建模社区-数学中国

标题: 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$ W6 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. |. w4 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) {. [! K2. 功能点介绍
  r% i9 B4 K! h/ cMySql其实就两大块,一块是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+ aMySql使用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
负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。
% \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手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。& V; [- p  }  b  A" K. g

2 U5 B# r+ F2 D' Q$ |
7 l- q1 M+ ^' L+ t( J6 R3 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.cc4 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 Tvoid 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/ Zstatic 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_notification6 f2 T7 }/ n* H, G- T( ?
  one_thread_per_connection_end,         // end_thread; o- }" ^3 `8 F8 D  Z2 h
  NULL,                                  // end6 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 Svoid 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_connection2 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; f0 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  Kbool 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) dint 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的哈。&#128513;&#128513;&#128513;
6 Y5 \" n4 U3 t' M* P
* f" g6 D: q$ _1 J5 ?! U& a3. 调用链图
' d7 |3 {# c$ S4 H这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
( Q/ \3 K/ Q) I4 P4 G6 c3 W9 ~# ^( Y9 f7 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/1064874155 r3 `/ H$ V5 b  M





欢迎光临 数学建模社区-数学中国 (http://www.madio.net/) Powered by Discuz! X2.5