QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 2676|回复: 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
    一:背景
    & {3 Q/ o! P& p7 k1. 讲故事/ T8 o& n3 F5 U
    最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
    ) Y0 ?# z& k! W6 V$ ~/ t; T& I/ m$ q- S' r# p& v
    二:了解架构图
    0 O# h* s- ?. w$ Zmysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。5 o2 ^6 ^1 o. C9 D% B; O. Y- ?
    ' h2 g( g* ^  s( U6 |
    1. 从架构图入手- r9 |  E2 D. {: t/ @* \
    大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
    ( ^& ?6 \3 ^$ H0 D9 i; Z+ X0 ?7 i! `' \8 {0 M! ~- ^0 I! m

    0 b7 T5 ~: }5 {% v% V: J- b. {9 x1 Q" X
    ( |: p! K0 e# \: C1 j) d其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
    : h1 `' D" W0 ^$ U8 Z. R1 y6 U1 n2 x" \& h% s8 Z
    2. 功能点介绍
    : L# z4 L7 h+ u- T0 A' k1 tMySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。, I4 P" g% g4 p& E; L5 A* p
    ! g+ c! E# q! ~2 p+ {4 X1 ]% I
    <1> Client
    , V$ O2 H& }' ~8 Q% C# m不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
    ' s3 _2 v. l5 Y6 O0 |) ?9 |: l+ E4 a1 m3 U9 Y$ ]. p
    <2> Connection/Thread Pool
    ' J- w) l3 S5 K% gMySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。* Z2 C$ O7 Z4 I

    4 C5 s5 g  y4 j' C<3> SqlInterface,Parse,Optimizer,Cache
    ' ]) o3 F5 W& b对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
    " x+ d2 R5 ?; Z+ U- v
    3 d( X- g# \8 [! f: W- u<4> Storage Engines0 A# a# x2 R* w$ o
    负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。
    % z5 S( g! l$ @5 r& n) p8 b
    " u- q! S, u: [. |( Z. S3 \/ ?三: 源码分析' x3 V3 t- T' ^9 ~
    关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。( s$ \- W8 p& u  }

    ) N4 s8 M: Z1 u9 A1. 了解mysql是如何启动监听的
    7 Y3 n, i  d& J+ n手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。1 x" U, b2 b4 \/ V

    # `3 N3 O: O/ O8 i
    4 C" U- J; J6 s9 z  g6 G% S+ c1 D2 `. k3 t6 q$ V
    从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
    ( B3 k. W" y9 ~" h+ M
    3 o8 \0 {2 P, n* d5 a2 G$ p<1> mysqld_main 入口函数 => sql/main.cc
    2 `  v% I4 l$ A1 \+ t0 |, q  M7 B  O. L3 H$ b; N+ d  j

    9 `+ [2 W7 s* E+ z" I& Oextern int mysqld_main(int argc, char **argv);
    / o/ Q, ?; L  ?% g- e, Y
    5 Y' z0 E: }) Nint main(int argc, char **argv)
    % g7 w6 j0 o$ n+ z  ]{
      x2 j" A* o5 C& K. M8 y  return mysqld_main(argc, argv);9 r' z! x4 d1 _! h
    }
    6 I' N0 }& Y/ ?, d8 l7 }  q9 r, r$ M& A( ^

    * i- Q; o+ M& A0 d4 @' K这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
    ( C2 D5 n& |8 w
    ( J2 Q0 K0 C. J$ `6 o$ x<2> 创建监听
    : Q8 t% Y5 H* u) t: a6 A
    + V1 H6 W3 t3 l
    + a; E( W: ?. \) m+ c+ Iint mysqld_main(int argc, char **argv)4 n! H  y% O: n" C
    {
    8 e, G7 w8 V, @; E( x) P    //创建服务监听线程/ O2 k9 B- |& t- D1 R/ x+ k" a' I& _
        handle_connections_sockets();
    . {9 p% i7 Z: F4 s$ W}: ?6 C6 g  I8 E; K. s8 S

    0 g% s' q( L; c' n- F2 B9 Wvoid handle_connections_sockets()
    - P* o. \* Y% q, g4 E! ?{
    / e# w" D' \+ N) {     //监听连接+ Q* `9 y2 [* }0 B7 c" n3 w2 t" M
         new_sock= mysql_socket_accept(key_socket_client_connection, sock,2 o/ E8 w; {) Q: C/ R4 z
                                        (struct sockaddr *)(&cAddr), &length);
    ( d9 c- ^3 X+ E: ]6 ^4 I& o7 ?; D. j/ }" q* ?- r. X4 F& D: X
        if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))+ V  B8 s- F, b0 D, p) |4 W, X8 S
          thd->security_ctx->set_host((char*) my_localhost);3 X) u( Z- j- d7 x3 O
    $ [: \# t* t# r- o( ~# a% r1 u
        //创建连接) H' ^3 Z- N& ]- Q
        create_new_thread(thd);
    * b+ c: l, t8 o' @4 w2 y, `}( y0 F! G6 Q4 A' ?4 ?

    & n! H  Y9 f  J' c( o% ^//创建新线程处理处理用户连接
    0 d# R* ^$ W2 A9 Tstatic void create_new_thread(THD *thd){; |% G/ \6 ^( |& ~- n* Z
    ; ^& e2 h5 i1 q! f- H- a
       thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;5 ~) a# ^6 }2 W1 t3 X, O5 r0 u

    " w8 g& e5 p  E# }   //线程进了线程调度器  t* q0 v" `$ E- p" I: \1 b8 R
       MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   
    0 j; J0 Q# @% d- H' I  c3 G8 L5 r}
    * E' \4 Z: G8 B* i- w5 i) X
    ' a& _0 J) R* B/ q# s
    + Q1 a4 r( Y6 M7 Z/ ?+ F/ M至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
    & G& E0 [, A: h5 h+ X  }, J  G4 z

    : C- Q. X; l* C- o. l9 S1 }( E2. 理解mysql是如何处理sql请求# K' q5 G7 H1 x" V. p2 @5 ~, v
    这里我以Insert操作为例稍微解剖下处理流程:
    + Y/ s) Z3 ~* \/ N$ Y( r3 j! S
    " J+ I) f# L6 @3 }3 n当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
    7 V2 w4 z- q0 b; A5 Q
    * M( E. k2 Z/ O& H/ `0 G
    + m8 y4 v+ f! w, k/ Istatic scheduler_functions one_thread_per_connection_scheduler_functions=
    + X9 }5 m$ `6 R, I* T) b{2 I1 G" I& w0 f( i, u
      0,                                     // max_threads
    $ v/ |3 S: k2 U7 T' q; K  NULL,                                  // init  P6 S8 ?( b/ }: t& l' a
      init_new_connection_handler_thread,    // init_new_connection_thread8 S( Q. m& }3 k& x6 X+ j! [
      create_thread_to_handle_connection,    // add_connection
    ; ~- `* V, n7 Q. G( m7 b  NULL,                                  // thd_wait_begin" S% o8 C. J0 J
      NULL,                                  // thd_wait_end
    ' x- y$ v" d  ?; B0 X6 B  NULL,                                  // post_kill_notification
    4 a) I0 }% O3 a- T* ?1 d  one_thread_per_connection_end,         // end_thread( h' r( Q4 _5 z: N
      NULL,                                  // end
    % D4 W+ d9 x9 c. a};
    6 \& }0 y( Y$ b( W5 z3 E/ g! s" M' m$ B

    * q* l( |7 G. O9 Q- j, j+ x从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。, V  ~  z- @9 @& w5 a# m

    ; h. `9 H. h- D. w<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪' M5 W! C- C' N% b: e5 M9 T' b
    % W- f0 n% S/ A( x1 l% `
    void create_thread_to_handle_connection(THD *thd)  c0 Z" ?1 C! A) u7 k8 v
    {2 @# Y8 s' k8 o0 K7 F* D
         if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,8 S  b/ R( F0 V5 `
                                         handle_one_connection,(void*) thd))){}
      x; G# M: S# \& ~& [}- U  |: r; u. a# H' {
    //触发回调函数  handle_one_connection% Z6 ^! m% m4 N/ l. ]
    pthread_handler_t handle_one_connection(void *arg)
    ( L% x! O- d$ Z# c{
    7 Y7 p/ ^5 e4 o; D* |4 \9 r     do_handle_one_connection(thd);1 u) y+ v& X$ N2 x* ^& E
    }
    % H. R6 x" j  t1 `5 L//继续处理/ U' V" s6 d, F! q4 C. @
    void do_handle_one_connection(THD *thd_arg){6 U2 v* ]1 s6 @/ w% Y, X4 p6 Z" T
        while (thd_is_connection_alive(thd))
    + E* F1 x$ g: G! c6 k    {
    . H0 L* K; G0 g; y      mysql_audit_release(thd);
    , _0 ?3 o$ u! G4 a& v$ `. C      if (do_command(thd))  break;  //这里的 do_command 继续处理
    6 Y8 t+ V- n6 g3 \6 u# c8 [: B    }
    - O5 g& v0 B3 b, m( B) d# u1 r}
    + X1 n( k  v8 V8 \$ _//继续分发
    5 i( i  u$ j+ t' B4 z: xbool do_command(THD *thd)
    ; ^7 {3 e% S& f{4 J! C9 g) Q& d0 F) E6 C
        return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));3 @6 ~1 a# J, y2 l
    }
    4 ~4 ?% W( |3 e* M- T/ |: }# Y9 cbool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)% K' \! i8 r: T6 C" k: I/ B* a
    {
    - f7 `$ q/ p0 H      switch (command) {/ p  @. D+ g7 S* C! Z
             case COM_INIT_DB: ....  break;* F- N+ Z6 G5 T
             ...
    6 G5 `; l/ R3 q) ]) M         case COM_QUERY:   //查询语句:  insert xxxx; Y% ^& z2 I% m$ V, i
                 mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析+ e5 z1 ^: i- ~6 a7 `" _
               break;: k. A7 u# e- @
          }
    5 s1 U: X' ^- z1 _. l! o. Q}
    ) ~9 d' T  e/ A5 r; r8 q5 K//sql解析模块# u- ~5 S  b! y# f' v" l/ T
    void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)( Q( \& e: {* J0 w+ i$ w" I4 W
    {
    4 z4 i9 ^: f' T/ k9 W$ c      error= mysql_execute_command(thd);/ s+ V% P+ _  `7 n/ `* G
    }
    " p, m3 a* d5 w" H# o0 d( c3 e
    8 y# g/ g: \( U( t5 O% B& C- n
    8 ]' |) t, d$ h& }' x+ u. n<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。3 K4 m4 O$ P4 w7 L+ ]

    ( m; s9 y' a' o//继续执行. G3 I! d9 |; u* h2 \! C
    int mysql_execute_command(THD *thd)& T8 O9 P, `% o% n6 H$ c! F
    {
    / R/ W6 `; `6 c4 ]  switch (lex->sql_command) " \. X1 B4 {% b0 e0 L$ s' j5 V- \5 G
      {  Q- R7 W4 y7 I. B! y6 d
          case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;0 ^$ F" f+ y9 R* M" n! T/ J: L& B5 C; H

    $ a5 X9 K0 P5 M+ V# U0 K) J      //这个 insert 就是我要追的( _/ t1 c  r1 Q' M! i' V
          case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,) @: Q5 Z, H) e/ B
                                                  lex->update_list, lex->value_list,
    * h; m  S7 T- C5 n1 O+ V, V                                              lex->duplicates, lex->ignore);: p0 a. z, T* {7 ]: l4 m
      }
    ) x* N# \) F; @8 L}
    ( M. h. T- _# e) x( J4 A! J& s//insert插入操作处理
    ! D- Q" l9 I# w# S8 e" ~bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
    . W" P; y2 n$ Y                  List<Item> &update_fields, List<Item> &update_values, # i5 m8 R2 Z6 D1 e& O& C6 o( l4 O
                      enum_duplicates duplic, bool ignore)3 N$ B  H4 |6 }9 `0 a6 ^. I
    {% M# w! g3 h5 K+ s# Z7 T6 }. d
          while ((values= its++))
    & \4 ?% ^: C% m+ a" `: u% o/ q) Q      {
    0 P, m5 q0 v8 E6 x' B2 T' `9 C           error= write_record(thd, table, &info, &update);5 j1 l4 n7 |; a) ^  H
          }
    , W, A: x* t' `3 T5 p1 a}  A% B) |% W7 j6 Y- l9 L- U
    //写入记录
    , @+ i/ d0 {3 F& {6 ?int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
    . \) @1 K) l# g& V4 L' P7 R{* |/ F+ ?) ^- J4 |5 e$ Q6 a. }
        if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
    $ [" k. i6 g' u/ H( G" Z    {9 |3 E% q8 P6 ~" K
             // ha_write_row  重点是这个函数+ A# I0 J/ c4 H
             while ((error=table->file->ha_write_row(table->record[0])))
    ) s; Z8 J. r. X' b& s$ j0 Y         {2 G4 ~6 q  g. R
                 ....
    7 y5 ?% X- _* F5 `         }
    5 ]6 e  l' i' I1 Q# U6 g    }. b7 n0 P- j$ w# ^6 [
    }
    $ D: E, w5 V3 w9 {4 b9 g' G$ ^; Q6 x

    4 P5 Q- f9 j6 f; [8 ?  _& p; I# H9 j6 X
    可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
    $ J8 B  @) B+ S! ^( R5 \% r' B1 |/ E' Y. b9 @6 N) x0 I% ?
    <3> 继续挖 ha_write_row
    / @/ T' |6 U& R8 D, e3 B& |  @% `1 u% O2 o/ D: i
    int handler::ha_write_row(uchar *buf)1 S. f! j4 B6 p  {3 n8 `) N
    {
    . P( p: {) {% E& o  x9 }    MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })5 h3 u. |* w+ ~) |. n
    }
    & @6 n9 B$ {  [$ r2 ~" c9 ~5 L* E* i2 ?2 ?
    //这是一个虚方法
    4 B% E. |6 J0 B  }( Y! jvirtual int write_row(uchar *buf __attribute__((unused)))
    5 m: u: I: E+ U{
    , t% J* P1 s$ Y- L. h    return HA_ERR_WRONG_COMMAND;
    " Y& s/ w5 @! k}# t2 `3 t$ R5 V* m; Z

    3 `" f. Z3 P3 k
    0 r/ z( U; I: ?# G* N5 n& T看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;' O: @7 s% i$ }& ]
    ; R! n, v) _4 Q
    3. 调用链图
    + G/ }1 T  H- V( u这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。8 B/ q; z/ Z3 x. u- ]1 J

    ' @; _. F4 t7 I, S$ ^  I- i1 c% o" |# j6 G5 e
    3 n$ A7 @0 n' A8 k
    三:总结
    4 x$ u5 T$ {0 w0 W  [7 U大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。
      Z; ~2 v) y( x# V9 e2 i) D————————————————
    6 j) ~. I" `& V, S& x+ c版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    3 {* I+ o, }! i8 @) n  N原文链接:https://blog.csdn.net/huangxinchen520/article/details/1064874154 k) L2 S1 J, B! e, B  A
    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, 2025-6-25 11:34 , Processed in 1.021674 second(s), 50 queries .

    回顶部