QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 2753|回复: 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
    一:背景
    0 `7 L! I; H4 Z4 B  r# q  @$ X+ f1. 讲故事
    - |/ S: c' }* z8 d4 L3 ]: C最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
    & K# M- s& ]0 M8 C  ^1 F
    7 ~! ^: {# ?# z4 w9 Y0 c3 n6 L二:了解架构图; d, e1 d& ^, W0 V+ [7 f) p- }
    mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。" r; s( c9 X0 b9 s% T5 Z; e

    , t1 P0 _! D! k1. 从架构图入手
    ) g% _8 d) F. |: u$ m$ t* x大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。3 a2 |7 j" D! E. Y# D: i

    9 s6 \9 M3 D# W8 a& C1 N8 n/ n6 T$ G

    8 z9 E; C# H+ C9 c其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
    ; p7 Y5 M- n& R& t# f! G
    3 v- ^( p- H7 K9 w6 }: |2. 功能点介绍$ B2 |2 T9 b) o4 |7 F
    MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
    , @1 [$ L+ n3 _: E/ @7 W$ A, x1 u; y/ u. u* E2 c
    <1> Client
    " q8 ~: P' o1 h" G不同语言的sdk遵守mysql协议就可以与mysqld进行互通。5 H- e+ j; n4 K- J, D
    - e- m8 D4 x% d% o( S
    <2> Connection/Thread Pool
    ; K$ E  [# Q+ oMySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。" H7 [, b$ }+ ^4 Z/ U" F

    & ^# E4 {5 g( y<3> SqlInterface,Parse,Optimizer,Cache# r& O. @7 e& m4 k: J) m
    对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。8 C2 K; Q( M% }, l. y

    6 M) G& {5 O4 c* o0 y<4> Storage Engines, H; K( K$ g9 R6 ~( j  d9 ~: d( d
    负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。
    / ]1 x) H. J7 n$ s# u. d  ~" m0 V8 `5 Z$ X& ]4 Z& H2 t6 _5 E" z
    三: 源码分析1 m2 R) Y. y4 U6 f, c
    关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。/ {5 u4 ^0 L, I( U7 e

    , S! `3 ?5 m" T: v" U% S1. 了解mysql是如何启动监听的- P: b$ Z3 B& @5 q; _* n0 Q9 n% E  o
    手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
    . F7 h+ b* j6 \( A: _5 i, @5 ?- R- C: Z0 ?4 [3 x+ B( t

    ) y) {9 q+ k( @  i: c( j4 k( J9 v" P  M# \5 j  K8 r
    从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
    . j! ?1 J. f2 L, K# S
    * x+ V! I  \& V! S3 v3 d2 k<1> mysqld_main 入口函数 => sql/main.cc
    , B8 v& N8 z+ `( q+ W
    - T* ^; u- {" l0 h* k4 \' v% Q: h+ s% O  u& i( @2 C( I0 N$ Z/ s: P. C
    extern int mysqld_main(int argc, char **argv);) F8 N/ Z; X# J6 F. E
    6 v& i1 c. J( d0 A
    int main(int argc, char **argv)" `9 |" Z" S; b
    {( |! F" L! _- b' u0 t4 |
      return mysqld_main(argc, argv);
    7 u  ]/ Y, [) X' f9 _4 a1 n}% _0 X4 ~- {8 E' J, H, D. B( Z

    5 w( ]1 l0 _0 h  H* |% t; \% v8 |( d5 H
    这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
    % j0 a( [1 O" |9 b/ C
    1 j) I; K8 N- V3 q' q- c6 ?+ A5 B<2> 创建监听
    . _( L6 r7 `3 f  m0 n# J* z* z8 A8 C7 ?6 k" Z

    2 L+ i1 A  Y6 Q' I$ u) b8 _8 sint mysqld_main(int argc, char **argv)
    $ @. D# J8 C5 ]6 d{
    ) S5 l4 {) S6 T- h- r* k7 o    //创建服务监听线程3 u) c6 Q  C% C: P
        handle_connections_sockets();
    / q- N# c; b, E7 @}
    5 J5 u, Q- K& N, J# G7 E5 |+ V2 f/ f! F, h/ e. H
    void handle_connections_sockets()* O$ H7 f1 V8 U3 n6 c
    {$ Z" ]  [  e' O8 R: a( G) Q; r; K
         //监听连接7 P! |. l* F$ t- r
         new_sock= mysql_socket_accept(key_socket_client_connection, sock,
    ( F9 z7 `  r6 w+ i; O) o4 W                                    (struct sockaddr *)(&cAddr), &length);4 z# J* D% C$ K2 ]) m
    1 }/ y  y8 N2 t! [* F
        if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))& V4 {+ X+ Y0 i. R8 ]
          thd->security_ctx->set_host((char*) my_localhost);
    3 w  @/ D6 i9 B9 q
    & I4 S! m1 j+ G6 E/ I( ]. V. ~    //创建连接
    ' t6 E" E8 _6 v, j9 }% o8 k5 H) z    create_new_thread(thd);5 M$ a5 J% N( }: L
    }
    0 f( Z% r# o8 l$ {7 @$ [/ e4 r9 N
    & N7 p/ C3 A" \//创建新线程处理处理用户连接
    4 r* _# K. m/ Y; j9 i% T; _static void create_new_thread(THD *thd){+ o+ m' W, @3 ?  y* t
    ( V3 y( X( z/ `
       thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;4 S3 W: Q  ]+ d$ l0 F! ^' O
    " r1 j9 w9 e" @; Q6 X" f
       //线程进了线程调度器
    7 y# k; W4 i7 f   MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   $ P6 h$ ^/ S$ e
    }
    ! {- _  {" W" @* Z# _
    5 i( W6 O* P+ r7 T: h4 Z" H
    & m. `5 O" A! j. C2 k7 M) r5 `  r至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
    4 i/ G4 J8 P1 P$ K6 X5 z% A- C5 m2 \5 k) m- V* T
    2 _$ m- ^1 {2 J1 \8 w
    2. 理解mysql是如何处理sql请求
    7 t" C# {6 \  i6 t8 I$ O. q这里我以Insert操作为例稍微解剖下处理流程:
    2 @" o+ o5 H- \5 u9 H4 v/ q
    . ^/ ?1 F% }; H: R, B3 w) b当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
    # a) d+ r- T' ^8 a# W. p8 o+ A, P& \. n7 f

    - e5 V3 w& {, b/ h' N9 ?* c6 g; Y& O5 Tstatic scheduler_functions one_thread_per_connection_scheduler_functions=/ n: I/ O1 G2 v, U9 k3 G. H5 \
    {
    6 L/ v+ e! _6 P9 h5 a6 L0 e  0,                                     // max_threads
    7 m. U% G  e* L  H9 n- E  Z# i0 |- s  NULL,                                  // init8 c6 ^/ ]2 i1 Z" T$ B8 `/ w" l
      init_new_connection_handler_thread,    // init_new_connection_thread" s$ ~6 u! F/ r4 ]/ Y: G) K. X$ s
      create_thread_to_handle_connection,    // add_connection
    : m7 c0 n# \3 V  g+ Q% o% l& @  NULL,                                  // thd_wait_begin
    , b" A! Z1 f8 e8 [  NULL,                                  // thd_wait_end) r; Y7 }8 M5 L- |1 z: n
      NULL,                                  // post_kill_notification3 _* D9 x. h9 v: Y7 u8 j7 ]
      one_thread_per_connection_end,         // end_thread, k: }9 o0 Q5 I7 a+ y( G
      NULL,                                  // end
    5 S* _9 w5 R0 H0 l: r! m};: W, u* D: ?' ~& b% b9 w* g* r0 G5 [
    , b+ t9 D. \# P, f) B
      d& g5 z; U* G1 N
    从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。2 f4 ?' j. L1 [  }! s
    " F1 J% \5 d8 I4 ^3 W
    <1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
    , L( r- q1 ^, f$ j5 j
    ) j( H' |1 w" D. Jvoid create_thread_to_handle_connection(THD *thd)
    ) E$ ?5 k& v: t" i$ K4 n$ r{- t, A* \0 M- }6 `/ d
         if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,( K6 F7 L4 Y' Y6 G
                                         handle_one_connection,(void*) thd))){}/ ?$ r% S( Q5 w/ [0 w2 [* z
    }
    , `* I& A! ~- U1 h: \//触发回调函数  handle_one_connection" j, R; G3 A1 S
    pthread_handler_t handle_one_connection(void *arg)
    4 g& x7 Y5 ^. h) r) k) L- ]{
    8 Q5 E& ]0 @8 i: v, w     do_handle_one_connection(thd);
    , v8 C8 `8 E3 o, O/ N}
    & g$ U1 T, e3 e% n) A//继续处理
    9 }( m' ~3 c6 Evoid do_handle_one_connection(THD *thd_arg){
    2 {1 Z. \. K$ H, a. I    while (thd_is_connection_alive(thd))
    , w: h/ q3 C1 v; i% n  c0 Q. \, t. a5 f' w    {9 O/ y9 y+ M9 o
          mysql_audit_release(thd);
    2 l& Y2 M0 R0 q) [2 }4 \( p      if (do_command(thd))  break;  //这里的 do_command 继续处理
    1 d  z8 c; c* Q2 B2 B: F    }) f" R# Y! i4 R: @
    }
    4 Q; `' Y- f+ R+ r3 e/ @0 X//继续分发, z% Y$ ?$ D# J4 v5 w
    bool do_command(THD *thd)( ^& t& u( ]8 m. \( [- Y6 V! ^
    {
    ; \9 \  p) I& A; q, |& d/ q    return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));) y- l$ s) ?/ D% Q. d4 c
    }
    / L7 b) V# _* U4 f) Xbool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)6 [! E# \1 [# d. C) `
    {
    + }' a, g: j# ~+ Y      switch (command) {
    ( X. T+ J. E" _& e6 q& C$ e6 m         case COM_INIT_DB: ....  break;8 N* v5 N) E& ^9 m
             ...) c+ P5 v8 E9 {; y( a
             case COM_QUERY:   //查询语句:  insert xxxx3 Q6 ~/ @9 |9 E. K
                 mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析/ c/ c2 a' j& G, l
               break;7 ~: T# E: F( c. N) [* v
          }3 G1 D0 p. |) ]# w
    }
    6 l! Q! w% t0 s% I4 D+ [" T; C  {1 a+ d//sql解析模块
    ) W3 p5 B( `% k, c' \+ }# ~void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
    ( I9 |# {  ^0 j  `{
    % u4 Z$ c7 J# I, [( [9 |      error= mysql_execute_command(thd);
    - p4 {/ i, F3 [$ n3 C$ _( K* l}
    - E- S0 g1 G# o' |. \) N& K7 |; E! F8 {  S
    * ], [/ \; P3 ^
    <2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。% c: _/ ?# h1 F1 P

    * y  @0 Q& r" `+ q//继续执行0 d- Q* L. n/ D9 c4 C7 d* ?+ [
    int mysql_execute_command(THD *thd)
    9 X6 [" c8 z$ L( `{
    / g$ Z$ p- X) `2 j: i/ {  switch (lex->sql_command)
    0 B3 j& J  D' G5 W& f% s% ~3 J  {
    2 f% Z0 q6 C4 N2 d, }5 E6 `# M      case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;0 d& s8 M0 q' M& Z

    : R+ ]5 e" D- z, b& m      //这个 insert 就是我要追的
    . Q- `6 U( N* w4 I" l      case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,! [% J2 U5 b; N- m3 ]
                                                  lex->update_list, lex->value_list,
    1 A% p3 x& I; J; a$ U                                              lex->duplicates, lex->ignore);
    5 s  K3 N: `4 ^, d) J  }
    % o, U- Q" Z2 K}
    0 G/ n& R' o: z//insert插入操作处理
    1 I( }9 [* z7 N( K* Xbool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,/ x' r, j" f  k, @7 \. f0 A. f
                      List<Item> &update_fields, List<Item> &update_values,
    . ?; E9 o: k' u1 N# D% i3 B% R8 Y                  enum_duplicates duplic, bool ignore)- B! b) ~, D3 d  X2 m7 X1 H
    {- r" R1 m$ @$ d7 l' S; \
          while ((values= its++))& P4 Z# `7 [1 h
          {
    5 w. A. Z! T' e$ G4 |           error= write_record(thd, table, &info, &update);* W4 [5 M  g8 j/ o; M% R
          }
    , w3 [$ i# n0 n' X}
    2 x9 _2 f* C6 P! ?//写入记录
    - e  W, }+ q  G0 J" U  J" u; Gint write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)5 r3 m" m5 @4 X& n
    {! m- e4 O: \1 Q% S* l
        if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
    / r' t5 q& W1 _3 ~1 j: j4 \    {0 W+ S9 ]/ u; {2 M3 h. W
             // ha_write_row  重点是这个函数; P7 n: x7 e, @0 s/ S  k
             while ((error=table->file->ha_write_row(table->record[0])))
    . M6 V! e- a6 z3 `2 s  ~, s) @3 @         {% ~  P: s/ c: W0 J6 m4 m& U% M& |
                 ....
    ' a' H$ [- H0 c' |) z6 t         }
    ( G: h9 S* h8 ]; h+ S+ ^. I    }' O7 e5 i! D" |( f0 W
    }: d+ t0 m) y1 G2 x7 _

    0 H( @1 z6 N/ O6 ^, B7 ^. L. e# J" t

    # o# J" g+ t1 V- \* q+ Q可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
    2 g. y! G( C3 h
    - ^& G8 u+ j3 ^& u<3> 继续挖 ha_write_row3 P6 a6 f! ]9 x4 Y* I- R

    ! h# j& o# z# Z' |9 Mint handler::ha_write_row(uchar *buf)
    1 H/ U* [( Q  u/ B: e/ j: i{
    ; s) B9 j8 A) P: w" a    MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
    % h1 N" x2 G2 Q  O- A9 ~' h+ d& I' X}
    # u5 K, u# g8 L( ^0 ~
    5 z. }8 x$ O! j) [/ j4 x//这是一个虚方法
    4 ]* S% a! J$ Vvirtual int write_row(uchar *buf __attribute__((unused)))
    0 N! R9 M- H3 R: f1 I& y$ A; W{
    ' J7 Y4 L+ c8 b8 U    return HA_ERR_WRONG_COMMAND;
    . j+ D' b! C5 Y+ F/ H}
    - ?' x- i1 m3 J  e9 F' M, u5 K% ]8 [
    8 Z1 ?9 R( o4 X0 K) a
    : I* \/ |+ j9 P# n+ k9 c% |看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;- q8 t5 v, D4 w: D
    % ^1 T* T, [9 {/ t& V3 w
    3. 调用链图& Y5 E/ v! p4 L% h7 b
    这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
    * o) L. q; g! J0 T% A! F6 a
    : W) B9 _  s' \- M# }: y6 r
    ' j* P! O. l4 X
    ! B( O$ D+ ^4 e0 O, {三:总结5 y/ K7 S6 S* R, M8 J
    大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。
    8 F( u* R# y2 b, i, W6 h————————————————
    - u4 j3 K& V+ m  X版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    , g2 i) S# D) G- b7 @! v原文链接:https://blog.csdn.net/huangxinchen520/article/details/1064874153 z* T- S) Z7 o2 I! Q" M
    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-7 14:12 , Processed in 0.314043 second(s), 50 queries .

    回顶部