QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3621|回复: 0
打印 上一主题 下一主题

[个人总经验] MySql轻松入门系列————第一站 从源码角度轻松认识mysql整体框架图

[复制链接]
字体大小: 正常 放大

326

主题

32

听众

1万

积分

  • TA的每日心情
    慵懒
    2020-7-12 09:52
  • 签到天数: 116 天

    [LV.6]常住居民II

    管理员

    群组2018教师培训(呼和浩

    群组2017-05-04 量化投资实

    群组2017“草原杯”夏令营

    群组2018美赛冲刺培训

    群组2017 田老师国赛冲刺课

    跳转到指定楼层
    1#
    发表于 2020-6-3 10:36 |只看该作者 |倒序浏览
    |招呼Ta 关注Ta
    一:背景
    % H( F" o' _$ R1. 讲故事4 ^' m, W0 K/ a) n: l8 o0 u% L
    最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
      a2 ?( h) N% W4 M0 Q( [. ~8 B1 g" f# Y7 i0 M7 w
    二:了解架构图
    2 L. c+ B+ a# hmysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
    0 b# [2 Y* u6 c: q1 K3 Q- M. c' @6 J+ {) g6 g8 O3 y- S
    1. 从架构图入手# B7 ]( |, I& R. ?8 m
    大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。5 H; Z7 T1 q0 q) c
    1 R( {2 y  L/ u% Y/ u- c
      e4 \; d+ L) K

    # Q1 H8 _7 R: Z9 x8 n其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
    & R* s+ E. a# t6 B/ q2 m% ]; E+ I6 E* M4 f* a& V" u+ Q% @
    2. 功能点介绍4 f) X% `0 Q: j/ V( l# A, _
    MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
    ; S7 r+ R+ m# y1 c( O& G- s" u
    ! M% j% Z8 T9 O' [8 K& V9 H<1> Client0 ]- A! w6 T4 v. d4 Q+ Q! P+ ^
    不同语言的sdk遵守mysql协议就可以与mysqld进行互通。* }, p) R2 p* F6 z
    5 Y4 Z/ e; i) E3 q* \* L. R
    <2> Connection/Thread Pool: W. S8 a& A( n  b! Y
    MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。8 _4 W7 X; T/ j
    ; w0 N* w7 L' y  p% u" |
    <3> SqlInterface,Parse,Optimizer,Cache+ }3 X+ H  P6 g1 @3 a2 h* ]
    对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。- T. s& z# S* a/ \4 w5 H

      w) @  p1 V  R% P# _1 v<4> Storage Engines
    0 a. r: J7 y" D7 {负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。
    6 B) X9 c( R$ S/ B# M0 w0 h+ {) S( `3 b" w3 _; i
    三: 源码分析1 q: J; _9 c8 P1 e# c5 V7 q
    关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。& W6 X' ^5 z1 ~* h; }" {4 k, _
    ) `$ s( R: d+ O6 w$ H# j- X2 N* W0 K) e
    1. 了解mysql是如何启动监听的
    0 ?! v- j; P. d/ |. H手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。" J' K2 [; X' u" h

    5 h& K& j9 n2 L8 t# y5 r0 x2 }5 m: F. G+ a/ _
    . }: }  @3 D6 K* Z: w: G
    从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。! A& e0 A4 G: C. r3 n
    / s4 K. K- P* Z& S/ R
    <1> mysqld_main 入口函数 => sql/main.cc
    * C  l+ q/ ^" T# d2 j5 g
      w3 F1 [/ x, T3 ~8 `
    . s+ k0 |2 K3 ^9 lextern int mysqld_main(int argc, char **argv);2 ?& y$ e! R, j' T! ^/ x

      z6 q7 {9 O& C9 pint main(int argc, char **argv)
    : W( p# ?% d! N8 H" s3 h4 C{
    ) C* j; z+ F( Z8 T# \  return mysqld_main(argc, argv);1 G5 Z! l, Q8 \. r
    }1 K/ ~- T  \8 c
    ) ?; v5 r: X, ?. b
    , F3 C; s- X0 \9 I" j
    这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
    " h7 B$ D3 I! h0 ~$ m# ^4 l9 d( ~4 @' v, U8 Z. {4 g
    <2> 创建监听
    + L& {# N! [* V8 r0 `/ Z9 p! @3 r3 b6 F
    * H, A2 I8 C5 ?( X1 U
    int mysqld_main(int argc, char **argv)
      q" m& g$ H* x3 N{* r; C4 V( N9 H( b& u; }
        //创建服务监听线程* x% M: Z, M, K0 n  r* u2 _
        handle_connections_sockets();+ t4 E' W3 y& M( T
    }1 I+ j1 i/ \( ]* E
    ( I9 ?7 G" y0 {. D7 q
    void handle_connections_sockets()
      f- x1 `3 ^" _" P{  r1 _. E6 _9 S% O$ I5 J
         //监听连接
    0 N+ t0 i7 K' a2 I% U# A     new_sock= mysql_socket_accept(key_socket_client_connection, sock,
    6 i  o& z) u4 z                                    (struct sockaddr *)(&cAddr), &length);
    3 C- A8 O9 L+ h9 l4 [! T. U# m
    5 @# d) {/ x" U  ]9 s% u: C    if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
    ( A7 j& |# e" j, F/ w3 @+ w, L      thd->security_ctx->set_host((char*) my_localhost);
    ) ]" m  A! l7 @5 O! {6 U! o7 W' e8 I( a/ R, C
        //创建连接' l4 T) R$ \$ |; m% q
        create_new_thread(thd);
    0 b" ]" B' t! }8 U8 ?4 G' H1 ]}
    . b1 G1 M: O8 G+ B( T, q
      h5 M/ ]; |# f# d" r//创建新线程处理处理用户连接2 B1 i! L& i5 Q; `' }+ ~/ i9 N
    static void create_new_thread(THD *thd){$ [$ ]/ ~9 a% }5 d" n
    & ~& f# V8 c1 T5 z% @8 \/ S. K
       thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;- I' }! F9 G! S
    * j7 r4 k; g- o1 w
       //线程进了线程调度器- |" M5 M/ H! ?
       MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   
      J( o. H9 x& k5 r: m4 v4 D}* w, W+ x) q# P* o# q( I. N/ Q: R

    1 g* A5 C) ]9 j1 y, M% |' U6 m3 |, ^! N+ e$ @3 f2 Y1 I
    至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。2 ^% U4 m# A2 O
    5 M& R. J8 ]' M0 s- l% Q

    ( i1 P2 J6 s! y( R0 n6 D2. 理解mysql是如何处理sql请求+ X0 w& M% ]4 c) p& ^" X6 t
    这里我以Insert操作为例稍微解剖下处理流程:
    2 o3 w. a9 E5 v: p" r3 c3 o8 ~) |$ Y& N5 h! W2 X! p$ t4 r
    当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。+ X( H5 v/ [+ p/ q& c3 A0 h

      G; ?0 J1 l- I4 i* X
    ( Z8 E8 a& a0 Y8 |static scheduler_functions one_thread_per_connection_scheduler_functions=
    * W6 l! A9 c0 w{5 o8 J8 k6 Q! n& O5 H6 q  @$ R+ h
      0,                                     // max_threads" a7 M$ K& f  N7 g
      NULL,                                  // init
    0 m0 [) |! Q4 d  P  init_new_connection_handler_thread,    // init_new_connection_thread
    9 h3 V- l% ?1 r2 d) a9 Y  create_thread_to_handle_connection,    // add_connection
    7 q+ W1 K. c* H( J# N/ e  NULL,                                  // thd_wait_begin' R! [  [3 ?" ]! q
      NULL,                                  // thd_wait_end
    # o$ L/ x9 P: z( M& u9 x  NULL,                                  // post_kill_notification+ g" l2 y# ^2 U2 J' V
      one_thread_per_connection_end,         // end_thread
    - ^  |& J/ |9 C) z2 w  NULL,                                  // end+ h  s8 i' l* F" O: O, I) g+ L7 \7 Z
    };+ D3 v- m4 O6 Y3 M
    " U; b/ t8 s( t8 X- c

    " n' x2 r  }9 X3 B) V% ~" }+ V% o从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。, N7 @4 U( z1 g/ W- n2 m

    7 s/ R5 \4 Y$ J; S2 X( E8 c& @<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪' A+ I; p4 y) E- x0 J  Z
    + m( u: U4 P) M* a1 {' a
    void create_thread_to_handle_connection(THD *thd). t/ Q. R9 q, }( v; f: q( y" b' C
    {
    : K6 C: f  K8 h8 C0 u( w8 ^6 K9 i6 C* T     if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,$ y' e7 _7 s( c0 |. d. d
                                         handle_one_connection,(void*) thd))){}
    - O- o3 s) A$ c2 \/ _& |3 i- Z+ B" e$ G}
    4 Q4 P7 X4 X5 o//触发回调函数  handle_one_connection& [/ G. H( b# K: ^
    pthread_handler_t handle_one_connection(void *arg)
    ( z7 h- `; A7 Y' ?  M: B{7 O* g. f1 `  L) _
         do_handle_one_connection(thd);
    9 v8 t# h8 E" v}
    ! A9 H: e. ~% t  C& A: C//继续处理
    ; Z5 p* z4 ?! O4 B/ b. {void do_handle_one_connection(THD *thd_arg){
    7 R0 n1 u( p+ `0 x    while (thd_is_connection_alive(thd))
    ! I) W' O( {$ {. z+ P1 z; m    {2 c- a, u/ d% R
          mysql_audit_release(thd);
    # ?( }2 _( S8 R& T5 D8 o      if (do_command(thd))  break;  //这里的 do_command 继续处理
    5 J0 h- d0 y6 e5 [* F" M) M    }
    # P% i0 w" o/ k* |+ y4 j}: K! S1 J! Y3 F, P5 [
    //继续分发. c/ b7 c% b6 }7 h
    bool do_command(THD *thd)7 ?, j, ]" H% X+ ^, ?7 c
    {0 a! L7 Z# B. ^# M  w8 q7 G8 f
        return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));( X: S7 z$ H; c0 v8 }: y; S, n0 J
    }9 O- ]( D* e6 |) H0 B5 O
    bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
    - V# p) m: [" p- |{
    ; N/ W4 T, T! U" v/ ~6 \      switch (command) {
    : u' ]% w0 ]; B9 V' X! b, c8 N         case COM_INIT_DB: ....  break;+ p% ?0 O/ r4 o6 x
             .../ R# o/ h* H$ f$ {2 e# M
             case COM_QUERY:   //查询语句:  insert xxxx
    ! }7 R/ p5 i% E: V             mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析* R  Y% Z* H* k: p
               break;: k9 ?) i( t8 m+ o# T! v# H
          }$ g+ k' x& t) C, m
    }
    " _" N$ e- }( h2 t//sql解析模块
    % z8 N. M- R$ B% I  K1 p5 |void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
    - U1 O9 h# m4 U4 c8 N, x' p{
    3 H4 Y& P( @( ^/ |- w4 \* n, o8 f      error= mysql_execute_command(thd);( J) Q; y1 Y- {3 L3 m3 a# d; x
    }
    , z/ x: H5 D$ ?6 f* Z+ E9 q( r  z, B+ [" H- z. I# k" U/ U4 P* D- q

    ; i/ D+ c+ e; h<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。$ A8 R, H+ D  o/ ^1 Z) @
    2 L& K5 {+ H& N! m- O/ [
    //继续执行; Y0 p) V% g5 u( o! m1 ?
    int mysql_execute_command(THD *thd)
    2 A7 r: q4 d3 j0 ]{" b* b5 x0 F0 M! W& a
      switch (lex->sql_command) ' O$ ^3 r  D; s( [4 ]+ y4 T+ w
      {
    ( N) [+ c4 V% \  c- h/ [: ]+ z      case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;
    , S( D; K6 }3 U. G, k- f) U
    " z/ h# u& m5 M: [# m0 _/ ?      //这个 insert 就是我要追的
    , V% a3 w, j) ~1 z9 s: p      case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
    2 F; }5 H  Q0 B5 l                                              lex->update_list, lex->value_list,
    ' G5 z2 C. |1 C% H: Y" {                                              lex->duplicates, lex->ignore);3 i& h+ w) N3 @
      }! w# t6 [% m0 i2 M6 I1 U4 o
    }. u2 ~9 [3 K% j: V1 p
    //insert插入操作处理7 z, E3 {+ G. t  c6 p5 P
    bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,, q; c5 h. K8 V: p( f# X: F
                      List<Item> &update_fields, List<Item> &update_values,
    4 t" t* Z/ U" ]5 ]0 J2 i                  enum_duplicates duplic, bool ignore)
    ; H3 f( w- l. \{
    0 W/ R2 ?  m" m! r7 C% q) ]      while ((values= its++)), O- K$ Z1 ~7 P6 X& |" f) b
          {
    5 J- A) i! S  @8 Z6 U           error= write_record(thd, table, &info, &update);, G, p  G& N; O% u2 D7 m) e3 P$ q
          }! A0 n& {7 g2 h3 A
    }+ ]; b" S% ~- C. ~$ w
    //写入记录
    $ Q" C6 X# `7 y6 F6 P: Mint write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
    9 u0 Q3 O7 _, W/ d0 j, L9 v4 ^& D{. o$ u1 H! p8 P
        if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
    - m: H' p, b4 ]( H3 K* P    {
    : `6 Z  `2 V" e: R. g* x         // ha_write_row  重点是这个函数; C* v0 k- b& C# S- m
             while ((error=table->file->ha_write_row(table->record[0])))
    3 E6 c$ J- Z" x2 H6 [6 T7 u) W3 j6 _         {* B, Q, |3 ?' _8 j5 n" q
                 ....
    9 M. w( ?  d- P8 n         }$ @9 F# ?5 M! F- F' h
        }3 Q4 n' x" F( R3 x
    }
    $ X% [- W5 j5 G, n! n8 E3 u* [# P7 `# S
    ( A/ T; f2 H( r1 r- t/ D
      v4 n3 v! k$ m' i, G2 N# @* g/ W% J
    可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。1 ~( d- I) s. p" N7 I

    $ {+ M- i) w0 ~1 |' j& ]<3> 继续挖 ha_write_row
    ) X( O$ L4 |% |0 Q+ z" K; m7 D. ]
    int handler::ha_write_row(uchar *buf); A9 e0 z+ V3 V. O' z& ^
    {
    - S1 j6 x1 D5 V    MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })& Q! Z6 E1 v& z2 q/ F) K' ~
    }
    ! V% R  B4 [4 r7 {8 K1 Q/ r+ c. K6 V5 A2 J2 j4 P
    //这是一个虚方法
    2 f! H# u: ~$ G" p! uvirtual int write_row(uchar *buf __attribute__((unused)))! q! [/ ]2 t$ a+ d5 r
    {3 z9 K; A  e! n4 m! e) i
        return HA_ERR_WRONG_COMMAND;
    , r3 O4 z1 U$ _" P: Z( Y}
    * _) b7 G9 b9 H+ m5 }0 r7 s9 L8 f: Q* v
    . Z$ p  X5 A/ S& ^2 l
    看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;
    % I% L5 f- ~: M. M0 T* ?7 l0 R) A7 i) M; m) d
    3. 调用链图& y3 Y- z- X" E3 ^
    这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
    ) T& R6 s( t' p$ x5 q
    4 G, X2 R" `# i  M* A- K( _) g
    2 U( R4 Y, q  L2 s9 [- @% R) O4 d- v, a7 [
    三:总结
    0 @8 l$ d0 u' d大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。" E+ N: ?* \. m7 q+ \+ Z4 c# |
    ————————————————7 L; I, V% b2 ]% E. k
    版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。' L$ C9 T' F  }- ]$ s# \$ l
    原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415+ p' y8 C1 X" w' D# E8 M) a1 m# q
    zan
    转播转播0 分享淘帖0 分享分享0 收藏收藏0 支持支持0 反对反对0 微信微信
    您需要登录后才可以回帖 登录 | 注册地址

    qq
    收缩
    • 电话咨询

    • 04714969085
    fastpost

    关于我们| 联系我们| 诚征英才| 对外合作| 产品服务| QQ

    手机版|Archiver| |繁體中文 手机客户端  

    蒙公网安备 15010502000194号

    Powered by Discuz! X2.5   © 2001-2013 数学建模网-数学中国 ( 蒙ICP备14002410号-3 蒙BBS备-0002号 )     论坛法律顾问:王兆丰

    GMT+8, 2026-9-23 00:16 , Processed in 0.748500 second(s), 51 queries .

    回顶部