QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3706|回复: 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
    一:背景
    ; k6 |8 G3 H  {2 k1. 讲故事
    3 }% ]1 b' ^; C% V9 D最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。5 b" L* z) m  f3 R

    " H% k2 Y' s6 {  s二:了解架构图1 x/ g' y* M/ ]7 q
    mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
      A/ V4 M( v$ v6 u2 S- ?8 h1 A8 J. j" ]1 l$ z# h
    1. 从架构图入手& T3 x, v3 z0 F. d2 k" `. s
    大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
    / z; t: L' m1 f: r2 C5 d
    ! R2 F. C: t% D' o- w% |6 \8 e5 b0 A9 `6 Z5 m

    " J: E! r$ i8 y9 v! Y$ N其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
    0 V- h  V9 `6 B3 P3 P5 Q
    6 T$ {4 w8 s& t( U6 T( `2. 功能点介绍
    " y7 L& {& {+ x: w8 MMySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
    / X3 y3 `* p  M+ S1 ~2 |0 H$ v2 t1 }, E0 S+ m1 Z( f  `: z
    <1> Client, I: P+ i& d0 C
    不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
    1 E$ a7 M/ x' T2 w2 b* H
    # e, l$ ~5 K: y' s' j. c! b<2> Connection/Thread Pool
    , |# g/ ~% ~8 G" m; \MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。) m" @7 t7 p* r0 Y+ _, R& T

      [0 K* [. B# W+ I# F7 i<3> SqlInterface,Parse,Optimizer,Cache
    * u4 }7 m+ V4 N# G4 I0 F4 z对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
    & P. V0 W% F. I, [
    # c) |/ h- [( B# v5 h<4> Storage Engines
    & k* {# ^& Q9 x4 w% F负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。" k; _5 ]6 C7 V9 Z
    ; C8 v. V2 x! ~+ s6 W
    三: 源码分析# v  b* g( ?/ G- t' @1 O
    关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
    % D# ~9 n* h4 G; L8 V! `3 i, }2 H  u  ~
    . b3 z( q! y, I' {! R2 D* u* x1. 了解mysql是如何启动监听的% a5 C$ i8 x3 N6 E5 X  j$ N$ R
    手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
    / O# e: `/ @9 p# [" ~9 N+ z) r% ~
    $ l6 s3 u* w5 n% |' T
    2 K/ Q6 [- R2 F3 E8 ^) N2 K! Z8 [% b! B8 `% t% t
    从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
    3 y. Y. y7 q; W$ V+ ], |. ~
    # w/ O- y. ]% |5 Z8 `<1> mysqld_main 入口函数 => sql/main.cc) f& C- r" S2 j% G7 }
    1 Z1 y7 M; K2 l; M. d( \2 O% w
    ; G3 u( k2 z8 Y1 u5 e5 }0 b7 s
    extern int mysqld_main(int argc, char **argv);: ]3 G' t0 X- K8 b3 l( O' M1 z
    / ^) I/ U) f: S: ^
    int main(int argc, char **argv)
    5 c' V6 U" N' q) `2 [{
    " b* N. D+ F6 M+ ]  return mysqld_main(argc, argv);' t. `8 {7 q0 H( V: p( T
    }
    3 G! {7 p) E! d, W/ Q0 z  C% q
    + X) H- p0 o2 z& S, g
    7 O2 r. {+ _* L/ k. K0 D% ?, G: Y这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。1 j& [4 F% I  C, M. `  k

    & F4 P" R5 q2 `4 z$ G: |% E0 R<2> 创建监听
    0 T3 E! n/ ^6 c5 \# k
    * z$ G: O! x% t" w) I) y8 U  m* t1 q$ G3 {$ U  J
    int mysqld_main(int argc, char **argv)( x" n' U4 j3 U5 a" {. p7 M
    {& U) \8 Z; E8 ]  [; P% a7 P) [
        //创建服务监听线程1 b  p) ]2 z6 v; v+ l8 n) ]
        handle_connections_sockets();9 G, V( m8 h! M8 ?
    }" T7 ^8 s4 i. u8 O- a! t
    # _9 n1 i* P  a
    void handle_connections_sockets()1 M9 i; b- O& b4 T! X
    {8 U% [* q2 y) W: g6 m. c! b
         //监听连接5 n+ v; j; t% x& A7 s
         new_sock= mysql_socket_accept(key_socket_client_connection, sock,; U7 c! @- q: q- v0 R! |
                                        (struct sockaddr *)(&cAddr), &length);
    # P! V+ [8 u4 J/ v: Y' q% }
    6 j: r( i( v! j; q9 R* k    if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
    2 G% `; {+ v7 u$ e! Y      thd->security_ctx->set_host((char*) my_localhost);
    4 p8 B7 B; `  ]& N& C' T4 X
      D1 t1 t  C( R1 G    //创建连接
    4 z3 W* t, C$ H7 k7 K" s% Q& z# i    create_new_thread(thd);2 m. ]& p, \+ ~' J
    }
    - u) a' I1 [) a) S; s5 k+ L' n+ h2 _0 ?% D  S
    //创建新线程处理处理用户连接& i* c2 X% _+ R# ?9 v" M7 g
    static void create_new_thread(THD *thd){
    , {" j3 n4 Q8 H: A0 ]# X( H- p: b9 q) h! n! D
       thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;% ^% g) c5 e# ^4 W" C
    ) q6 ]/ O5 T) F
       //线程进了线程调度器
    8 z. D% K3 K5 ^$ E! z   MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   
    ' @6 H; \! r1 R' q' Q. _}
    ( j7 K9 ^9 R4 X1 w0 y
    ' H& F1 W# h0 Q+ @
    ' I, Q) B. `; `4 k' T, ?) @至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。0 V. [8 Y1 j( k, o! R4 D
    $ o) @3 T1 W6 o/ M3 y6 s
    ! ?. Q0 G8 N3 V, [% D7 v" }
    2. 理解mysql是如何处理sql请求% g, n+ `$ N1 v, v
    这里我以Insert操作为例稍微解剖下处理流程:
    6 E; v& q% B0 |% i, W" E. s
    / U9 p1 p$ S8 a/ k- T; M8 b当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
    7 ~6 \0 G1 K" Z. o( ~- @# L, |; F; F2 `
    5 t- j4 X- d; B! Z: m: K4 t
    static scheduler_functions one_thread_per_connection_scheduler_functions=
    5 V+ W% n, X$ f* e{3 Z' U/ i; S+ l
      0,                                     // max_threads
    0 h/ d3 c: g8 W  i5 \) A  e  NULL,                                  // init7 B$ B9 @, _# ~' ]! v7 d/ r
      init_new_connection_handler_thread,    // init_new_connection_thread
    5 |* J/ H( {8 A  create_thread_to_handle_connection,    // add_connection
    , K9 ]9 j$ K0 U  NULL,                                  // thd_wait_begin% `$ G% h  Z5 l' r  r9 ^
      NULL,                                  // thd_wait_end
    6 T4 [! V8 v9 W" |" P; s% B  NULL,                                  // post_kill_notification& Q6 N' U3 A; a: q
      one_thread_per_connection_end,         // end_thread" L8 @7 v$ p* [3 `7 H) Q1 j1 @
      NULL,                                  // end. ~0 |% v, y' j+ v1 l4 i
    };$ ?  P2 U9 x+ |7 S+ ]8 r3 M
    % V' f' P' ^" m- B* J0 m
    % p, O3 N) M+ e. ?
    从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
    % S0 X- p! Q4 m+ L7 s" A  _3 n$ u1 C2 }, t# a
    <1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪# n+ ?" H6 D  y) r& K- W& c. R. Z

    , V% v3 D# i8 i# w# f) a" V8 fvoid create_thread_to_handle_connection(THD *thd)6 p/ V  [8 h8 ]. W! X
    {# z6 e9 T1 K3 L8 d
         if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
    . S6 E4 a+ w" w                                     handle_one_connection,(void*) thd))){}5 p' B( }. O, ]4 h  s, P: |
    }
    1 C3 ~/ K; x, W& Q  R//触发回调函数  handle_one_connection
    ( k; _6 ]. D9 ]# Gpthread_handler_t handle_one_connection(void *arg)
    * I2 m  R% t* I9 f{: `! |! I; i7 `: t" n6 J
         do_handle_one_connection(thd);8 x7 h  }; ]0 U- ?+ A" j
    }
    6 l! Y9 }! z& `) w& H3 w5 C! r//继续处理
    # M; \, c; N5 Xvoid do_handle_one_connection(THD *thd_arg){" a$ a; o! }& ?& R1 \* f% z
        while (thd_is_connection_alive(thd))
    9 L9 v! G. G' X- J    {+ e1 ^9 y8 z! }( H
          mysql_audit_release(thd);
    ) f2 o- o0 g: U& r! F) c# z5 p5 C      if (do_command(thd))  break;  //这里的 do_command 继续处理
    8 ~. \/ S  H$ a9 ]1 M6 A* ?- t7 W    }+ u! e+ z; I1 u+ @6 d" c$ N2 q# j
    }. |$ f4 S( ~3 G" t8 Y" N
    //继续分发
    ) n. p& _0 p: p2 s3 V0 D' E  b7 zbool do_command(THD *thd)
    6 `' q) N) v- j* p" r! _  h! p5 L{
    / ^0 a% H6 x' B0 |& e; O) A' @    return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));8 v1 v5 a  L: R% _$ @
    }! J$ V# W7 j" ?- e* V4 t) n
    bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
    3 o4 y1 @9 H! Y+ p. ?{% I+ K; i1 z) F1 x
          switch (command) {& J2 I4 j1 a& H% k
             case COM_INIT_DB: ....  break;* N2 R& M- |4 G* t5 w8 H& i
             ...
    , V7 \+ P/ X) [! D4 z. s) t         case COM_QUERY:   //查询语句:  insert xxxx
    0 `1 n+ W0 s0 G& R- f1 a* C             mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析( Y3 E5 a( Q% H0 `9 F
               break;
    : z! E4 A: B5 s% R8 |4 I      }
    ) |5 v  S* q. w* L4 c: a, y$ |$ e}
    ( ~7 c9 ^, k& Y$ U; G6 l. Q//sql解析模块
    2 e; p- ]5 v2 ^! w. r/ y$ ^void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
    $ s* S  {+ a3 E; V{
    - `9 V, Q0 T9 E* e3 z      error= mysql_execute_command(thd);
    - K0 Z! h% l; r$ ^* |" L}
    5 G/ j% Z, G7 I1 O0 N9 ^* k: x6 L3 M5 m. D! _; V2 O) {$ F, u
    * I, r( Q% _2 D1 `
    <2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
    3 |8 n% N/ n, J+ t0 Z) r. d0 |. Y2 f- g( Q
    //继续执行4 S, o& B6 F$ p7 R2 }' l0 Y- _
    int mysql_execute_command(THD *thd)
    % Q" e7 Q/ o( j1 D* d# E{- @( e5 Y! P1 n0 m$ l
      switch (lex->sql_command)
    3 h: `  d9 N' }  {7 a5 j) S) d" C: w- @: X
          case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;! C9 j6 [* N( c# U4 Q& F- z

    ! H/ ~- i4 S) h. P' X      //这个 insert 就是我要追的
    0 W7 C5 v3 K# _7 X6 G      case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,: O0 z, u8 p2 I. g! k
                                                  lex->update_list, lex->value_list,' \! }; X: A1 o
                                                  lex->duplicates, lex->ignore);; i. P0 O" C0 I3 u- l. u
      }; }7 |- r0 ^3 t6 i
    }
    3 J! k! E/ M# d//insert插入操作处理
    % n) `$ n& Z# d. V& Pbool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
    3 |  z4 r* r/ ^                  List<Item> &update_fields, List<Item> &update_values, . u* F0 N( Q" n! ]  S& U" z3 ~. }0 z
                      enum_duplicates duplic, bool ignore)) T- y3 q) f& a) s8 l/ q
    {
      m$ O2 Z9 r- Z; V$ P: x( x8 W      while ((values= its++))
    6 G9 C0 C8 Q( P" a$ W      {% u3 E- v  q& w& l
               error= write_record(thd, table, &info, &update);& y0 `# U& j& x& B& b) K
          }
    " I3 ~; h8 n, g: m6 N. C}
    , w; d% e3 `+ p% S, ?: S/ j. S6 D//写入记录
    1 H. u& P" X/ c/ }8 R& q5 Rint write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)- U' j, F5 j" u  g
    {
    3 f" m. H( i. l; Q6 ^    if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)# _& l, m, P- ~. Z8 m% H) a, c5 t9 x
        {
    ; e+ s; _5 L  O( V         // ha_write_row  重点是这个函数4 w1 G9 V, H! |7 _4 t0 v% M2 h
             while ((error=table->file->ha_write_row(table->record[0])))
      J4 \- i! f) T4 P; l' N5 y         {! [* O' x2 f5 R4 g6 `% B" n
                 ....( Z; \1 V) `8 w$ j
             }! M. ^& R7 n- E1 J% C& _: |
        }
    ( h) {+ i) C, ]}
    & O. V. [8 V" B$ G( U/ a4 G) S6 f' P1 i+ d/ n

    0 G% H, S) E, \. L0 s+ U' O0 l- A; C# |9 Y  }# G
    可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
    $ }  M4 x7 o6 a5 ]& |
    , k* E& K8 _. ~/ k+ G$ t<3> 继续挖 ha_write_row
    , g* j" k  _1 @* d6 M
    4 J& h5 {' W7 R" k" H4 Eint handler::ha_write_row(uchar *buf)+ }4 _- E( \6 X, U  `$ s! t4 b7 |6 H
    {( f: m" J; `: C7 v9 p+ B
        MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })- O2 j0 l$ ^: O* z: L
    }
    ) h( Z9 z6 @& X. w3 d1 f3 Y
    - m8 }( n9 `" d4 y! a0 C4 y. H//这是一个虚方法
    - ~! ?" j4 k+ Z0 d/ Nvirtual int write_row(uchar *buf __attribute__((unused)))
    3 \( h$ L; U5 E! q  b{" z* j% }, ?6 V6 p+ Q# r; O
        return HA_ERR_WRONG_COMMAND;- a- {2 D4 k: k* n3 h
    }) M% {0 U4 e$ g: M
    $ V( S( W. F# y$ Q. r/ `
    + ]& I3 ^) x/ ^3 h
    看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;
    $ ~: _# N2 n% f+ ?: Y7 }# M& h# ^
    * A; g" L! \  Y, Q( F8 M3. 调用链图( s7 R1 ^9 S  d' \7 e7 a
    这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
    7 k/ d1 p" w6 G$ l- P, y2 `
    / l' w2 a4 C( L. D& P4 ?
      @  |) J$ m0 w/ h) M1 s# }; X  ], W2 G; W6 r% ^: h
    三:总结/ W. C  c" _4 }4 F; D' s' e7 B
    大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。( d& ^8 S7 {# D1 p* d# x
    ————————————————3 z, c4 q- Q0 `9 H7 I
    版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。' s! c- N+ _" k4 C5 V; K
    原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415; r3 l8 N3 ^; j6 {
    zan
    转播转播0 分享淘帖0 分享分享0 收藏收藏0 支持支持0 反对反对0 微信微信
    您需要登录后才可以回帖 登录 | 注册地址

    fastpost qq
    收缩
    • 电话咨询

    • 04714969085

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

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

    蒙公网安备 15010502000194号

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

    GMT+8, 2026-11-10 16:47 , Processed in 0.217616 second(s), 51 queries .

    回顶部