QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3629|回复: 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
    一:背景
    9 U* G, x; E8 g# J. [1. 讲故事
    ) @- Q4 n4 a3 ?  a最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
    ' m% d9 n+ {! c+ L+ B6 K0 I* b! x3 I
    二:了解架构图7 s, q) l+ U4 S4 ]6 n
    mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。3 W, e& z+ i, z9 P9 @

    . r, \' G4 o/ T4 N3 W! z1. 从架构图入手  D, ], v7 ~- Z( E0 P8 s( J) |
    大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
    0 d3 e3 u( T$ g) W3 k, e+ y
    $ d5 i8 K% ?8 T' U" F3 D5 ?4 N9 u& g
    5 z' I8 o. @; a( S9 _/ e0 j6 M
    其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~* f7 m4 M3 }# C* H% j

    " ?: F+ L3 l7 h1 w' h% M1 @: d2. 功能点介绍
    : ]% m: V! e! r2 s$ QMySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
    $ N8 D, k' U$ d$ a& u  ^, `) r) q0 f9 O4 ~; G; S
    <1> Client
    ! m$ l% u9 P' p不同语言的sdk遵守mysql协议就可以与mysqld进行互通。$ O4 L% s2 e& {0 U/ w
    & N8 j- r/ V- H
    <2> Connection/Thread Pool4 i! S9 W# o/ S3 V! J$ A8 p" U% H2 T
    MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。9 C" b. t0 ]/ j; r7 c0 V

    + J5 i( L8 M1 X8 o+ n<3> SqlInterface,Parse,Optimizer,Cache  O* v* @5 w" z6 x5 c& d* V+ D' U! F
    对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。2 Y, e, t& N) k- c! j& o1 B( F
    3 L% v. K) `5 d3 N" ]7 L- J) F
    <4> Storage Engines8 F3 ^/ q3 p- V& m3 W1 A
    负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。
    0 Y$ d2 p5 B+ |' H; J7 I6 c# h3 j' H8 c, S9 D
    三: 源码分析( W" T9 ]7 `! g+ S) d: P6 T
    关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
    3 K( A, [, q" K1 Y- X
    8 M$ z+ G' b) |8 R1. 了解mysql是如何启动监听的2 m- k$ w( E1 |8 v/ `' w
    手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。! W7 R; [) |! d  U

    ' S6 P, ]4 A- M. O. h5 ^' g  |7 _( W2 U& v" j; F8 L% [
    3 F; U' w( F& y
    从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。! W( [: o+ b/ o3 a  J$ v1 S$ D
    0 c; N( ~) n# F  A3 l' z! e$ o
    <1> mysqld_main 入口函数 => sql/main.cc! {* C0 M6 A. e: d5 l' @

    # l8 D$ ]9 E2 t# x
    - D: ~) O. F$ o4 T; J& f1 kextern int mysqld_main(int argc, char **argv);
    1 O% V; }& B( D* p6 i( `7 D5 M: p1 V- a( z/ O
    int main(int argc, char **argv)4 I. B" f; C. c* R% V* M
    {% p! Q' b9 R9 R  p7 W: t, y
      return mysqld_main(argc, argv);
    / [- ~: S; Y2 Q3 K}7 |+ J+ S! _% X

    . V' J) R3 B  ^. t2 w0 k
    . N) ~: f7 c0 t2 a" N% Z这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。2 _/ E- f2 o! G- W2 n* z
    ( E0 C* Q' s% S" R2 @& F" c$ }
    <2> 创建监听5 D8 w3 {: ^0 w. I

    . \. `& H' p4 |+ K1 Q
    , ~6 z: x/ m3 p2 uint mysqld_main(int argc, char **argv)
    . h6 R4 P4 Y5 T# S' ~{
    8 a( B. J5 M6 @! ^    //创建服务监听线程9 T1 |2 i2 U6 r+ d; H4 {
        handle_connections_sockets();
    & {( A8 Y0 D' W- X. G$ n8 c}/ J" d! z9 l; w0 C/ ~' _) O
    9 N. a2 ?" i* e" x( _6 h7 [" _
    void handle_connections_sockets()- _5 S% ?& g4 |7 a& I
    {( v' c  b- Q! F& Y+ V8 t% p
         //监听连接
    2 j. }" s# s7 `! ~, o     new_sock= mysql_socket_accept(key_socket_client_connection, sock,% {6 a- I/ x+ N6 W: u( u3 i' c
                                        (struct sockaddr *)(&cAddr), &length);
    * i' ^- I% f: ?& R1 }; A1 N$ y; i- o, P, ~. `- J$ [6 \  D
        if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
    : H( z9 }- Y) t! M      thd->security_ctx->set_host((char*) my_localhost);1 n  z; D; F. n2 F: D! Z* E& h
    9 f- m/ q* M: C) ]( Q' H
        //创建连接
    1 w" h' G$ I& @$ z& r    create_new_thread(thd);
    ' z3 V% z% L: w}, W, O. e+ _" U5 n4 Y& U7 W& X
    1 G3 f( @0 z3 M+ o
    //创建新线程处理处理用户连接( o2 [; _& Y. T
    static void create_new_thread(THD *thd){
    % g( v& P" G1 s3 ^' {, J/ x5 @4 i
    9 ~' O4 |5 i$ |0 z5 m5 E   thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
    # _! }9 z/ f& `9 o5 p! {. Y3 c6 F( J0 x% \0 ~3 {; T
       //线程进了线程调度器( |2 p& m  L0 B9 ~( h! X: y  M* O
       MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   
    4 |! y8 D: }& m6 p6 p2 m6 D}
    5 H' [; [! r* j  F: q) p
    3 F  h6 O! [7 n. {2 o
    . o# Y4 R& X) @% @1 Q! J% _2 m1 B至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。7 X& ~: Z' o. B

    + c; j' F. M* ?/ ~9 q/ D9 Z# v7 q7 \" |# R
    2. 理解mysql是如何处理sql请求
    # b: W; u  J. b这里我以Insert操作为例稍微解剖下处理流程:
    , a6 F8 ^9 I# ]) j, V+ ^
    1 ]' M" ^5 J% R$ N当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
      M- t5 x. Y- T; F) m3 B; }$ v1 h4 D! i5 ]; }  X) R9 S; c

    5 T+ m; {- ~( J# n7 |( p" h: f5 Qstatic scheduler_functions one_thread_per_connection_scheduler_functions=& Z# O" E4 V" D2 ?; j
    {8 K4 `2 ^0 w, ?2 [  `; I, d9 U( w
      0,                                     // max_threads  G4 a4 b, D( U$ D$ Q! L
      NULL,                                  // init$ R5 K/ j; N" I  P! e0 N% e$ B
      init_new_connection_handler_thread,    // init_new_connection_thread
      a9 G# Y- ]0 i* F4 m  create_thread_to_handle_connection,    // add_connection
    ! k. i0 }* }) }1 C8 {0 h  f1 q  NULL,                                  // thd_wait_begin3 n8 O; g" I5 Y8 h9 N& a1 |+ p# G) u
      NULL,                                  // thd_wait_end
    * B- ^* N" b) d* {" Z) B  NULL,                                  // post_kill_notification3 B+ A4 w7 e1 p4 V6 a% m) r
      one_thread_per_connection_end,         // end_thread
    1 J8 R$ J3 ]7 _/ n: s7 p0 Y  NULL,                                  // end
    ' k: h& A1 R- R0 X( z};
    & Q( m. Z; F; H' V& X; b* Z% j/ N" |: u4 q* M& I0 n+ P

    . R( ]7 r* v! {6 e0 N从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
    3 g" D" Z9 U$ q5 k" R, I; K6 G. `; R
    , v- t. Z+ w# _* N<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
    8 B5 b- d1 K& z! F2 T/ b8 t! @3 H, p, R6 h0 _8 y
    void create_thread_to_handle_connection(THD *thd)% N/ m9 L$ q# m- c+ n: t
    {" Q) [0 r6 \% O
         if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
    ; i  ~6 n/ r$ C1 Q                                     handle_one_connection,(void*) thd))){}! l1 R7 a7 G' P
    }( u; V( n- l& j6 I( ?
    //触发回调函数  handle_one_connection
    ' p; c4 E2 u8 i- }2 ]: g: H6 c  X/ ?pthread_handler_t handle_one_connection(void *arg)6 F2 r2 f' Z; T% {5 H: K7 ~
    {
    $ Y0 G: p% T4 ]. v( e2 {     do_handle_one_connection(thd);
    * Y3 h8 Q7 N. j1 Q" O* f! d}" L. R1 _2 z. W' B! c! n4 Y. Z
    //继续处理
    : l& K2 {5 ~9 yvoid do_handle_one_connection(THD *thd_arg){+ E, t4 V/ ^- ], I6 t' V
        while (thd_is_connection_alive(thd))1 ~( E' h8 I3 S+ z
        {
    , K2 _% N1 o8 x4 b' |* c0 P/ c+ D# R      mysql_audit_release(thd);
    2 i* d; w$ a% R4 T7 X: n      if (do_command(thd))  break;  //这里的 do_command 继续处理/ C% d* y7 s6 N/ X4 U2 J& |3 O
        }
    9 R4 C7 |- g( v9 w, M}" x3 e  e7 [9 z1 T5 Y# Z
    //继续分发$ ^. T3 M' i5 ?/ `
    bool do_command(THD *thd)
    ) T- U  K6 c+ e  @! U- a{8 O, |4 d' E9 a. ^0 m, [- ^
        return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
    5 l" B6 O; K3 n; V/ a4 ^}% A5 A- @4 J% y- H" z( R
    bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)2 f( i* y* x5 {  V* {, p
    {
    2 s9 J& C8 k! s& t) j: P6 N% i      switch (command) {/ {! g, B6 A3 Q- U0 i& @
             case COM_INIT_DB: ....  break;
    9 ^/ B8 G% _6 {+ @  p& M         ...
    8 z, }/ p( G+ f0 m& s# o         case COM_QUERY:   //查询语句:  insert xxxx
    2 B0 A+ L' U0 g- D7 o             mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析4 W/ u2 J( [9 R
               break;
    ! x' b0 h# p# w; w' z      }8 z3 B6 o- d: t0 \5 h2 u+ C2 G# ?
    }7 k9 V+ M, z. }  m: A
    //sql解析模块
    ; `& i, Y" w/ o5 c/ W3 J# vvoid mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
    , c' @0 t: R0 \2 ^8 U{
    2 y- X* ~2 B6 e! I! a; w      error= mysql_execute_command(thd);
    8 `! K, v" ^+ m0 m3 q3 f" }  d}
    3 t) M9 V, }3 F6 w' w" w1 ?/ |9 L" n: \& y) y8 _% c# Q

    8 b* @9 W2 q* x; M" ?+ s<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
    2 ^5 Q. r+ B9 G' B2 E, @7 a! Z: L* u2 Q
    //继续执行
    7 K8 c% k/ k7 c3 n3 |$ vint mysql_execute_command(THD *thd)% T! _: n9 Z8 c% u
    {& j9 L9 g4 D7 S/ N
      switch (lex->sql_command) & M1 G2 A  i2 @5 g0 j
      {) V" [) h) o( G1 X7 L( h' Y
          case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;0 B/ Z& p. w5 J4 h1 o- v/ x
    # J/ _: e7 Z6 Q1 x4 ^
          //这个 insert 就是我要追的
    + S; x  U% g  K/ v      case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
    $ w' R4 c& H& Q! C+ F- L                                              lex->update_list, lex->value_list,
    # P& J3 o: a( f& U% v                                              lex->duplicates, lex->ignore);) Q. e. B3 F/ U1 w+ T- `
      }( X& ?( a0 I6 V/ q' i  G5 M: [. U
    }
    0 \" V" x7 W# \& ~/ r//insert插入操作处理
    : g2 u8 l% m$ |  L) k" j9 [6 ]bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
      ?0 d0 x3 u" L/ b; a                  List<Item> &update_fields, List<Item> &update_values,
    1 ~; F. [8 C: a, B0 e3 |                  enum_duplicates duplic, bool ignore)
    8 ]1 b: T0 F; }{
    ! ^- H3 y1 ~# D% d) |) h      while ((values= its++))9 f5 I4 t: \; m' A
          {
    9 }& N/ X$ R" D% [& s           error= write_record(thd, table, &info, &update);5 _6 [& J' `% M: Z
          }" e# i3 u  t- y8 w7 r
    }
    " h6 k( |, S8 n; ~6 S* V//写入记录
    + y/ K2 |, N* @# B5 s& iint write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
    " R. C0 D& C5 k5 Q& V6 ~{
    8 d1 P; y( f4 H$ L' P: U    if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE); z  O/ S  P! h* S
        {/ G. u- w& i# L7 E+ }2 N
             // ha_write_row  重点是这个函数
    6 l& `! G$ v2 L. b1 U8 T         while ((error=table->file->ha_write_row(table->record[0])))9 r9 C- R" j3 i! Q" h9 R1 T
             {
    ( O1 C# p/ T( M  W  e7 z  {             ....' Y% p5 S% {2 D( @
             }
    - I% Y+ m5 H4 |1 \" S    }
    % S4 Y3 ?4 F0 O' u" u}
    ; I- `2 ]$ `  d0 l' q" x) C5 m/ m2 v0 U1 ~+ Q( z6 O. U3 K

    . y* t2 X/ [( p* v4 F8 c0 t- a5 s) b* A( d* h8 c4 M
    可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
    - N; T* E0 P( W" b5 N% H- c
    4 h; ?1 v$ y, N2 n  T( O  q( V<3> 继续挖 ha_write_row- J7 o8 |$ ?8 X# b1 ^

    : O% Y% v7 o# N* rint handler::ha_write_row(uchar *buf)
    ( b( Z4 p: p5 o5 _0 R7 P$ @- W{; l2 a; H: c  R
        MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })3 Z! P- C! U# q/ P* Q
    }  C2 I7 c4 w& Z' [, w& g, [
    $ t" U4 Z% U- t* d, p
    //这是一个虚方法+ q' h3 j5 k0 v, i5 t
    virtual int write_row(uchar *buf __attribute__((unused)))- ^! B; l7 K" {1 @* a
    {
    : T7 X, i6 k8 E4 K, V    return HA_ERR_WRONG_COMMAND;" u0 [5 A5 A; G7 W
    }+ {+ C1 F; H0 T' y, N% M5 l
    8 D' g; `& F  A5 d% @! }2 Q/ q# H3 H5 Z
    ; S# Y3 c8 |* [. ^3 a) w8 o
    看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;% c1 {( O9 L; f% B
    $ l, k) j  G4 T  x1 W
    3. 调用链图
    7 ?; b% Y" n) i2 m, h% l这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
    * V: V: u% s' D# R: O( |' J  X- q
    : M; @+ b% D$ C, _2 r7 v8 g3 q; K

    & f3 T3 a& n5 k! h三:总结. Z  ^5 F5 B- ?# A$ R1 ]
    大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。
    4 F( k) X. ]6 j" z* F3 ]————————————————9 @  Y+ P! i! G; k6 N1 h0 |
    版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。& S9 ~: R+ T: x* Z0 c, m- k8 s2 {
    原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415# b7 F- O7 a: O" w+ A# e
    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-24 17:16 , Processed in 0.273219 second(s), 51 queries .

    回顶部