QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3556|回复: 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
    一:背景+ b, M% V0 z6 P
    1. 讲故事
    & g$ A- H+ I& n' {- S8 k- ?( D最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。  ~) l8 d) B  o
    / {6 Q  g7 v* _$ ~6 J- i
    二:了解架构图
    . W- e4 z, l% T0 v' o" zmysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。# B9 R' `: r: w7 a

    ; \+ L8 e: s, _( h$ P. J1. 从架构图入手* G0 L. q7 k3 z# L
    大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
    8 j6 {2 c% L$ {) p" D/ a
    4 S" C! \% R+ ?7 f1 f. f2 R) ?' r. G. I0 \# g+ L! Z! M# ?3 F4 S

    " h" ?4 g9 R! o$ h. d( r" a  l* E其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
    ; \9 E* B9 L+ B2 {4 W$ v" u0 I2 r
    4 v" O7 n) F: B" n3 P2. 功能点介绍
    7 F; J* L  N& B. }MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
    ' A) B8 \3 m/ |: o" k' M  F
    8 T0 e% E' C8 v1 }$ p" v+ y<1> Client# l" b* a  ^/ y! Z; Q3 L, Z7 Q
    不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
    7 F: T9 Z2 t8 c# d4 y' X/ g- D. A( ~# O9 p) P
    <2> Connection/Thread Pool
    & V* r  Z. F/ V$ G5 K, iMySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。; j  T+ ]: s! a( F: x8 l+ M
    " e8 W. D2 }$ @2 b/ p: W& u; \" o
    <3> SqlInterface,Parse,Optimizer,Cache! @$ {* r% X/ i  @4 m/ N- y, E
    对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
    ' r4 \5 J) }, ]" l5 F) H8 p" K0 Z$ s( S; x% u/ Q3 k: [$ @
    <4> Storage Engines5 M, r3 I  ?* ~' ~
    负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。
    * h# e9 N( o) I. E* \
    % R" M* V: `( V三: 源码分析: x2 C2 I$ \( H  a: z! X
    关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
    ! `' K7 F$ \9 f" t; {; w6 {& t  h% |
    1. 了解mysql是如何启动监听的
    / R5 \0 K8 _( f2 L# {0 J% c手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。5 C- Z- F4 |5 d5 G" O; W( }5 w% Q

    $ k! g! P- T& B0 X
    : w; t$ Y" B6 H+ g1 H# {, }' e! c( u
    从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。/ e; ]6 l) r' t0 [7 q( x# T7 U8 {

    : `" R' y: K* ^0 Y; m# g3 `/ {<1> mysqld_main 入口函数 => sql/main.cc) B- R# g" S! h5 {6 U6 ]! S/ A$ W
    - x9 J1 A% s# `/ I) }
    6 Y, h4 ^) W+ T0 X
    extern int mysqld_main(int argc, char **argv);$ }/ U/ g1 l0 _; l8 K) g( w. @7 H
    & O0 |1 C' b5 w$ D) C
    int main(int argc, char **argv)' I3 G6 u' P" T& j$ h
    {- U, }4 l& _# a: T; V& L0 U
      return mysqld_main(argc, argv);% D: I7 w/ L* L% ^: B
    }
    8 c* V9 S: N5 @1 [" e* w, _* k( B8 x4 Y
      T0 b$ ~7 M1 w6 D; v
    这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。% t) c, k5 s" C

    / C& x- Y9 ]2 g! Q<2> 创建监听
    & |$ J) g- m4 H/ @  y- l# [. M
      j: z/ u2 j0 B% F- X1 o1 o
    , {1 Q' k( r* i. m  Cint mysqld_main(int argc, char **argv)  f" [6 ?3 Y; c3 u
    {
    $ P' {. B- i+ B, \6 g- j6 C    //创建服务监听线程, M* C4 y' q! h, u
        handle_connections_sockets();) o+ [( q/ d, d% F( H  }, U
    }% R3 H9 C4 m  R! W  Z
    1 I3 s2 g7 z$ |  b2 e
    void handle_connections_sockets()
    # G! M; v2 }8 y) a; T/ [6 R{
    & o, o7 ]& b: w- Y9 `" A- `     //监听连接3 |. c4 c8 R( s$ Q5 b" _+ ~1 v) j
         new_sock= mysql_socket_accept(key_socket_client_connection, sock,
    % |3 Y$ h/ N6 |+ p% a" j                                    (struct sockaddr *)(&cAddr), &length);. |* k" h( ^  L, R& O
    , _$ X  A- Q9 S7 c- n+ ~
        if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
    ' s/ i+ E8 A# v8 P  Q: ]      thd->security_ctx->set_host((char*) my_localhost);* ]- A0 J# V) m

    . q5 A5 k' k9 y& ^* e    //创建连接: _. G6 u: `2 I
        create_new_thread(thd);$ F! w6 B8 Y7 |! d- S
    }2 S* F- ]' T9 s
    9 n' m" }7 n% p5 O' b
    //创建新线程处理处理用户连接3 w. p2 e' f0 E1 B/ s' D* h! f
    static void create_new_thread(THD *thd){- U7 U7 e$ e$ j2 C* z
      i7 [) z# x4 a
       thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
    8 [1 D# o$ S9 h* @
    $ g5 K5 g; T9 v   //线程进了线程调度器
    / ?! m' O0 Z/ b8 M  S/ W   MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   
      {+ t' _- N$ Z, W}9 N! L% {6 r) `, Q

    % P# [- `& i4 n$ E# w2 N1 S/ P
    ; I0 Z. W( T, U% \8 n3 @至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。9 b+ m. i3 B1 z  {
    ; |( Y; _, _. k% V- Q
    6 s! b, O2 k" k7 Q6 o8 R7 R( ^
    2. 理解mysql是如何处理sql请求
    ) j( ^7 _  \/ M2 D$ y% {2 m这里我以Insert操作为例稍微解剖下处理流程:  z9 C% Y" e* B/ k; V9 b5 Z

    " q- Q. s0 i+ R! J+ @0 o- a当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。; H% r4 w9 L( C3 v' S2 M1 i/ N

    6 |5 `  ~! d! u1 [1 H5 s
    7 B& `6 w0 c2 ~" f6 g1 H2 I! l' Zstatic scheduler_functions one_thread_per_connection_scheduler_functions=
    7 p9 q% y$ v; o0 i: h{
    1 Z# V1 v# c2 P5 A1 a; e" o  0,                                     // max_threads) Z8 j6 q9 B. \) {4 _
      NULL,                                  // init% C. r' u' o- n3 c/ s; W
      init_new_connection_handler_thread,    // init_new_connection_thread* r0 _. ~2 e1 i$ y
      create_thread_to_handle_connection,    // add_connection
    - C& d4 L; {9 W9 w" X5 k! E% d  NULL,                                  // thd_wait_begin
      z1 M" l( h) ?) h" W; s9 R  NULL,                                  // thd_wait_end
    9 h/ E' q. ?* ^; q5 {) |  NULL,                                  // post_kill_notification
    0 z/ [1 a4 v" R  one_thread_per_connection_end,         // end_thread
    % v+ m0 \1 h, q3 j/ }( j  NULL,                                  // end1 Y" A; V  [$ u$ u9 r
    };
    ( s8 a6 [* y  t1 q! t7 P% U) J' b. @

    3 p  d5 @  W8 P从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
    0 E$ K% V+ p5 q0 a6 `: j. g6 p2 o! C5 I
    <1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
    ' N" n- p" j1 j: r* q- j5 s* a0 f/ c/ X( p; C
    void create_thread_to_handle_connection(THD *thd)
    2 G2 r3 C/ i/ g  o7 Z2 d( c1 b$ f* n{% t3 U. K* T1 ?; R$ r8 m) F0 O
         if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib," d- p5 a+ A+ P
                                         handle_one_connection,(void*) thd))){}
    " _* _5 t9 G& `" c0 S7 {1 T}
    8 R+ G2 X: S% n) [+ i# ~//触发回调函数  handle_one_connection
      }+ b- I; q/ p4 J& Kpthread_handler_t handle_one_connection(void *arg)9 }. K4 C# t0 H1 t8 D
    {9 e6 `. O% M0 ]7 y, P1 X: M- E7 V
         do_handle_one_connection(thd);
    9 o  J: s  @9 x; V) G" C}0 z" y! Y7 Z4 s, e
    //继续处理$ X  H& A; [+ l3 M/ W& u& m
    void do_handle_one_connection(THD *thd_arg){
    ' v4 o; {" g/ V& h* |    while (thd_is_connection_alive(thd))8 L  t5 d. `8 d2 q6 C5 T
        {
    . K! x) P" \! h2 W      mysql_audit_release(thd);  m$ z8 C3 G6 C% w6 k. z+ |1 v
          if (do_command(thd))  break;  //这里的 do_command 继续处理* I# D; [' K3 s7 k8 _
        }
    ( d3 }% L  ^8 o1 _}
      l2 N; F/ X: G  w! d$ A//继续分发
    " b' z/ d* y) C3 V1 d4 `* z" ]bool do_command(THD *thd)
    - ^( S8 f6 y  C7 C{
    ( b# @0 z; }6 V& j! x    return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));4 f5 s" r' [6 A/ P9 u- i
    }
    * _/ M; Y" U9 Z& F* jbool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)( J1 Y8 W& ~  j/ `2 T. ?
    {0 o' Q  h4 s9 m/ @' L' {. k
          switch (command) {
    0 `7 S8 u8 h: G" B5 r6 u' w2 h         case COM_INIT_DB: ....  break;
    3 e: O' N: a" Q9 _6 p2 f0 v         ...
    # y0 c) [. ?9 G         case COM_QUERY:   //查询语句:  insert xxxx
    " e3 C$ Q3 m5 E- q2 F6 n             mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析
    ; o* d- h9 J# d% @+ T% P2 V' m           break;
    " c+ `" w- R8 A1 b. M9 D& P, i      }: v* z- ?' W# J# d: x- q* p$ q
    }0 V- x+ x- @2 L
    //sql解析模块1 r4 ^; P. s7 M5 V$ @1 t. l! s# l
    void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)5 a4 D% T' S2 M  E5 y
    {
    * U- r4 r0 r3 q/ X9 T      error= mysql_execute_command(thd);1 Z% _  i4 s' L; r8 ?
    }  b( v$ ^5 o$ B0 r( e
    2 u- p5 w6 H3 z. n
    7 ?# q% U/ _5 I8 i: f
    <2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。* O+ Z7 O7 {- a9 C

      Q0 ?& _: f# a0 F//继续执行- a3 \/ c* I/ f- {8 |" [8 v' `
    int mysql_execute_command(THD *thd)
    ! L2 `/ X5 W/ d5 J: R{) E/ V4 S( r- ^5 _9 }
      switch (lex->sql_command)
    . G" j# K3 e$ P  {# A, y# T# b8 T8 E3 N9 J- ]
          case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;
    ; k$ E# g$ r6 D* O& }% p% B0 w: r4 a2 `6 X5 J
          //这个 insert 就是我要追的# }/ j2 D# }9 W. X
          case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,$ Q$ k3 g$ K0 t
                                                  lex->update_list, lex->value_list,
    $ f% W; H7 U3 t# B, f, k6 c                                              lex->duplicates, lex->ignore);
    8 W/ B' @& |( R- c$ E. c  }
    ) F9 a$ C% [$ Q5 X$ |}
    7 T; {6 f( i9 `" E//insert插入操作处理) c  X# V: D0 F9 i, \  r0 I
    bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,1 R+ f& q! N0 A3 v- O
                      List<Item> &update_fields, List<Item> &update_values,
    8 c" c. A7 a0 e& P/ T                  enum_duplicates duplic, bool ignore)
    2 I$ u* a$ U: x% r# A; e+ j8 p3 M: i{
    8 q. G6 e0 }0 E      while ((values= its++))
    4 j2 V$ K3 q5 S" X      {
    7 d' H# P! P7 t- A6 `           error= write_record(thd, table, &info, &update);
    ) u% t/ F% g1 a# O. @" p' H      }
    . o  B! w4 }# W5 I+ L- z}
    % |9 j1 ^6 }- o9 [3 K//写入记录& R% q9 t; T. _
    int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)) ^" O9 B: M6 R0 E
    {6 S" `* ^' [  B7 ^
        if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
    ; t/ J' D7 I6 d2 r: _( ]7 N    {
    0 E* x8 \1 F% |         // ha_write_row  重点是这个函数; X' \+ O4 R% ~+ `+ s8 Y% g/ J
             while ((error=table->file->ha_write_row(table->record[0])))
    2 P. t8 d; [8 T; J1 k         {
    " N2 C- {3 k3 G' ~             ....
    # K) `& p6 V9 ]         }
    4 P0 m# b: G  S' ^0 ?- q    }4 B+ Z% N7 M! ?
    }. B' ?* I( r$ Z# `4 J" O
    # m- B  \4 }5 _: _

    ) t; r4 u& L. l4 c3 i9 a& M% J# X, X- Z$ c% `, z# Y5 R
    可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
    9 f3 u. ~, @1 v$ ]9 u) T8 n4 z4 t/ \
    / G& z2 V7 U' Y/ P4 D<3> 继续挖 ha_write_row" @: S, N& w1 M+ @; m1 m( Z) {0 R) f
    $ L: Q/ m  }! K  c
    int handler::ha_write_row(uchar *buf); ^6 H0 \( Z) r, z# O
    {  g! I/ d* o/ V2 N6 s+ c/ r
        MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
    ; ]1 e" ?, O# w0 \! Z. x}& `8 Z- N- Q; ~2 T7 |

    1 C1 F% e/ ?) }5 S# i" }1 k' q//这是一个虚方法
    6 G5 T) c0 j+ c7 L+ N. K! g& ~' gvirtual int write_row(uchar *buf __attribute__((unused)))0 f9 D0 p5 o9 x. o: Z# w: k; V
    {
    % j5 g8 J0 H2 f& V6 O. x    return HA_ERR_WRONG_COMMAND;- O! q) y* J) ]4 r3 U: f
    }! @* o. n$ i. E1 }- ^- l
    * r5 Q* X  X8 M# _' V0 E# A* ^
    2 V6 O, j2 M% u. B* t0 y
    看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;
    / X+ t* l$ j1 K9 |+ H/ Z7 t- ]1 a  W$ N3 @
    3. 调用链图
    6 E! o3 e* ?' I- k' E9 W2 \$ b0 k这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。( c! g$ X2 g2 ~& i8 b5 P
    3 }. a6 b& ]0 y

    5 e* Z  t8 z& J3 R5 _1 [0 B4 {9 i, t& V
    三:总结
    ) |2 T8 r, O% j* h/ C大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。" w+ q2 E7 L( E' S# I! H
    ————————————————
    . D8 T, N7 B3 F" B# p7 G9 _版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。' C# V) y1 X# x& S
    原文链接:https://blog.csdn.net/huangxinchen520/article/details/1064874156 ?; h4 ^; i/ P! \
    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, 2026-7-30 21:37 , Processed in 0.354262 second(s), 51 queries .

    回顶部