QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3514|回复: 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
    一:背景, O: L% F4 [& [5 C: X
    1. 讲故事! W( T7 D9 b' u) q7 q6 C
    最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
    : R9 S+ ~+ O. d0 x3 t9 X, i5 M  [! i( N- s' p" c' K
    二:了解架构图* m0 M6 r4 h; W, b# }
    mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。0 y7 o: \# m9 Y- g/ e' q
    " }# u: c2 Y. f! j* j
    1. 从架构图入手
    % |3 _8 v. A: J8 r大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。0 S* }4 Y# P& y% k6 \. X9 G* A' n

    ! e+ s* J% D1 R, h& V
    ! N* D# v( v  `3 O5 k4 H, A
    ! g& M8 f* D1 s$ h' f3 w其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~5 X) l0 ]4 a: q, g3 _4 t" T

    1 W4 G3 A: I; D: Q$ v2. 功能点介绍
    1 Z/ v, B) ?4 [8 UMySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。% F1 k/ x( Z7 k. l. a" t9 b

    ; ?2 `6 s8 I/ N2 ^4 o8 F<1> Client
    % \$ Z' X" F8 l5 ^8 R4 i  ~7 X不同语言的sdk遵守mysql协议就可以与mysqld进行互通。) a) I( J, }0 ^. p/ Y
    ! C' \1 \0 {( S" I3 J) Z' [
    <2> Connection/Thread Pool1 h& w+ O0 b. Q8 M' r# Z: m7 T+ h3 n
    MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
    ' r7 ~4 P% F9 i! h5 E1 {
    4 j; A4 f/ y+ h<3> SqlInterface,Parse,Optimizer,Cache
    % o; O! S" [/ n* p! x6 T0 l7 _对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。# I4 E2 u1 m8 N
    3 K6 o8 y5 G3 |% ~
    <4> Storage Engines0 u9 ~+ B: [2 p6 Y8 a3 Z
    负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。; Q: ~+ q; P$ S, ^  |" p% v  T( V: D

    % R' y6 S7 C. z1 A三: 源码分析4 _/ G( t' U& P0 R
    关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
    4 M! v% p  Y$ R  L' }- p
    " q+ [7 E2 f/ I' t3 v( h1. 了解mysql是如何启动监听的9 m% D8 A1 ~2 `5 R' ^: n1 F: g
    手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
    / S. O% ~& A$ {. o1 o. z- B' N# r8 G0 G8 J8 [5 k1 g
    ) N9 }% R' O5 I$ B3 l; x3 V6 j
    2 r; C0 Y7 u8 V% l  D
    从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。" b' }3 f2 t+ K& m

    4 W0 e( c- g! K; |$ c9 x9 Q6 u<1> mysqld_main 入口函数 => sql/main.cc$ f9 o, ?5 z9 s
    6 ^8 n0 v, H; f1 Y! Q; l' _* {

    ) S$ q! D- J0 `3 Wextern int mysqld_main(int argc, char **argv);; Y3 Q+ z3 z) r% y7 H- K0 u0 C& N

    5 k0 a4 d4 o+ @. Yint main(int argc, char **argv)
    2 ~2 e3 ]  A4 a& @{& k& r* t! h1 c- i
      return mysqld_main(argc, argv);% p6 S) _5 K9 F5 {9 t
    }5 K0 S; [& c# X: d& j# m! h2 e. D+ r

    0 w3 g  [% q( ~. E7 P4 ]/ }3 r
    ' u9 ~& p  {2 a5 R9 o这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。/ i5 [4 L9 [9 _! a- J+ [7 X1 }

    ' F  a0 o, N$ O( k2 Y. t<2> 创建监听
    " J  v* p6 S) ?. {) S( s. b
    ) i3 _  U2 i9 d, J5 ?; \
    % q# f$ l8 \. \- v2 |int mysqld_main(int argc, char **argv)
    0 O4 \/ N* g1 i- M) H{
    2 H  G( q# b6 E& p  \    //创建服务监听线程2 @6 O4 z* m% k  b/ R8 @9 G! N% ~
        handle_connections_sockets();
    ' U3 T& h. u- r$ F, q1 _}- f# i3 O( Y2 ~7 Q' c
    , K; f8 W* p0 p
    void handle_connections_sockets()
    9 ]( O! M& D% a6 [: ]- p{
    # R" `0 s2 g4 i0 C, L' b" G1 x     //监听连接7 R# Y; J) S* b. d
         new_sock= mysql_socket_accept(key_socket_client_connection, sock,
    6 @% U! T% m0 ?                                    (struct sockaddr *)(&cAddr), &length);
    4 [" c: ]4 T2 C! x& V8 h, d4 e0 V3 c# o
        if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))3 j( x8 K, i. u2 c7 Y# |
          thd->security_ctx->set_host((char*) my_localhost);
    $ J+ ^$ j+ }+ }# S  p: E
    * X# P. b# t# N  G    //创建连接
    + `' u  t" T: G6 E& G# L    create_new_thread(thd);
    7 f, `7 A( N& ^# N+ e" i9 }7 B}
    5 J2 h9 y; L1 ?+ {$ {) v5 t% |- q6 v/ G4 P, i( T( }: H
    //创建新线程处理处理用户连接
    1 D9 \* [2 O* e* D0 c6 Y) ?static void create_new_thread(THD *thd){% N& _5 G9 r5 P5 B0 N
    ' ]' E5 E( r; S9 M% P- Q) n3 r) h5 H
       thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;. l, s- z! a( T! U7 C" ^
    # J! y% {& D3 W" P* N
       //线程进了线程调度器& K4 D; h! @# P+ J" C+ }! W, _8 Y
       MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   5 n/ v$ P, Q8 i
    }
    $ l* @5 l) j4 f' J0 x" v! F$ J2 {
    " q( U' v3 C& G* k! k( y2 ]& A5 e* N# O- W" \! e
    至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
    ; k3 |6 }. n9 ~& G- n
    8 E8 B; q' Q. `- G/ j
    3 L! E4 C1 t* s7 }5 L3 m! F: o, n2. 理解mysql是如何处理sql请求
    + a! Y$ t- h! F这里我以Insert操作为例稍微解剖下处理流程:
    , Z8 J' ^# y6 g. d! Z
    ; [7 \7 j% s* g当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。( l( T+ J1 q7 e! I, a6 }

    6 N  G3 D/ w% E7 B/ D3 i0 o4 M, E9 j6 X  i5 q- q1 p8 v
    static scheduler_functions one_thread_per_connection_scheduler_functions=
    7 ]/ z& N- h% D7 Q2 o$ e5 P8 P{
    & U' R/ z  K, L- z  0,                                     // max_threads
    ' y0 l2 [$ o" d8 H5 r4 W. \0 b  NULL,                                  // init
    ( J( N7 Z* u* V) ~8 p; F  init_new_connection_handler_thread,    // init_new_connection_thread7 t, @  X3 U1 h$ Z
      create_thread_to_handle_connection,    // add_connection
    ' [% M, O6 s& o' c* `; h8 S  NULL,                                  // thd_wait_begin
    : l3 ^. c. t' h  NULL,                                  // thd_wait_end
    7 T5 \  x% v  O1 z5 v/ G  NULL,                                  // post_kill_notification; @- ]6 E4 H( E
      one_thread_per_connection_end,         // end_thread) Q8 b! S& u  X% T. f" f
      NULL,                                  // end
    1 J1 M5 k. y; v};7 o/ w" H4 }7 O7 i+ I
    . S* ]2 c. {1 b) Q, c8 E

    & _1 p8 a% x# T7 @% R  i- X从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
    : b5 X6 m* ]( {( H& H$ c
    ; I0 B+ Z! P; f; f4 H& B+ ]1 `<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
    , P, g1 N% e) I& C7 V" d& M7 h
    ) p* G" a- t1 \void create_thread_to_handle_connection(THD *thd)5 d3 Q# }, d+ L% ^/ m
    {
    % m" [0 g' L/ N) v4 J     if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
    : Q' l8 v9 Q* k- P) m; m+ I                                     handle_one_connection,(void*) thd))){}$ F* i7 b) K: d3 y1 M" V, H# `
    }0 `' @) X0 L5 w
    //触发回调函数  handle_one_connection
    5 B+ ]- b. |( B9 f2 s: b# l$ W/ F5 F0 ~pthread_handler_t handle_one_connection(void *arg)5 R7 x  ]* W0 N9 I% L
    {
    - z7 ~- v7 O' P6 Q- T/ L6 o     do_handle_one_connection(thd);0 }# J6 O" `; l. G+ p- V( m
    }
    , z; Y7 G, C1 x# D3 l* D9 `& [* Z! B//继续处理# `  G. i: d* S! f/ _
    void do_handle_one_connection(THD *thd_arg){1 S' r" m% O4 |; Q" g, c% ~! P0 w% v, h
        while (thd_is_connection_alive(thd))
    1 T& ^% U: s0 ]# p    {
    " N9 v( X+ J) p. S; ]  u      mysql_audit_release(thd);7 P2 _% G+ j' w
          if (do_command(thd))  break;  //这里的 do_command 继续处理
    / r) i$ J& {* V    }$ V. V# d3 n" M7 T% L, N
    }6 [, e4 y3 F) K, r( p! X4 Q
    //继续分发! q, ]- c4 G4 v% }# y
    bool do_command(THD *thd). S$ g  X* v8 G4 A/ Q
    {
    / h% F8 C" E2 i0 Z8 n6 [# C    return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));  Q5 g, I* i. S
    }
    6 i5 k' p& O* D; `+ x9 \& Wbool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
    1 e/ q4 o1 @/ o+ ]: K: B' Y, u- O: j0 m{
    $ ?& i$ c1 ]) b5 C, @8 p2 Y! X      switch (command) {
    8 Z2 p. A* X( x/ {: v: u         case COM_INIT_DB: ....  break;2 ~/ ~. O* W$ f& c- Q0 l/ a
             ...
    4 k( N# K2 `5 I9 |# ~) K$ f         case COM_QUERY:   //查询语句:  insert xxxx
    - E1 ?  D0 M, @$ g1 a             mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析# ~* @# e9 t. ]* j
               break;# N$ B8 X3 ^/ @5 z& a2 G6 i
          }9 {6 S0 K1 F) }, P& I2 u
    }1 Q" w, d+ A! j, P; x4 ]: A
    //sql解析模块- c1 a: @$ n8 Q; X* |
    void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)5 k2 P2 F8 r. A
    {) L1 P  \* M' m1 u5 X# W
          error= mysql_execute_command(thd);2 [8 `1 N, y; e; J# ^
    }/ T$ e% r5 q- _8 b# F, n
    0 @5 M" S9 P/ O7 F2 G4 H6 d( O  j

    / z, w* `3 C0 N8 K; n! `; y<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。# H$ [8 [  P- A
    % |" P- i" o" S; F% p: ~. F* J
    //继续执行
    ' A2 w+ w2 q7 B. d9 f7 I/ ], Q2 zint mysql_execute_command(THD *thd)
    7 u5 a4 R6 [1 Y/ C- ~{
    ; {8 d, G! t5 g' ^  switch (lex->sql_command) + f: I1 |6 |* i1 Y& w7 b! u1 y
      {% v$ U0 B8 P! r( V$ w6 h
          case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;
    + M- s) k" h  w. n5 J3 i
    7 H: g' E" g  F      //这个 insert 就是我要追的1 ~; s, W5 Y$ Y
          case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
    & A% n3 F% L' R( K! A2 Q                                              lex->update_list, lex->value_list,# z& o& z) Q4 U* Z, V  _) V
                                                  lex->duplicates, lex->ignore);
      M! Q7 O  w* ?/ w3 N  }
    3 u  R9 \! ]/ ~( ]9 X: t* q}
    ; W5 J1 V/ ]3 n: |+ H4 j% }//insert插入操作处理6 t; H) U0 r4 P+ z5 @" |4 V+ s4 D
    bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,. p% w# i9 T* c9 U0 x+ w
                      List<Item> &update_fields, List<Item> &update_values,
    & y4 {4 c# Y% V" L) [8 ~3 T, \                  enum_duplicates duplic, bool ignore)/ G  b. o. X! T, q+ H
    {
    ' C) j2 H- S: w9 Z- E( G* ]      while ((values= its++))1 q5 k7 n' B* A4 b4 K
          {8 G1 [& g# L; ?% s8 d6 J! i0 m* _: j+ P
               error= write_record(thd, table, &info, &update);9 N6 ~- N9 k+ l0 b) _, k( @
          }
    & j# v0 {/ u+ f5 B1 H* X& s: K5 ~}- d7 K3 p0 y  v: H
    //写入记录
    ! @% w& M6 U2 ]% s. E4 Xint write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
    0 d$ ~2 P! a; u* u# w{
    : A6 |  P) i; o6 L/ h& Z# U    if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
    . B/ q* B1 P+ M, s% M8 C. q6 M  w    {5 I9 N% x6 y! _" U- a; D! ~
             // ha_write_row  重点是这个函数
    # B0 t5 @& a5 b5 K         while ((error=table->file->ha_write_row(table->record[0])))
    % V' E; D- x( ^         {
    4 b5 b- v+ y/ F: r1 e             ....
    + o+ ?+ ^/ h. O8 Q; J4 z         }
    ! y- j- {) d$ h; A& W) f2 |    }
    9 x. s3 ]9 C+ b5 d, E& M}; \! ?: O7 |7 E! I6 A( w4 t2 j# p

    7 ?$ N  ~0 N! W' R  Q
    0 l+ ^$ o8 x! L; I+ z  A+ f' Z4 ^- l$ W  I# d2 R
    可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
    % e+ H/ O3 x& P2 |- A( j, W6 c0 U1 t( f1 ]+ A6 ], v  S/ D3 e
    <3> 继续挖 ha_write_row0 D  h+ u0 r$ l: g& ^. [1 l: h

    # ?( G6 f( q/ wint handler::ha_write_row(uchar *buf)
    6 s$ Y, L/ j( ~+ X2 M2 y0 F{) R5 b; g& q( l2 O& \
        MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
    ) `- n' ^9 Z: U2 c- c% ]" ?) Q6 C) R}9 _! A/ c, e, [. ~+ W% m* b
    & U9 C8 {+ P) ~& a3 c- s
    //这是一个虚方法9 c1 P4 M  }  J
    virtual int write_row(uchar *buf __attribute__((unused)))
    5 S; j0 {1 b# A4 |{
    1 w* R- @( T2 N( u3 ^  X8 v    return HA_ERR_WRONG_COMMAND;8 d, D( A/ ~5 G8 S% W5 l
    }# I  d( y6 @8 u  q, _
    * `$ M1 V! B8 d$ c, V1 X
    ) `! A  K7 B8 N8 y& O  Z% i, ^
    看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;+ X3 U9 J# H  \0 f! c4 W7 R

    $ B  t( ^! X( ]- t) H3. 调用链图
    3 w. s7 d3 ]/ R) X; V. g这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。  w6 A% M% w0 z# O* |8 K
    ) b2 p2 Y( B1 J% l" d2 ~
    5 Y: T: x, c3 ~) E1 V& D) h- E+ P

    0 I8 y" O4 X; B% Z! N三:总结" h2 q/ w6 S5 Q$ @
    大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。5 A6 `' r6 _5 X6 Q+ B
    ————————————————. P  b) w8 @3 L0 f0 {' y
    版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。4 S% Q5 F( r/ k' Q
    原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
    , {) M: l* m5 J- Q+ `# ?
    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-4-25 18:11 , Processed in 1.507020 second(s), 50 queries .

    回顶部