QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 2971|回复: 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
    一:背景" D. I. c( X) D$ r- A, a' i: b+ A
    1. 讲故事
    + Z- ?! I; Q1 \最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
    ) J: W  A) b  ?( }0 X! _- Y" U
    . `3 s% P6 l; T1 ~; e) X2 Q二:了解架构图/ g/ o. o, Y) C/ [$ e# E
    mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。: I$ d6 \% J! _

    5 ?8 h/ u) d5 O/ Z8 a% k1. 从架构图入手( n$ \5 |6 E0 D8 j% _8 V
    大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
    / O# S$ M( ^7 e- J
    $ s/ C6 a" f9 t% o. r% o7 K5 R, }$ `  k& L) ~" Y
    8 u# Y7 Z% z$ ~$ m* o
    其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
    . _( f3 W1 ?# [1 n4 ^4 F1 g" m% x8 {! V! d( Z: D
    2. 功能点介绍5 m% [2 E! d" c  D5 k$ e" N' a% L
    MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。% y% O6 @( K) X) `7 H

    . ?8 {! b& E6 g% g& W- u+ E<1> Client
    $ E3 I" l* h$ v9 h& O7 P; P+ K不同语言的sdk遵守mysql协议就可以与mysqld进行互通。! A4 ~1 {2 R, \& W/ [
    + W' d" ^4 G2 X5 s
    <2> Connection/Thread Pool( [/ @: p# e& r4 Y, \, B; z
    MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。7 X: o+ Z1 s" a- P& m$ w0 V

    ) F9 u- I( _/ Z1 R0 m+ ~( {% s<3> SqlInterface,Parse,Optimizer,Cache! A6 I$ E5 q% P) V
    对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。3 d% c; U6 O, d+ Y' w
    4 L3 }3 X! `1 l2 `
    <4> Storage Engines2 e! V2 v  P) y7 r6 ]8 I
    负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。; c1 r- `+ j: e  ^

    & S. w5 y* g9 n4 e" k! d) z% A& Q三: 源码分析
    + S* A; k/ T% h# o) @关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。  E6 Z% @6 ~, E5 j. c

    . H" k2 I9 N( U2 z+ D0 i8 H+ c3 D9 ~1. 了解mysql是如何启动监听的
    5 b" T' j3 f7 x8 n; t( B手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
    2 @) k( C4 `  v  _4 B: E( G2 m6 D# N* B& c0 w( A1 \, N" F

    0 c( A3 v6 Y4 P; x" I8 H% M0 {: [5 h7 o0 _4 q
    从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
    * L  M+ @8 n9 Y* K  e  _
    6 }+ x, q2 F) W% X! A( H+ Z<1> mysqld_main 入口函数 => sql/main.cc/ W; m, X; M0 w: x8 d

    8 K" `2 H7 J0 o9 K6 J
    ' g' S1 b# P. n5 G* uextern int mysqld_main(int argc, char **argv);
    ) Z6 c" O; a, i
    ! V# y7 W( M3 ~$ `( W' s" E* {1 Pint main(int argc, char **argv)& z' _: K* m8 @' F# b6 i
    {
    " r3 q4 x# s; [" a4 h* G2 ]  return mysqld_main(argc, argv);
    - P( k( F0 H+ S# j9 p}% Y6 g2 m0 [8 Z0 ?+ O' [

    2 h+ _& t; R& f" l$ ?
    # d  \# e& Q  W, M* B这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
    4 f1 b- r) b; S) Z' Y+ B  \
    7 f) t: g- b& ]( G: s<2> 创建监听3 e# _# p$ V9 H, r' M) a
      h$ g$ S  s7 [

    & t) U0 U9 k) G6 e3 Q2 q, l  `& P( K& Wint mysqld_main(int argc, char **argv)
    0 s- I% E3 B1 Q1 |{  ^) O2 c( V. J  D6 R8 q$ r7 n, L
        //创建服务监听线程8 \, T+ X  b# b) N, i# ]! @' v
        handle_connections_sockets();
    ' o2 Y% g0 s, A- R  ^5 Y3 a- z" d}" V1 J6 t2 H' |2 w; L2 f! M

    - b0 }( |% E# nvoid handle_connections_sockets()7 b8 j0 N2 P  J/ l  b
    {/ c4 K# _, T% N) U2 v4 h
         //监听连接
    7 S) f( e4 N* B: |3 d! Q2 y  p     new_sock= mysql_socket_accept(key_socket_client_connection, sock,, h+ E' S- v! H3 ?
                                        (struct sockaddr *)(&cAddr), &length);
    2 \0 h: ~) _  k) p, A" Q( v$ }
    8 C. E- t! K6 q  Z3 ^3 [1 n! I    if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))5 j5 ^4 L/ A9 ^/ t4 W6 M" ^' x
          thd->security_ctx->set_host((char*) my_localhost);
    9 b& {) n3 b+ V  B0 F  k- U* {5 G
        //创建连接
    1 D5 @+ c9 \* V6 T( z    create_new_thread(thd);
    $ D9 G2 _' A8 A* d) _3 V; S}
    9 E* |1 q, v$ A2 }$ R5 s
    - r5 C6 N7 B1 s3 h/ v$ I/ s* k+ Q//创建新线程处理处理用户连接
    ( a; @4 P& B2 c) `  W4 m1 z% Pstatic void create_new_thread(THD *thd){
    : K3 {0 o$ E. S6 u( F. Q9 ?$ E/ M
       thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
    ( I. v6 I4 Z% N6 T4 g
    1 M& Y( B. U7 J7 E* ^: M; o& u   //线程进了线程调度器: E: T8 f( X+ e5 t" u
       MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   
    ( w1 U; ]( w7 c+ _& x' v( |}
    : w! D% c: l! Y0 I# \3 N" }
    8 S0 u& f4 f5 t1 F/ s
    : z; e$ D' }/ v5 L( q+ L" S至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
    & C. W5 D2 \( [" n. m+ F3 A8 ?( B" D" c
    : r/ \9 Y8 s! m& U% w
    2. 理解mysql是如何处理sql请求9 R, R4 [& {2 O; }- k! L
    这里我以Insert操作为例稍微解剖下处理流程:
    ; I+ _# g7 c7 `9 B# R: g; R2 d3 v  @$ [, L
    当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。6 S7 X, i/ e5 n0 E
    ' k% Y. \. {/ q+ L. Z5 r; t$ o

    # a0 r$ S* g9 k+ @$ w) l3 a5 m/ M/ Astatic scheduler_functions one_thread_per_connection_scheduler_functions=4 ]% i2 r! H3 v- r# [( V: p0 G
    {1 D+ A, f3 W9 g
      0,                                     // max_threads
    4 q: {0 I+ D5 w' }+ Y% S2 p( r: `  NULL,                                  // init3 A, R3 d+ ^6 z
      init_new_connection_handler_thread,    // init_new_connection_thread; r. t! K5 [5 K
      create_thread_to_handle_connection,    // add_connection
    $ _7 c; s; k: s* s+ f  NULL,                                  // thd_wait_begin
    1 f* C# \8 a9 }) e4 {5 i. A6 ^  NULL,                                  // thd_wait_end
    / @' l6 c: s% B3 M/ S5 Y4 v0 y  NULL,                                  // post_kill_notification
    ! v& T7 h6 Z# m1 A8 F) P  one_thread_per_connection_end,         // end_thread& b, x3 r5 e  g& A7 b2 ^2 x& L# d: q
      NULL,                                  // end$ j% L+ B) y$ Z, E8 X
    };' c5 ^2 f% P) V

    ) `& f- O3 g' x% V3 ?
    + X5 W, N/ a3 ^- `从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。/ _8 n% L1 `7 c8 o" l0 l& G, `! D

    : _$ C7 _* d' H' E  F3 q<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
    4 Q% R8 Z( a: I- M8 i; x! ^
    ; |* a, J) T  d# k* w' r+ qvoid create_thread_to_handle_connection(THD *thd)
    " ?- s. a1 o% A+ T- W5 W{
    7 k# _. Q2 m& P, `& `0 M     if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
    ! p. i8 N+ c. F, N, k' Q7 }6 W" l                                     handle_one_connection,(void*) thd))){}
    8 J( n5 Z$ }% l. s3 q}0 t+ S+ s& @4 A- V2 P
    //触发回调函数  handle_one_connection" K; ~. ]' I$ |; G5 r
    pthread_handler_t handle_one_connection(void *arg)# e* J3 \' f; m" e5 a2 l8 \
    {6 w7 k! k6 h+ x. v% R; R# U' t' g3 c
         do_handle_one_connection(thd);
    # }# H4 ?) n) ^}2 v- v6 Q- e1 R9 q' E* q3 F
    //继续处理: r: u0 R9 A* A2 C! o5 f" t
    void do_handle_one_connection(THD *thd_arg){/ S' p4 Q) |! ]# ]9 \
        while (thd_is_connection_alive(thd))
    , t- i9 o- I; x4 F8 F9 u( p- Q    {
    4 U8 p) u1 {$ @% u8 ~$ j      mysql_audit_release(thd);! b: C( a% e& i$ g) n0 R
          if (do_command(thd))  break;  //这里的 do_command 继续处理
    # e. u8 m+ M2 {4 h6 W' W9 h    }
    $ O' d& Z* G* k) j: o}! B+ |( G. H1 ?% v. q- f8 d, |
    //继续分发
    ! u) z) i2 s7 m6 q* rbool do_command(THD *thd)
    8 R3 `$ H* t' N7 W  ^+ Q5 e6 `{
    7 M% g6 N: W& Y7 o/ A% N+ ^2 @/ ~" [    return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));) u5 c" v+ W& o9 D* v3 }& Y
    }
    8 R: {% h9 S2 Lbool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)! U; B! S: p7 B2 p
    {+ |6 ]' X8 p6 T" A$ K; Y# f# B
          switch (command) {
    # S2 G! E7 D4 V6 [% ~. O6 J         case COM_INIT_DB: ....  break;9 R  A# p7 R6 W) A4 T
             ...) r: C# f. h, N
             case COM_QUERY:   //查询语句:  insert xxxx
    ' n# b$ q7 G  n. a$ i+ ]/ O, W  g             mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析! U/ U7 d9 {4 v
               break;3 F5 ]- T0 A7 w/ n% T2 }0 b9 l
          }
    9 _: |; b! K. k0 w/ v3 Y5 B4 x1 G}; c- {0 t/ H9 d6 Y& |
    //sql解析模块
    / m" t+ e- K4 J- _& {: J) lvoid mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
      y6 b. h2 o4 I" d; G{
    ; x  w* p7 u6 E, n! x/ J8 a8 W+ t: B      error= mysql_execute_command(thd);. E' b" e2 ]# h! }( I$ w: r1 t% ^
    }# U+ b8 J: U# N1 x1 ?. Y, D
    * E5 S. i; a# H: B% m, v

    3 n: X2 t: a! \& p- [<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。- ~  b, X8 u3 `
    # f2 P$ ^# C* O0 R0 Z" q7 Z
    //继续执行5 n& W% a9 S, i* L
    int mysql_execute_command(THD *thd)
    , O( f; |  D- W' B5 V{* ]) T. s# E( B6 I& x
      switch (lex->sql_command)
    5 o5 _* N$ @. p3 s  {
    4 `+ I+ H4 T) m. y3 A' u      case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;+ v) y# c. _+ k: e* [. y
    3 u+ V4 _) Y1 V. `3 D% L5 N& \: x  R
          //这个 insert 就是我要追的
    / q6 _* x# e) m      case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,0 h2 ]. h( E; U: C5 s+ r% j
                                                  lex->update_list, lex->value_list,: N$ p7 Z7 v9 p1 y/ \
                                                  lex->duplicates, lex->ignore);, P2 s3 ?5 _2 I4 C$ @$ p7 ~
      }) B( v- R8 ?7 f# @4 K" N, u
    }
    1 f- p7 ?$ o3 Q$ ~# ?' J+ M5 x) ^//insert插入操作处理% K- w2 ]1 f0 i$ [6 z
    bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,/ h3 O1 q, v; }7 I! w
                      List<Item> &update_fields, List<Item> &update_values, / [/ A' m! Z* e6 @* D6 w4 R5 r
                      enum_duplicates duplic, bool ignore)* u" E/ x% S! @
    {0 a% X. F  s; a: l+ A# x
          while ((values= its++))
    ) H' U3 Q: @  G1 B' ]! e% x. o! @      {
    ! c0 Z2 C/ |- |& a           error= write_record(thd, table, &info, &update);3 s6 P& P& P( H5 ^' H
          }
    / S4 J7 c: c# F1 Q7 Q) ^" ]1 g}
    7 p3 ^7 Q/ `! U/ @! d) \$ q: S//写入记录  c% j) M5 c4 ^
    int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
    . S1 y/ n% ^, h$ o2 y{
    $ J" g, F3 O% @8 j; V0 Y( j# G    if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)" C! v% b$ \# h; l0 I
        {8 w8 a( _; x$ L% }5 P3 E5 x
             // ha_write_row  重点是这个函数+ F# ?4 J' N" ~  a7 v. p- T7 M* e
             while ((error=table->file->ha_write_row(table->record[0]))); p8 x% h% T3 G
             {
    # r6 I1 N6 Z6 E6 y4 V5 Z9 ]             ....! }* z6 c1 F' y3 }6 m; ]0 V. b
             }8 t+ R' A+ Q3 z- {# x
        }
    . v0 I0 l& S5 G- j" \: U& R+ [}6 j3 F3 y  ?3 K% C4 Q
    / w4 `. M( v8 m0 h) a# P1 L

    4 e( f; X0 `: M) z- S! e  }5 x- L- U# D/ Y+ t: @
    可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
    " x6 o5 b1 s/ e6 Y! a' N0 @3 o+ n. _+ T$ Q
    <3> 继续挖 ha_write_row% ]9 Q2 q6 n/ ^* a. B: Y
    5 K+ `# D5 u7 z: a: _  P
    int handler::ha_write_row(uchar *buf)
    ( I' {" b6 g1 }4 ?) [! V8 y6 x{6 t" r3 w$ `0 g3 z- o* w
        MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
    " `, W: X+ B& _( N/ {3 W8 t0 K}$ Q$ K6 ^( C# ~! R2 A% ]2 ^* Z
    : q' h; D) K2 }% X4 ^& g" |* S
    //这是一个虚方法0 i0 f! A  J5 f# B$ C5 n
    virtual int write_row(uchar *buf __attribute__((unused)))
    * J' V, v1 @! a1 ^3 E{
    $ \' s  z: u3 U2 z5 P    return HA_ERR_WRONG_COMMAND;
    9 x9 P1 p& F8 O2 o$ S}9 Y) v5 C" ^8 A) u$ s  R( q& P
    $ b' t& e5 |1 _9 q8 v, ~

    " V) ?. p$ A, B% j看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;1 ?( @" |* B2 R. p
    ! b! t# `1 a$ |$ q/ Q& a! p4 m( w
    3. 调用链图3 |; {$ T1 B) D& K
    这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。) S, j5 x/ E" w3 e# h

    # a. L& G% _/ ^4 s) [; [, r7 Q( y2 [8 ?$ V5 g$ J# ^
    ( B% A" j( m5 i" B0 c
    三:总结
    : \& A: y! X8 G6 z( `% O; B大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。
    9 {/ J, h7 u: E# g" ^9 z4 x* G! x4 v————————————————
    9 T1 s- z0 q) M! |版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    & c$ F; @& k# K' H5 j( f* y2 @, G- A* y原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
    ' }  m3 \2 k+ i1 Y
    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-13 20:24 , Processed in 0.521231 second(s), 50 queries .

    回顶部