QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 2493|回复: 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
    一:背景4 K+ b: }. ]8 v' J
    1. 讲故事
    8 {: ?+ V- y# O( I( p0 o3 n# K最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。! W  L; w& Y  X4 F+ Q# ~  g7 a

    $ h( G) U# D' h* g二:了解架构图: K( X* |) k8 H# x2 ^: |6 E
    mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
    % k) g7 }2 }/ }" N( ]! P9 U
    ( ?9 T4 P) ?- z1. 从架构图入手
    - ]/ N9 g$ k* {7 h8 R6 B大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
    : d! g1 S/ a! o# D2 Z
    3 S! @* y% L% F  x& n& y  N# {& s$ g' s+ b# {, Q- V
    ! H1 A: ]7 ]7 Y, g( C3 [0 P
    其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~9 x! Y2 q/ q9 ]1 n1 |
    ! j1 `, G7 E# m: f0 @. ]! D
    2. 功能点介绍
    9 w( h$ C; P  e$ M% r% {: [MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
    0 V( Y3 Y  y4 d/ ]; _; E
    . U' K& ^. I5 A<1> Client
    2 ]  u: l8 F* b, X% f* G9 Y5 X不同语言的sdk遵守mysql协议就可以与mysqld进行互通。7 \, A% h& S$ s, b$ G0 d8 m
    . W% a) F' c3 G2 e
    <2> Connection/Thread Pool
    . B& l8 s9 Z+ a. [( qMySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
    ; x# R$ y0 E) H, |; r1 p6 Q) {4 q' T. |3 o2 N  @
    <3> SqlInterface,Parse,Optimizer,Cache* ?0 n9 }" t4 C  R
    对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。4 L" i; w9 r% r. I. U0 v
    3 g; a; Y; i7 J0 b- e6 q
    <4> Storage Engines* J8 ^" T8 E/ L8 R* s
    负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。4 ]5 o& S! u; m  U! M

    $ L1 b& ]- F6 t0 C- Q: }三: 源码分析
    - W3 m% t: J' B& B$ `! t关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
    . a4 [5 i  q; f
    , Q- m; Q$ ]' a& z, B' @1. 了解mysql是如何启动监听的3 H, k! ^  Y5 a3 H: T4 P( D. H
    手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。. p0 U( Q. T/ e

    " q2 {7 p) ^0 s+ H! ~2 j* C" s6 w4 k. g4 I' o& k
    * L8 \$ Y1 _& F, a) {
    从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
    + C( v7 g5 d) C/ @* Z6 I
    4 N* d/ H  p7 {& ^<1> mysqld_main 入口函数 => sql/main.cc
    , h' a" M$ }! w% N# A2 ]# c7 O, n8 w: p+ g
    4 x& u2 o3 `/ u. q8 G/ m" M
    extern int mysqld_main(int argc, char **argv);
    2 f% S8 y7 c7 }( E; A5 c; K/ u0 }4 `" o# z: c( ^
    int main(int argc, char **argv)
    ; V" F% z6 Y* S+ o{; ?3 Y, g7 R; J0 E- U" s$ T
      return mysqld_main(argc, argv);
    4 |2 H( w( I$ L' |* O- z8 i# F}
    4 h/ s# _& \5 V) w! G: N
    2 ], U2 G9 w( s8 h- r
      E$ A6 V5 e7 D  K9 c这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
    3 S- q, @& A' z! ?1 C" G- ^" \: A
    ' B( @2 l: A$ U, g<2> 创建监听! i$ m; i" F+ ^$ L+ b

    - p" R/ O0 x3 M, F* J; L
    1 m* ?) `3 q4 }3 Qint mysqld_main(int argc, char **argv). }4 }( ?/ v# `% Y
    {
    / F  x: w& y! v$ ?. \$ h% N) l    //创建服务监听线程
    % M, f$ L/ J% m    handle_connections_sockets();
    + u( }  j* F# Y3 a% G7 Z, |}
    6 x* R9 S! y$ @5 W' E/ R; s1 G; E, F
    void handle_connections_sockets(); l9 G- U; z/ r
    {2 _- J$ J2 Y  c
         //监听连接( @5 P/ l/ R, w; U/ w6 r
         new_sock= mysql_socket_accept(key_socket_client_connection, sock,
    4 _9 b' B3 w8 b+ S4 Q' i* w4 j                                    (struct sockaddr *)(&cAddr), &length);9 C0 [! i4 f$ E
    ' u& M7 R0 b1 b
        if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))" e  X& l4 C+ z- Q
          thd->security_ctx->set_host((char*) my_localhost);; \, K- T$ U: c) o, N3 g' e

      S, C- `3 ~- s, T( q    //创建连接
    ( n/ L! ]" K4 o# s# M1 _    create_new_thread(thd);
    7 {- t" Y- C$ `}: q2 w+ t. D- X" ?. G5 A

    / k! Y- h- O% p  ]; \; ~//创建新线程处理处理用户连接
    2 f! W: f2 o% y( ustatic void create_new_thread(THD *thd){
    * p. |' I% ]  }# f1 Z9 V4 t
    6 F  `/ w5 k" B& }   thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
    : y' ]9 S# n$ ?7 t# ^  }) y4 a
    $ p9 M( C' y# j' }( m  Q: Y   //线程进了线程调度器
    2 n. S' ]  a3 d# `   MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   ) C6 `' ?) A. Z) I$ r
    }( P- C5 b; W( d, A" U8 H7 _
      W7 C/ X& k% U% R6 r; a+ T+ j
    - D5 t& E3 c& B# Y
    至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。2 |- V: N! e# r9 ^4 V3 W
    & a/ K2 Y+ ]: Q3 O% h1 M' s8 X; j

    2 w% d+ D$ f8 q! y8 M% J2. 理解mysql是如何处理sql请求$ a: z4 `  v3 c0 X7 n
    这里我以Insert操作为例稍微解剖下处理流程:4 r0 w9 b. [4 p( ^/ n

    7 Q9 t5 V: o0 ]6 i当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
    " o. K& C% n- j$ n8 {- s# r
    # G: n  A. n' k& N. h3 s) q' }7 c9 J0 ~* z" K2 O
    static scheduler_functions one_thread_per_connection_scheduler_functions=$ ^, e- }- n  y& G0 q( y
    {& e9 c# H; P* i) o7 d
      0,                                     // max_threads
    , ?: v: q, I- D1 x5 G9 I  NULL,                                  // init
    * b3 A4 t- m7 s' a& t1 {  init_new_connection_handler_thread,    // init_new_connection_thread' ~4 ?% y4 @/ m9 |7 u
      create_thread_to_handle_connection,    // add_connection
    6 z* @2 x' J3 y6 F$ `  E  NULL,                                  // thd_wait_begin( l# K% T0 w4 c. ]
      NULL,                                  // thd_wait_end
    ; D, M' x* c5 V+ @" E  NULL,                                  // post_kill_notification% J9 I" J7 j8 J8 v- t
      one_thread_per_connection_end,         // end_thread
    3 L+ u" G' N$ f) o, l) |4 Q  NULL,                                  // end
    2 _" i2 M4 g* Q, A# l};- ?( W, C; ~7 g6 F
    6 U$ m1 j. e5 F' u3 I; k1 d
    / r$ v! ^/ v0 E, {! V# @
    从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。& I. Z0 t3 W. [. m* _/ _7 j

    8 O: O  g* C+ h  z$ D<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪( W* J) {6 @( V1 k

    ! z( D4 f9 P$ E( o6 f. fvoid create_thread_to_handle_connection(THD *thd)
    3 z6 z- x7 Y; d- ^3 J6 [{
    & b7 k  Z$ y0 q, w/ O     if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,2 F: p; q3 a( k: y  c
                                         handle_one_connection,(void*) thd))){}* r* l5 P$ f# S, T* ~) }
    }
    4 ^, Y+ z, N* @' z//触发回调函数  handle_one_connection
    ( {8 `9 L6 y9 }& p5 q+ x1 i) kpthread_handler_t handle_one_connection(void *arg)
    2 W- T$ r/ E/ L0 {8 A5 L{
    4 n. G& f' ]' n0 I     do_handle_one_connection(thd);: F4 g( t' h6 T2 t% O
    }
      c' F. f. S7 n& M4 j2 V2 r- s& _( l//继续处理" l& `( J6 f/ P7 K2 N
    void do_handle_one_connection(THD *thd_arg){# S& E. S4 m9 N) S5 A' i
        while (thd_is_connection_alive(thd))# Y. \* q7 Q" ~( ?- C
        {# |5 L- D5 z: b3 y3 F
          mysql_audit_release(thd);; O8 E6 g4 [- \7 P4 p/ y
          if (do_command(thd))  break;  //这里的 do_command 继续处理0 s8 m. a. s3 B6 [2 {9 \# q
        }
    2 g6 {+ G7 J( f7 H}( u: G% L' n2 p0 A% r9 Q2 j5 B* _
    //继续分发2 k% K. j# i, }2 i
    bool do_command(THD *thd)
    + M( q& z& z1 Y# x{( e0 k& ~! N( F7 P
        return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
    9 I& T* Z& k, L+ |}
    + w; _; l  u5 j! Zbool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length). [5 P+ t, i+ N3 ?2 L5 S9 C
    {
    " W) ~; k4 [8 W% _: y      switch (command) {
    - N9 e. o! s. b7 u         case COM_INIT_DB: ....  break;1 [$ k" a+ `/ \) M+ k7 m9 D
             ...5 x- _6 `; o) N  U+ P
             case COM_QUERY:   //查询语句:  insert xxxx( \* E) W6 Q# Q) A& `) ^) k! R1 K% ?
                 mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析0 E" f2 J, U; K: n$ a
               break;
    % b; P4 f+ F  @7 G" M" K/ w      }: n4 ]8 \7 p% ~
    }
    & }  ~3 v- N0 Z$ v* U5 l' A  a//sql解析模块' B4 S# c! ]4 ~' [
    void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)9 e: X- ~; U+ \4 t7 \/ _
    {
      V- V6 u. ]* ~. C# z8 R; f      error= mysql_execute_command(thd);, q& j: H& W$ {. S( P" W
    }
    - x) e& K) A' \0 ?: a0 O0 Y+ ?8 {0 n! n/ }
    ! b/ y3 u, N8 W0 M# ?
    <2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
    5 P9 O. |( D% G# |8 Q2 e0 M+ \% o, R/ h  t7 L" O& n
    //继续执行! ~$ C0 V" ?5 d: X& A! e. \# s. d1 p# x
    int mysql_execute_command(THD *thd): ?! r! l* Q5 |) T, f: ^
    {
    7 `, J7 x7 ~( _" M9 O5 }# Y+ n  switch (lex->sql_command) ; m. k6 d8 D1 i4 S! s
      {
    6 G" h8 _- W7 n6 i      case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;0 n4 @% k9 C* a$ f2 P" e/ D* u0 ^

    3 w" R, D0 S+ a# }2 C7 a  ?* E      //这个 insert 就是我要追的" c  U9 s! E1 C2 q1 y" B+ J9 L
          case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,! s5 }4 q' e, p; l- O. b
                                                  lex->update_list, lex->value_list,7 I) U. n1 c) |
                                                  lex->duplicates, lex->ignore);
    ' d9 `% C3 i* _  }
    % b" F# l; M3 x/ Y}& E6 x' s8 K/ V- I) `7 ?& T6 o- A
    //insert插入操作处理
    ( ?4 B) \6 \% q% o& |& dbool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
    6 O+ V8 ?4 t7 o- t3 h4 s                  List<Item> &update_fields, List<Item> &update_values, ' F: K% o! U- Y7 I$ ~' j  K
                      enum_duplicates duplic, bool ignore): I% W5 h  D( l4 s: |, R
    {5 ?5 s, r  B+ I* Z% N* ~
          while ((values= its++))% m0 k3 A0 n' v. s& u
          {, L, I( x$ Q$ b" j9 j0 @. c
               error= write_record(thd, table, &info, &update);8 ~$ J/ N- e0 b% ~/ t; u
          }
    ) g+ S: c/ L" B}
    1 P! O1 ^: Q: d6 l% N//写入记录
    ; S% M$ Q8 j$ V6 P/ }0 aint write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
      M! a! i7 ]0 \+ }0 t& [3 [{. u6 {" m: i/ H, I9 }3 r' G' L
        if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE); z4 u/ k; ^, D" Y* ~
        {
    1 _) U/ U, Q! D4 c9 f  B. W         // ha_write_row  重点是这个函数
    / w) q7 z1 q% R6 n  a. I+ U. Q         while ((error=table->file->ha_write_row(table->record[0])))
    # f$ c7 \; W6 Q* |9 W         {
    . H* y$ R2 d$ ]  ]9 G0 x* q             ....) U, z3 S% Q* ^* a' `! z
             }) [- j  o: t5 R- B
        }
    1 ^9 q0 t; R5 ?; c1 ?}9 v+ X4 E* D4 j7 B

    . x7 A. l8 B. r$ J. B" l, @. w7 _& o) I/ Z

    / h* o6 b. Z2 c* H" C: K! W可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。. F* Q+ Z* i; J2 L: {. Y
    ) f) A# Q# v7 H- m# [* ]
    <3> 继续挖 ha_write_row8 M+ B, t4 z# y
    2 d2 ^* Y0 ~2 s& g' `
    int handler::ha_write_row(uchar *buf)
    / [7 r5 Y& f  v. y" h1 w9 J{
    0 _/ y6 A/ }: z* h    MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
    3 D6 @* \( _/ z) w}5 I( [6 s2 A! i
    3 Q2 G* Y! k% m. K
    //这是一个虚方法3 S8 G2 Z: T! ?8 ?9 J  F9 s, m5 W5 v% g
    virtual int write_row(uchar *buf __attribute__((unused)))/ @! v7 `6 O" Y* `! N" _. q
    {; i4 X: P% X2 `* Y; [
        return HA_ERR_WRONG_COMMAND;
    # A* Z3 {3 ^: N, G3 e}7 z  O( u  |: A9 W& |  a) S
    - f! ?. Q  H& q- m

    8 A8 D; c3 p( G% a& }看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;
    ) |) H6 o' S/ N" M' z4 [9 ~6 r0 [$ i& `8 u- }, j6 @  |
    3. 调用链图( ]" r4 }: }  K* y* v
    这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。% J: ^* M. U! j" x: m
    " o" H# S- R2 ~3 k( M

    0 [; J! d& A1 R2 p/ j, {8 E/ A
    & h+ k6 S8 a+ u; o, W三:总结
    # q( n' o  i- s& d3 L大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。) k- K$ |5 h- T9 B
    ————————————————$ G4 d" b+ |) ~8 K' W
    版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。$ O& t9 f6 ~) G6 t6 e5 n  i
    原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
      x! Y% d+ O7 f9 s* ^+ _: G
    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-5-15 14:19 , Processed in 0.642026 second(s), 50 queries .

    回顶部