QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 2860|回复: 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
    一:背景
    * A4 A. l' l4 q! l7 F2 x1. 讲故事
    ; h* `, j1 M3 X0 _4 ~5 D最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
    4 I5 ?' }5 i6 m) ?/ h" f; X! @, F, N: L) s1 A
    二:了解架构图" F, w+ N+ F+ i  M* G
    mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。# ~. X. X1 a8 e* v( i% E' r- O

    5 ]- r* {/ J, `# G3 i1. 从架构图入手
    - J, N% K2 c( s+ M* T8 {大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。' d2 T5 ?. [6 U) o( m. P  v) f

    # z6 E- _6 p+ S2 A4 f9 `( Y; d' |' Z+ c- }

    0 W0 c$ c' K  [( z8 D# s其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
    1 A" X  r& C6 e0 k/ O/ L$ _" C) ^
    0 }, G- R0 d" b% ?$ V  @% D2. 功能点介绍
    9 O! W6 N+ v# O6 yMySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。1 i, s. f% v  ~( {* U

    & r8 h/ C: ~$ Z6 _<1> Client
    # P+ m# ~7 r6 i$ a0 l# [6 s! i0 Q不同语言的sdk遵守mysql协议就可以与mysqld进行互通。; A! {5 v2 e, n4 i; s% C

    # }) S  |/ A8 V<2> Connection/Thread Pool
    0 V1 v% a4 U& ^" lMySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。) x( F+ U; W. y. I6 ]5 s9 n1 [
    5 @2 B& s; U# q
    <3> SqlInterface,Parse,Optimizer,Cache
    / ]0 [- m/ F* A对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。  Z9 m8 ~9 M+ O0 v8 s1 e  L

    " Q( M5 G) {: [: \( z! P<4> Storage Engines7 n& X. Z5 A& a. i6 w/ {3 S
    负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。
    : ~" }8 [* M0 P% P9 Z  V4 }7 u/ Q5 e
    三: 源码分析3 X7 @+ v: c6 H, [" z. R6 G% h, u0 A
    关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。& J" v1 ^" y) b( l( I
    ' ^4 W' N+ T  J# Q
    1. 了解mysql是如何启动监听的
    ) M' f+ {# }7 h; V7 M+ p: N手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。3 c2 c$ t% X" ~
    ( z  a0 x* i# q" [' _) E8 v5 H% w

    2 Z2 D  u, k. _+ i. [" \8 B! b# t- b) E
    从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。* Y& z6 l5 Q. |, K# W
    : z9 s+ o. P' d
    <1> mysqld_main 入口函数 => sql/main.cc
    , K  ?1 r3 l" t) t  R* j& ~7 G5 k3 @- D% ]) E, r: A5 O0 H

    8 c. a: w5 ~" Zextern int mysqld_main(int argc, char **argv);  k  B6 A" _2 q/ Q' f; x$ e. K- ^
    # K' Y& D% R, N5 J/ \/ u5 T
    int main(int argc, char **argv)
    8 l6 o9 F2 C; a# u4 _. H{
    ; |+ i/ ?+ s4 N2 V6 Z  return mysqld_main(argc, argv);
    ( Z( L! V4 ]' i. ]6 U3 I2 a4 p}9 L2 i0 {. h7 h6 O# b
    3 _0 j# Z1 k& \1 w5 T2 w) b

    ) R1 X7 Q( v7 J& y) g. y这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。& L5 k: ^3 D; h6 R( T/ X4 e
    + K$ D# u# p2 N
    <2> 创建监听6 _2 x$ b- t- R% B
    % m& ?) M9 [' P/ Q
    9 M5 R, a0 D3 L$ A& W6 U
    int mysqld_main(int argc, char **argv)
    ' c8 B# j: E  E+ _' n5 E{1 u$ L+ Y. H, K9 _" C1 b
        //创建服务监听线程& T# v' v6 ]7 J& K' Y4 G. m
        handle_connections_sockets();  k4 t9 k2 U, J. [) A
    }
    8 L- `; _4 C2 T2 y3 F/ Z* ^1 Z5 v9 U1 ^) N5 w; L
    void handle_connections_sockets()1 v1 e0 B  y5 V
    {& y) |9 p8 W' W0 g
         //监听连接
    7 v, ^# ^' _3 e6 R# ?. q' u     new_sock= mysql_socket_accept(key_socket_client_connection, sock,0 K/ r: Q# j& h
                                        (struct sockaddr *)(&cAddr), &length);8 [+ A. P" ^  I' ]3 j/ _
    & G  G0 ?. k* T4 W2 P2 _
        if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
    2 ]" I! A$ `; L      thd->security_ctx->set_host((char*) my_localhost);
    0 T3 Z4 r7 `# j. g% M0 T' X$ ~: _4 }
        //创建连接
    , U& o( X6 |! n4 B9 h. F8 r    create_new_thread(thd);) U3 Y- v" @1 I1 }' O
    }/ c  r% t' G2 x1 i

    1 @7 f' h- \% J& J8 d% V//创建新线程处理处理用户连接+ m  i% I  i! I  F* ]/ z+ h
    static void create_new_thread(THD *thd){4 j9 L1 i$ [. V& t' N0 D) V

    5 Y! u7 q9 P8 p& Q   thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
    : _  }% y' u. ~; D) p$ |$ e6 ]$ f" m0 t
       //线程进了线程调度器8 R8 T9 \1 P4 M6 c9 T/ i' P
       MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   
    1 U6 ?7 z* x6 q. C}% ]9 w* E* I' a# L7 E

    4 s  L5 x" m% s! K
    ; x8 ~  B7 ]7 V4 U至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
    " U9 I7 h  Z- X8 a0 O, ^
    : d2 {1 r/ B. j
    & P9 Y& j. R; ~( V& x" a( f2 U2. 理解mysql是如何处理sql请求! i/ M9 [5 Z; b. `' R1 z4 u* Z
    这里我以Insert操作为例稍微解剖下处理流程:7 M3 b* Q5 k- t/ A, M: [

    , }. U; N0 m8 H5 Q8 l. Y) |. d% \, }当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。8 T# L3 `& u# Q9 Q2 u5 G: J7 d

    $ {5 z5 ^$ I0 W- l" X* S1 r
    & |- ]  \, U7 E, lstatic scheduler_functions one_thread_per_connection_scheduler_functions=  `+ r7 |, I" f0 S: S
    {" C) i) H2 D; E$ k5 Q4 w
      0,                                     // max_threads
    ' e. b& N+ J! j- V  V  NULL,                                  // init
    4 `/ o6 E' \- p# _* z; n4 u  init_new_connection_handler_thread,    // init_new_connection_thread
    7 P! O, O  c/ s% ]  W  create_thread_to_handle_connection,    // add_connection
    . ~( a7 z& Z7 u+ r# F: O  NULL,                                  // thd_wait_begin
    , B# P, M  o5 j* s( u0 q. k. Z6 X0 u  NULL,                                  // thd_wait_end
    , o" X2 ?% C4 k# K% p' Y  NULL,                                  // post_kill_notification0 \3 B. c0 b* F
      one_thread_per_connection_end,         // end_thread! T0 z& G( P+ E8 Q
      NULL,                                  // end) a" K3 h' E9 }- x# Z% a7 j
    };% E, [, W/ [8 p2 Z7 [6 b& \

    % j; G' D6 L/ C1 U9 k% H' N9 s* g  p, R! `# r7 @6 C/ |
    从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。- }7 a6 u& ~1 [2 ]9 E: {& }2 P
    , @; w6 X$ H* [0 z' w
    <1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪" g. I, |& F) Y, e+ ?( X
    ; O- w+ O- s& r% C9 E* r4 E! S
    void create_thread_to_handle_connection(THD *thd)
    , S" P2 _8 s5 E6 Q6 o! `- R& V{
    ; r: c6 n$ X2 d; s* p0 S# _3 G     if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
    + c! R+ z) \0 H& T) S* q                                     handle_one_connection,(void*) thd))){}
    . }& g$ o2 G# s: i+ I}
    ( s" b4 t& y) r# F: \3 S//触发回调函数  handle_one_connection( @3 ~) f: W3 L. H
    pthread_handler_t handle_one_connection(void *arg)
    6 B4 ]! m8 u: X4 X% g" I{" i" `2 p3 O, D- m, f: X
         do_handle_one_connection(thd);; Y  C* b& v4 e0 F& z% G
    }  q: v1 ~) J' ^/ m+ c+ G5 |
    //继续处理; m6 j/ C! G8 g/ z% z  Q
    void do_handle_one_connection(THD *thd_arg){& \+ H6 f6 e; a' V! G
        while (thd_is_connection_alive(thd))+ d$ w! [; W0 O6 U
        {! l3 L3 D8 Z$ l* E$ C2 ^5 v+ o
          mysql_audit_release(thd);. f/ S1 f8 N! \. z$ t
          if (do_command(thd))  break;  //这里的 do_command 继续处理
    / c3 _8 Y0 j: W( j) E    }
    , Y# W8 J* B7 c5 J# y}
    & }. `+ A+ N; a- ?, ?7 H+ K//继续分发
    : y* K9 N+ o3 M8 t' E8 ybool do_command(THD *thd)
    8 X# V) x5 w3 v{
    ' G% M* @( u* h, E2 a  T( @0 R    return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));" \; e, Y4 a2 c( J+ A& D7 [
    }
      M+ r1 G3 h' pbool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)- W* n1 |+ Y4 F: Q; ^
    {
    ! o, r$ ^- R. y/ `6 X      switch (command) {
    & D- i% ~: a7 K4 c0 f! W         case COM_INIT_DB: ....  break;- `2 M3 M1 _! g0 b1 q* g) U* W
             ...1 b* K) c9 X: B/ ]2 D$ x5 o
             case COM_QUERY:   //查询语句:  insert xxxx
    * s; }! C& @  m" P& `% m% i             mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析
    2 P8 U; K1 G" g2 t           break;
      U% J: g7 S. O4 |( v0 v  i      }# g* M9 L* `" ?- F2 @6 s; J* l7 u; ~
    }. Q; N5 M& d+ F& T( b
    //sql解析模块- b+ L& L( }8 G! a! \+ `
    void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
    2 }' L5 B; K) v& T{  C2 L0 q/ R* _  t+ f
          error= mysql_execute_command(thd);/ f" _# b% P6 X" ]6 E7 }
    }
    # H: o$ f- U# g7 q' O$ M3 @, M. F. {- [5 f' T

    3 I% |) \( r3 H' v' t' `<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。' O$ w2 Y7 f5 m) ~) P1 I2 z5 A
    . I/ _7 z. M, G; d4 B, t1 G3 b9 J
    //继续执行. B# s! W# n) {( h! Y6 u: p2 l
    int mysql_execute_command(THD *thd)- @9 z$ J3 A% @8 R  p- t
    {5 ]3 {' u; C1 F$ y: X7 l; A. g9 ~8 Z
      switch (lex->sql_command)
    3 v2 c! s; o( S# i( @/ c' @  {  |! S6 d5 D; x, Y  t# b4 K7 a
          case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;
    2 S+ _% G6 q4 P; b9 g9 Y* h
    - G( o5 K4 K, i8 k' O( b, R' g+ @      //这个 insert 就是我要追的
    ; _- Y( ]' t; ~* F# X      case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
    * t8 `$ Q& c7 S                                              lex->update_list, lex->value_list,
    , W: r8 f6 p  b                                              lex->duplicates, lex->ignore);
    4 D. t+ m. u* w+ i6 \  }7 G6 x! l, M% ~* j  J0 f  w" e
    }) {) e+ y+ E  `) V3 S9 B/ x8 E
    //insert插入操作处理
    & V3 K" E0 ^: nbool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
    2 U; B" o. a2 t# O                  List<Item> &update_fields, List<Item> &update_values, 8 w) @7 C% D- p1 V  C  _
                      enum_duplicates duplic, bool ignore)1 l% z6 s" s' @
    {
    " F3 D' o  t9 c( N3 B; P6 o      while ((values= its++))
    ! a4 O* n5 M" x      {5 p4 i1 ~8 X) R- ~9 b# z$ l! m6 l
               error= write_record(thd, table, &info, &update);
    , O- N8 Y8 ~5 B) p      }
    8 M+ h: _+ N; x6 `3 e5 k: Z' L}
    ( N( E9 ]& p% q: b2 O% X//写入记录
    $ W% O5 \7 g7 l% d/ v: w% d* o# tint write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)* @6 Z7 t5 `0 @  @5 J$ S
    {
    ! K/ v( Q2 M4 T1 k% Z* Y+ \    if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
    & [- S1 B/ x6 D: P    {
    - L# h, ^9 y) r% N& I         // ha_write_row  重点是这个函数; s) B5 j3 F: I! i1 I: z6 L
             while ((error=table->file->ha_write_row(table->record[0])))4 \9 {: D0 L0 W& k$ B) f0 ?
             {1 i& q+ s: Y. o6 n6 K" Y
                 ....
    + `) w9 T6 ]6 i         }5 R( e! r) Z+ l- p$ T
        }8 l5 L$ Q( M- A! }/ n
    }) _3 F4 K% m5 W( c! a4 U. Z

    , |2 m( b6 K, n- Z& |* v: z9 ]4 Z% C/ n% k+ X: ~2 j, @

    4 F. r5 s$ m0 c. H, Y) n可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。& W& k. U- c6 \5 V$ m4 b
    5 Z3 s8 I0 L6 A) p8 T
    <3> 继续挖 ha_write_row
    - Y! w' y/ C2 D& q
    4 i+ \/ v; M8 q% j' }3 Y' ], I' oint handler::ha_write_row(uchar *buf)
    - D# c) s; Y  A3 E7 O( V/ C. ^+ r{
    8 t% [+ X0 o; F# P/ x- h" J* m5 Q    MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
    : J" F. \# \8 D' x( ?}: I+ q9 d: k  L/ J6 y
    - {; |. C9 ?$ z; N, |" S
    //这是一个虚方法7 V+ x/ R1 p- l4 d2 ^
    virtual int write_row(uchar *buf __attribute__((unused)))6 X6 O* c8 i+ I7 K' \! l# F2 W
    {
      o- X  D/ }$ {    return HA_ERR_WRONG_COMMAND;
    * \. E! G$ U; M+ o9 V}$ e- G. R+ M; U* Z4 o! ~

      h. M. `! M8 }4 ^6 b8 r) g# l' o9 q( r* ^) g
    看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;9 V- R6 B9 v8 m7 q2 [
    ! v6 o) n/ ]( ~9 H! Y
    3. 调用链图' u0 q5 a" g. W1 h5 R7 v9 F
    这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。9 p) K: a2 ~$ g; H! B5 @, K

    " }1 c: c7 P4 T& k' t
    ) k( F" n8 Z1 U% |: T; B: e
    & g0 M2 d6 }+ Y- W" L. ?1 I- [三:总结8 Z. u& s' }9 g$ x6 V
    大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。  K9 p9 `( ~& }9 Q( K6 C
    ————————————————
    " P$ C8 ^4 d5 p! i3 ^版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。% r9 {( c$ `( f! ^* X
    原文链接:https://blog.csdn.net/huangxinchen520/article/details/1064874158 U) X8 I9 a; D8 c- H4 [
    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-7-24 16:56 , Processed in 0.344683 second(s), 50 queries .

    回顶部