QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 2759|回复: 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
    一:背景% T# P: D  A% D3 c7 `4 m
    1. 讲故事, F2 K7 K: m: p$ }: X
    最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
    6 [7 B" O  z! O. Y8 ]+ C# Q* T. X# D2 F! q% n4 K
    二:了解架构图$ I4 T$ ^4 C6 R& j1 H) m
    mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
    9 X9 G0 L/ Q: Z8 M3 D/ T0 a8 s  k: ^' h* W
    # A2 {, k% f, ~9 F" j+ y* {1. 从架构图入手6 s7 w7 [: D4 \& A& f! O
    大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。4 w2 }3 R$ D) t4 o4 [

    " b4 R' [0 O( x' D/ l9 }4 F$ u8 H$ Y: O3 m% H) m6 t% n  [
    7 a  d1 n5 H4 B! c
    其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~' ~+ r6 A7 ?9 l5 m+ H- Y; I
    5 N# b  l2 ~; E# q3 }' \
    2. 功能点介绍
    * R, K& y5 b# OMySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。' z& Z/ k6 j* P

    # V; `# B$ o3 F/ p<1> Client
    + A6 w1 ]* o- W. r( l不同语言的sdk遵守mysql协议就可以与mysqld进行互通。2 v7 _, S* B- L% f# ^. Q% Y+ j2 U
    ! R' p# f# R3 ]7 Z
    <2> Connection/Thread Pool# t3 I8 m* J4 A5 H4 a
    MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
    0 M6 B2 i& A6 d9 t( w  i/ _" X  |' {& Z; p
    <3> SqlInterface,Parse,Optimizer,Cache
    3 @! x4 e; ?* M1 ]& @对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
    9 S3 d" b+ D% n4 j/ |# j8 O
    + n; ]- G4 P( _5 h$ C/ O<4> Storage Engines# N$ |, {1 a$ }: W5 v  {' M) f- L
    负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。
    % U& O5 H9 d* [5 L. B4 O+ K+ M  T- u2 A; r
    三: 源码分析  n9 X3 ~6 o. _
    关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
    0 F1 Z$ N% M! n( j
    ) m" }5 R3 v" `: i1. 了解mysql是如何启动监听的' d& e/ O: U+ h8 ~3 Y9 |
    手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
    $ [$ `1 T  U7 F) U$ h: k$ W( e6 N! I" z+ V5 k7 ^. y* ?; s

    6 I# b1 I  I2 m) w5 L
    % @1 t) c  H. k: D4 q  @从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。/ h( z2 R2 |1 D+ ~' b

    4 `: l8 e9 v# P& N% G! A. u<1> mysqld_main 入口函数 => sql/main.cc4 I8 `* ]8 o5 t6 ?9 I4 [
    8 h7 i9 ~8 L) u( {% J) g
    ' S& \2 ^& [& U0 t# k/ O
    extern int mysqld_main(int argc, char **argv);
    1 k5 L8 o/ v( _+ P( C5 t! W9 A$ r1 v7 Y& w9 ?) m' n; I8 X
    int main(int argc, char **argv)
    # f8 S; G+ O, ]{" j/ y  {. W7 Y4 `1 M6 g& g
      return mysqld_main(argc, argv);
    ) ^' |( A/ N1 {" r/ _}
    # o. v. B) I# N7 R7 ?0 t1 f/ D
    & g2 }, [+ g9 G6 B5 `: y
    7 v! t+ R; @- O/ g这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
    & s3 b9 S* f* i7 r  x1 t* W/ h$ |; U# A+ i/ d
    <2> 创建监听
    $ @6 C' ~, r( T4 Q, y
    9 t5 G& f7 e" J
      B9 b; `6 e1 o/ e* d( |) q! hint mysqld_main(int argc, char **argv)
    6 k/ ]9 b# Q: P{
    * x1 v+ A2 r0 V% W( R# |7 |    //创建服务监听线程* _3 n% k- p. n  p  ~5 O0 k
        handle_connections_sockets();3 w8 O2 |" [+ m, S2 h
    }
    1 v: l6 T8 }4 H% c: K" n) z5 f& M* n. j8 Q6 r
    void handle_connections_sockets()
    1 o( K( U8 I& a  C7 Z{
    . q" y0 Z8 Z/ k+ Z; j0 I1 ?/ N8 E     //监听连接
    1 C0 q: e3 f/ \" ]     new_sock= mysql_socket_accept(key_socket_client_connection, sock,
    + `' X, x. [) P3 k& S9 L1 F                                    (struct sockaddr *)(&cAddr), &length);
    ( w& k& z, G3 r! h8 ]0 e6 |7 D  s1 K% K* g# f8 n; M
        if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
    6 }- M8 n8 j( o2 p" l( a5 O1 c      thd->security_ctx->set_host((char*) my_localhost);6 E+ Y$ X3 \3 y

    & U+ a1 {& e# Q6 \9 u# D    //创建连接! e" }( J5 {4 G0 e
        create_new_thread(thd);, E& B' n8 g4 R7 k
    }5 J" ?9 ]! \" |6 u
    2 _! q  x2 n8 p: F& z0 R. n
    //创建新线程处理处理用户连接9 ^; j, Y/ G3 j. l  {3 G
    static void create_new_thread(THD *thd){
    ' D$ ?; P8 }, S+ l0 X
    . i9 e0 l+ k0 _; ]' X: p) R   thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;" F& |7 g6 n  k

    3 q% |' U, D$ ^7 w   //线程进了线程调度器  \. u. e6 L; d
       MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   4 l2 M6 Y2 ~1 g$ S% a1 J) W
    }
    ; ^4 |7 L0 E' m/ B* t: Z' v9 l4 F( `! c0 `0 U
    7 @* x5 i5 I! [; J' C+ ^
    至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。! e) T! R; s9 o: C  W8 x$ G
    ( [: d: E: M" J$ x
    1 i; M" v/ |- m  [5 l+ I
    2. 理解mysql是如何处理sql请求
    # F( n: k$ I7 G4 I4 P5 c! a& A这里我以Insert操作为例稍微解剖下处理流程:
    7 F$ |9 _3 ?3 J6 P2 _7 y  F1 B( L3 b* l' l
    当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。5 j1 x3 ]; x7 k9 l, Z

    8 ]$ I) T4 \, n( N4 w- }+ {/ [+ F. s2 x! b1 d
    static scheduler_functions one_thread_per_connection_scheduler_functions=2 x7 d- t. E' u. [! |( g
    {8 n# f" Y1 ^2 U$ B1 ~. \- H
      0,                                     // max_threads
    7 T8 y- U' W/ h3 a  NULL,                                  // init
    + u$ V3 i3 ?/ N' \  init_new_connection_handler_thread,    // init_new_connection_thread
    # U8 ~" B7 ~9 P" R* l" ]7 F  create_thread_to_handle_connection,    // add_connection
    5 t* z2 }& W& k: V  NULL,                                  // thd_wait_begin/ V: s7 Q/ h* f) D" ?2 k
      NULL,                                  // thd_wait_end& ?8 C# Q; l! [$ W
      NULL,                                  // post_kill_notification- e; I) [# v- o  u# D/ r: T
      one_thread_per_connection_end,         // end_thread
    . ~4 b) Z8 H" T9 K2 F0 O) G  NULL,                                  // end
    . r. X: x$ i$ {$ O- L};
    / i- \1 s# Y- A' e0 g+ P
    / \  _# m# i3 X8 @
    8 a/ l( {* z3 b0 }7 x6 z, {) I从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。9 U. o$ F: D& C/ n, k" m2 y

    $ p1 w# c& N, c0 D  q5 i4 [<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪2 O4 e1 p8 A3 w9 ^

    : {" y" Y. f9 o: J4 Avoid create_thread_to_handle_connection(THD *thd)8 C0 r8 g8 n, ?* a4 ^! Y
    {  q, B& J. l: p
         if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,3 u8 U) T: m0 w  B! e" l& r
                                         handle_one_connection,(void*) thd))){}2 r" @7 L: ?0 W  Q
    }+ n8 d: F4 y  L( Z& P) ]. e+ f. R
    //触发回调函数  handle_one_connection
    , [, o) H/ Z% n' M7 zpthread_handler_t handle_one_connection(void *arg)
    + a2 [# H. K$ T5 y/ X4 ~) ~7 Y{
    8 d# v' e* a8 @: \$ o     do_handle_one_connection(thd);
    ( E! P; j) `# q( j* z! g}
    ; n0 j& e2 U# s/ Z7 H8 {//继续处理
    6 v1 K/ i0 m' r9 s6 ~+ @, R% tvoid do_handle_one_connection(THD *thd_arg){1 N' V, S" x8 ^9 k1 j0 T
        while (thd_is_connection_alive(thd))' ?  d- P* S0 Y2 t) n. _3 a
        {
    1 B6 R. k  o' \2 i& T      mysql_audit_release(thd);5 ]: P/ L) @; y
          if (do_command(thd))  break;  //这里的 do_command 继续处理2 }( ]" K* T/ v. R+ z" N
        }" G9 _( U4 f% C8 v6 R7 @1 v3 Q
    }
    7 Y$ K5 c: x' M$ O/ A9 n, I//继续分发
    ! N/ H$ W" ~$ u4 k! N+ Q0 abool do_command(THD *thd), A8 G2 i- C0 }" ]2 t2 f
    {
    4 ?7 I% n& k; ?2 ^: s    return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
    2 d/ S8 W2 L$ Z& J# ^% ?+ w}
    , s! X" |( o8 \bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
    : F0 `$ c# x8 \8 u{$ O2 z: z0 `8 y+ i/ i
          switch (command) {
    , @$ v) r: i. W2 C         case COM_INIT_DB: ....  break;2 d0 w6 C$ @3 E  f" ]7 f
             ...8 E" M9 L) l! w3 ^
             case COM_QUERY:   //查询语句:  insert xxxx  [& ]  {% E2 O3 X. D
                 mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析
    % D2 C7 x0 _7 [) n9 U/ g           break;+ e- R$ f3 R* d
          }* m6 B0 Z5 d' H
    }6 J7 [* b( J* J4 @7 X  m
    //sql解析模块
    + g9 q! e' J+ H/ bvoid mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
      M- D& A) R/ b. J& C5 G6 O{6 O" Z) A2 `/ G$ p0 @
          error= mysql_execute_command(thd);$ Z  d- o# `1 w; X
    }5 V5 ]" Q  r- z  v0 u5 }$ I
    ' v/ C( U; Z6 {' ?) v- N) e% D! H: [

    ' `5 u8 W+ P, Q; F, K( @<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。& @3 Z$ E, L. U9 R! n3 K

      k/ `. k0 Q7 t( g//继续执行
    9 Q, z+ C3 h. Q, j6 C9 @+ p2 y" jint mysql_execute_command(THD *thd): p  K# H7 F# z% r- I
    {
    1 \6 F1 V0 [8 z8 E- Z& }  switch (lex->sql_command)
    8 O) A3 ?  ~- Y, n  {  ^5 U4 o( G0 H
          case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;  ^) C0 z# l1 W2 k5 `1 [. Z" z2 g$ Z' C

    3 \) S1 j/ j/ u) S! j; H. P      //这个 insert 就是我要追的" m9 f. Z  J( u4 b0 D1 w
          case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
    / K  N; I2 A/ G% \& g* |                                              lex->update_list, lex->value_list,
    5 Y$ m  E7 A3 t% b  Y7 Q                                              lex->duplicates, lex->ignore);$ P1 X# k% P$ B$ B- x& `$ `5 }4 K! l
      }( M6 f6 g. s0 P  H( [
    }
    % v/ ?( j+ \# ]5 r6 }//insert插入操作处理
    9 i' d- g/ c& y: `( w7 ^bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
    ' i0 i/ g) p! {* j' C                  List<Item> &update_fields, List<Item> &update_values,
    : |* L/ e/ h0 l$ D5 P                  enum_duplicates duplic, bool ignore)
    4 A8 k2 N! E0 i' p, E* _{
    ; m/ R6 `' N3 t      while ((values= its++))- t2 i3 P7 C& A; l
          {
    7 z. P/ I9 B! \: C7 Z           error= write_record(thd, table, &info, &update);
    4 }9 R0 T% D6 R5 `: v* K! ]/ B. B9 P      }1 L6 y7 }3 ^+ H+ }) |4 g
    }
    & g+ ?( Y7 Z( A3 U* w//写入记录
    ( i4 H$ w& S* G$ x/ b' B  Rint write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
    * r& f! C% D# k: \3 z  m{
    4 `2 p3 f- Y" J: N  U+ Z    if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)) e$ T5 Z9 Q% e& c- y
        {
      Y9 i/ J- ?5 d! o         // ha_write_row  重点是这个函数, h6 j+ p& I' f7 M" A5 B
             while ((error=table->file->ha_write_row(table->record[0])))& m0 p) X& a! d. O
             {: k2 ^8 s  q6 F* g" r# l8 }$ [, E* C- o
                 ....
    ) w* Y- w) G5 B# p7 g! `% L( a/ ^         }
      _, S, ]! v6 m    }
    / b8 a  P) V$ x& r% J1 E* s# c}
    7 f' Q3 L" s( i5 S  H: Y4 `; C6 h  }' V/ G7 h3 u. N

    8 \( l7 g* Y8 b- R& @$ j
    : c4 q6 E$ W5 }/ [2 }; x& p可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。) N3 B* Z) d- P5 s6 S2 _" W
      z' e9 t) ]3 B6 Q. H: W0 X
    <3> 继续挖 ha_write_row% g# u9 a' L& d* k# Q& d

    4 r/ t6 u" q4 X1 ~4 kint handler::ha_write_row(uchar *buf)
    $ X1 G% u+ O8 T5 i2 X' x{) k) s6 I* L% k+ Q2 r+ y- z, v
        MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
    8 F( x( n) j: ^+ O, U1 g}
    : q8 [) j* F8 K: o7 H' d9 ?4 J
    1 f9 D1 E1 H  Y, |, S//这是一个虚方法
    & r' h7 t: |1 [0 ~. L% Lvirtual int write_row(uchar *buf __attribute__((unused)))
    0 u' ]6 `4 L/ ^- J7 z1 `$ n' G0 C{' i6 |" M8 g3 c1 m
        return HA_ERR_WRONG_COMMAND;' {4 Y4 x' B+ u1 S
    }
      t+ y9 P5 m. `1 t* {( ^7 e4 a% T& A2 P

    - x5 Y$ u! [5 O: w& }* T! D0 ]看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;$ d! B& ~, V$ o! F$ I9 f5 L

    ; O) o8 f2 \! l  a' T/ u/ _3. 调用链图
    # I! x6 Z  G& q# X2 f' ?7 q; ^3 {$ C这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
    4 u% R6 H* a2 K4 h  a# d% a$ l3 G; D* s
    ! C( U- d$ D3 G  ~# q6 X

    ! [0 Q3 Z1 {: r' N  S三:总结/ j$ L3 `. U- b* W6 G
    大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。
    ; {. ^7 L  K% C* f————————————————2 A" Y. D% u  L$ p
    版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    0 U; C" s6 d' n3 c! p原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
    : y: {% v; {# q6 h3 ~$ x4 n- b
    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-8 10:39 , Processed in 0.442453 second(s), 51 queries .

    回顶部