QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3019|回复: 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
    一:背景6 m+ r% p( L/ N4 D* t
    1. 讲故事
    3 K1 |8 W' ~/ A8 g- o3 W最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。8 {; s& H5 d2 F2 m3 ]

    / c5 u! C7 i, k6 c( t( o7 w二:了解架构图
    8 F0 Q( ^' Y- F/ q8 Z; X5 Kmysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。& t  Q" c" h: h$ J$ r
    $ Y0 I# ]( X9 ]1 G, @$ h# I5 U5 L
    1. 从架构图入手/ `/ ^6 a! N% x6 L( O
    大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
    3 a; K- z+ L4 u0 I. V& n- m% t1 q  I: S3 L
      V" A& s  [4 E, c! ?
    5 |- N3 z2 X+ x9 U
    其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~" q( {! X( n7 v, W3 H( N2 I
    - h) y  }& U1 [6 T
    2. 功能点介绍# Y7 z  j0 ]+ a
    MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
    ; B$ P- S/ {* q: y- I) Q
    0 ~# L" M) U2 V; L# P0 L<1> Client( c6 C1 x; Z9 |0 u. `* L: T
    不同语言的sdk遵守mysql协议就可以与mysqld进行互通。0 F5 s, r  g- v4 Y. ~
    8 j5 g6 C% Q, }2 n
    <2> Connection/Thread Pool' E# Q) ^4 J  x( L: x
    MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。1 I+ e% Z1 b9 w! F/ P8 l
    % l$ t( e2 K* h( R- D
    <3> SqlInterface,Parse,Optimizer,Cache
      H! d; X4 {5 C7 J: B对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。# _( \: o8 a9 w* f' x

    ( e9 a4 R* s1 p2 I' h% R<4> Storage Engines
    ) v5 ]7 K5 R4 M# Z负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。
    . d8 L# Q0 Y6 |* n+ t4 w$ X- o. S5 ?4 g& q! k
    三: 源码分析
      L6 ~; z7 C9 H4 y+ J! ^关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。, f" j" p* A5 M8 M; d: U; j4 u! E$ H
    " G& W) N) h* c: P9 K
    1. 了解mysql是如何启动监听的
    1 f& i" ~2 m* R' @: {# T& ?- d0 N$ f手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。1 X4 ]" r0 }! `3 v* i
    3 e3 m8 n1 p4 M' p6 u

    + ?+ o. i/ b; d6 l
    % E, G0 G  }% ]2 a从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。8 b# f6 p: `, ^/ {5 z
    9 j" a5 k- K% Z& _  X% o
    <1> mysqld_main 入口函数 => sql/main.cc/ j$ i9 P; f: T. m. \; r

    3 q7 w) a* U. Q% K6 O& I9 U& `: A' l6 X+ u8 ^9 b4 C% e. m
    extern int mysqld_main(int argc, char **argv);+ H3 x, N. k7 y; C% ?! ?

    1 I, k* Q  }, u& ?% u" c( r* Rint main(int argc, char **argv)5 Q( R* ?6 t( E' |# h
    {
    ! g. R8 D9 E1 }- T: l# q  return mysqld_main(argc, argv);" O! L$ B! ?" M5 u5 k
    }, P" ?* I# s% A

    8 K# A  }( c! @  c+ V2 u6 y, I6 i5 ~% _6 e7 q
    这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。0 P+ b3 \2 \" P1 W/ ]

    ! Q9 I% _4 Q8 P5 S<2> 创建监听
    $ e. l# C6 R3 O6 I2 J2 E
      S, O; _# `/ f4 H) V" A
    ' X! X0 `6 T* ]# j) q" C8 C, D* Gint mysqld_main(int argc, char **argv); n/ t) s( h  p: }
    {
    0 z5 s4 `( x* M6 N, m& K    //创建服务监听线程* f! ?) y; {0 M, f0 d% P
        handle_connections_sockets();4 O- p+ m. h" t1 K6 }6 K
    }' K5 l, j9 S; a! k

    & z& P( @8 q8 jvoid handle_connections_sockets(), q  a3 H1 O; o* l0 t( _  J
    {1 e) n/ r* s: ^6 X% d+ P
         //监听连接
    + Q* F- [; X3 P4 |! f" q/ {     new_sock= mysql_socket_accept(key_socket_client_connection, sock,
    7 |( l( L6 j5 G! P                                    (struct sockaddr *)(&cAddr), &length);5 m1 U1 o# u; P7 v

    ! \# r# |+ E! M7 {3 l" G3 ?* n    if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))# E2 r/ \$ H6 f! q
          thd->security_ctx->set_host((char*) my_localhost);
    5 H- s3 k6 G2 B$ C
    + f4 N  m+ k$ n) ~( M    //创建连接4 n& F, F* w/ S. C
        create_new_thread(thd);' R; o# }. g5 V5 a
    }- r; X2 @" S0 L4 A9 k' P

    3 A" h0 P' b2 u1 D( f  o//创建新线程处理处理用户连接
    , H' p, M. F. R9 f9 l5 S6 `  ystatic void create_new_thread(THD *thd){9 R: o! F  ]& `# j/ ^! [5 M
    9 |& Q$ e& z3 t# f; Y' {  L0 S7 }. e
       thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;3 y" S) c& s' ^/ B! N1 V9 p! U- a
    * ]% S9 E; @: n. v
       //线程进了线程调度器
    % F9 Z" [3 J; |7 b- L4 u) }   MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   
    $ U! |* l6 c0 b8 |+ j}
    2 l, H+ A# x+ G( {& V% k# I, q/ E/ j+ |
    % I4 _: Q' X% W' ~( v
    至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。0 [. D& z/ D% L4 y; [1 k

    5 u  e6 c; o% ]" {- ?0 `7 A3 p  f* `. l9 t
    2. 理解mysql是如何处理sql请求
    ) }' e4 i$ `- N5 W这里我以Insert操作为例稍微解剖下处理流程:- K6 f' d/ w( z# w6 F

    9 p" \8 r6 z+ _) D6 _当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
    8 l) B! b) V3 n9 n  ^# y; H
    * y( w( ^  W4 `; c& q+ [0 M: D- C0 w0 S2 @; Y
    static scheduler_functions one_thread_per_connection_scheduler_functions=! ^2 Z9 [/ T' Z' Q( w. P9 i
    {
    : x! [0 T5 o7 S7 N, y  0,                                     // max_threads9 v1 @' `3 Q/ E8 m8 f
      NULL,                                  // init
    ; f6 B: C  p+ S9 D7 ]" f+ x  init_new_connection_handler_thread,    // init_new_connection_thread
    ; ~0 ~7 Y1 v' C7 t+ ]  create_thread_to_handle_connection,    // add_connection
    ( W3 B* k2 u4 r. }: G( j" F$ ]8 _3 A  NULL,                                  // thd_wait_begin
    + n1 m' T! |6 d3 @  NULL,                                  // thd_wait_end
    8 I( l6 A! z& U  NULL,                                  // post_kill_notification- p' [, q/ I- j2 T8 w+ ]$ R  K
      one_thread_per_connection_end,         // end_thread
    0 K; ~" Z* J' `' B  NULL,                                  // end  u9 q' I# s+ b. j' j2 ?
    };
    0 G1 R3 T$ L" i! _5 h) Y/ x- A
    - S- g2 g9 S. P3 S+ ?4 B% ?
    7 S0 k& g' P* C( x" B从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。$ W4 C% Z" V: b" L
    ) P- I2 k- v0 h7 ~, N% X
    <1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪6 e. I  q1 c7 o5 x# y, F

    ) R1 ]# R% j( J1 @2 w/ W# K" V" Vvoid create_thread_to_handle_connection(THD *thd)
    & F5 _: j% G4 e$ ~{! X' f0 S# ~6 ]7 r  j) Y* ^
         if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
    4 n. m* @" M1 S# G  ^, o                                     handle_one_connection,(void*) thd))){}3 e' R7 n  v* g: P
    }
    , x8 r! B, O0 _2 W//触发回调函数  handle_one_connection
    8 L* ]  s  I& s' w" G2 m: Y! }pthread_handler_t handle_one_connection(void *arg)
    6 Z3 R( `* B0 `! w3 L1 y{+ P+ ]' F5 O  p' }7 d
         do_handle_one_connection(thd);+ A1 p. |4 B2 X
    }
      A# N5 i: d: H1 l; W6 ]. q. N, }8 Q//继续处理/ m: i, ~$ ?* S" Y! |
    void do_handle_one_connection(THD *thd_arg){
    ' J. B/ D# }4 q0 B& ^" r: N    while (thd_is_connection_alive(thd))
    6 ?% d! A% ~% D" y7 ~    {
    , }3 j! }  e9 L      mysql_audit_release(thd);8 n+ ~7 p7 i3 h4 B6 i  d
          if (do_command(thd))  break;  //这里的 do_command 继续处理% W5 F) n% ~* L, l1 t
        }* E! b8 f! \) q- H# v
    }8 |9 |+ Z- X0 M8 f4 F6 R
    //继续分发. Y: f+ u$ k. S9 Q4 ]$ @7 H
    bool do_command(THD *thd)3 A) ^' \9 e" J4 a; ?
    {( w( n9 i. l9 T; d2 f. J2 a
        return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
    . N0 _: R- n9 n" C7 N  K( S0 r7 O}5 |9 g) H. P' Q0 |! W- t) X
    bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)8 c0 n7 ?, {" ~* _* r
    {
    5 A5 c. z/ Z0 G, s. n      switch (command) {* E+ _) E' r: |' o: q
             case COM_INIT_DB: ....  break;. C1 I1 l0 o- f$ n0 s# p! V% k
             ...
    ( W$ F' r/ F; Q- x" b9 j         case COM_QUERY:   //查询语句:  insert xxxx
    3 y$ @" {( h; G# t! ]             mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析
      E( {; T- z' Z3 @( W           break;
    . e: T9 [1 U" e& f1 B* ?/ ~( S! U) |      }7 N# y8 H* Y* ?, Y" G5 J$ r# Q1 y
    }) x6 p& P% _0 [' p$ P8 W
    //sql解析模块
    ! z* h: ^1 G- ^1 ]% N5 hvoid mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
    , F0 p- l4 H% K) I' r2 g2 ], V{
    1 t3 y6 c" H6 c9 z      error= mysql_execute_command(thd);( j5 P+ |, p! M+ V. H
    }
    6 w& J$ c% f6 V+ U' f# n' Y1 ~+ ~
    , w( R3 z4 E, y3 }/ t
    9 V& `( J% [. _" s9 U) X* P1 h' M<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。  ]$ [) E* b7 _

    0 I" w& Y% {/ M* q" N//继续执行" h* i. K% a; T3 X! w0 D& E
    int mysql_execute_command(THD *thd)  P# y# B% t7 \* r/ q7 u/ _+ B2 \
    {+ U; H( T- m" k! v; m/ b- F9 `2 ~
      switch (lex->sql_command) $ L( e8 [, A! Q( h" r
      {1 r$ ], E* U% k( Y" A7 i
          case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;
    " @: i) z1 ^/ r, }6 {3 {; C0 ^5 c0 Z2 `3 L
          //这个 insert 就是我要追的* h  }3 D5 f% k
          case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,* p$ x) i, e  @+ x" j
                                                  lex->update_list, lex->value_list,
    5 r3 j4 j3 q" I+ H2 p/ P! q9 a( n                                              lex->duplicates, lex->ignore);
    + R- B9 p( b6 X! ~  }  n( [3 Q+ M; a7 `% I
    }2 d4 C) J# l& V" W0 i
    //insert插入操作处理
    9 }1 y; l7 x% W- ~0 Bbool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
    4 }) }4 t; |/ k  c9 O5 `                  List<Item> &update_fields, List<Item> &update_values,
    8 [: o+ y3 h: j                  enum_duplicates duplic, bool ignore)( k6 ~! Q" h# v$ ~6 Z2 i" @, R
    {
    1 U0 \; }6 h% ]  T6 m5 r; z      while ((values= its++))
    , W: h( {8 _5 I2 U' j      {
    2 v$ i) x3 u. A( I1 }           error= write_record(thd, table, &info, &update);. j) V# ]) ?' l$ @  c! v2 }
          }# T; V7 @& [; l- W6 A% y7 x  p1 k
    }; B" e( i: R7 x; M+ m& n
    //写入记录; |4 s! w; v5 V$ j
    int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)2 b  d  M+ F4 [0 ^: t
    {( l: i$ D. L0 a
        if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
    3 [+ P" E1 x2 j& B: G* V5 I    {
    . _. g8 i, W7 E4 K/ }8 d* h         // ha_write_row  重点是这个函数
    ) a" _9 |! h) x/ d) a; a" B         while ((error=table->file->ha_write_row(table->record[0])))% l# h1 f1 p$ _  b0 S
             {# p, g0 N6 Z- r4 K
                 ....
      T$ `8 u5 }6 E- _4 i         }! X- T) O$ H; u) [4 R( ]: D, s
        }
    & v" X  F4 t+ h2 i1 f6 X- @}) L2 B9 ^4 o# A5 W( {4 O8 e/ ~7 b& e

    * o0 {+ X8 m$ I- \7 q7 S3 A
    2 t2 \& Q; @8 G$ ?# n" g' Q" Y+ Z8 X5 B, x" E
    可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
    * u% F% p8 [0 Y- b4 {8 Y) R
    8 O+ M% |; V+ v$ \' C<3> 继续挖 ha_write_row% C. J7 b5 x4 Z
    ) r  h  F- S/ k  w" }' X- t
    int handler::ha_write_row(uchar *buf)
    8 l% e9 C- S8 N, Y- ?  }- V) b{, s" H5 X4 U2 p$ ^( u: x$ I
        MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
    7 V; ^1 o( }% A  t+ @& ?7 l7 [}
    " K/ A7 h$ `) v7 F
    % P5 v+ D5 J: l! w. T//这是一个虚方法
    2 C1 W* A# V5 P2 ]: V* e; O7 Evirtual int write_row(uchar *buf __attribute__((unused)))* {. A' B) I6 ]+ M; m6 x" m
    {/ t$ _! V* C) R1 W
        return HA_ERR_WRONG_COMMAND;4 G; k) L6 t7 P' {
    }
    ! W! E- D# L+ {
    . G" ?$ l* W& ^- F% H0 F7 |+ F$ \! C4 G3 _
    看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;7 ]4 l% V. K% Q4 U; H$ n

    / y6 W+ D! P! e( g( }) U' _9 O  X3. 调用链图
    / }& c. S  I- c5 H: R这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
    + U. r7 v. y5 a& G2 e7 L$ E& ?) }

    1 @5 `1 G9 y7 N& y8 u4 F* F
    ' Q- r3 a6 G4 d/ f  w  @3 m三:总结4 r* K+ s: k6 a, L: F
    大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。% P" H( C+ {8 s# T5 q  i7 ?4 q
    ————————————————
    6 |- A9 [4 u' Z) n! M/ L2 h! M版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。$ f4 D3 T- U" _. {
    原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415: u% F  W& o+ X' J* k
    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-8-28 19:31 , Processed in 0.416663 second(s), 50 queries .

    回顶部