QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 2918|回复: 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
    一:背景
    2 ?7 I7 k+ ~  K# X1. 讲故事0 ]- Q) t6 I# U4 T; m/ R- @% S: R8 p
    最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
      i' h2 Q+ O% r' F8 A0 H9 C
    ! z; D" [" ]# Z: i0 X/ k' h二:了解架构图
    ! \" n% D  ?, C. Z& k& F! g. Rmysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。# n  k0 R) a0 X& @# V2 H8 w- G# j5 s
    # H" w) `6 @) t0 b. S5 Z
    1. 从架构图入手$ A0 w6 U& b' n, n
    大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
    4 Y' D; A! p+ I7 f+ v$ O
    8 ]3 C  T5 d5 n- y, F& d2 z8 H9 e+ s: R9 L9 d% O0 H. c

    7 [7 |( a# B; G: r* I其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
    4 o# k7 V8 J! p( O0 E
    2 ?( [; f" B0 \' ^/ l) `# P; [; w0 [$ t2. 功能点介绍* h% e0 o4 G) H5 M
    MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
    & q+ }. x# Y( }" P0 ]4 o4 e8 ~# `; r; T5 p+ ~9 Z- N3 r2 ^
    <1> Client- J) N! U4 Z) J4 H* ]
    不同语言的sdk遵守mysql协议就可以与mysqld进行互通。2 J! E( E( |; d; s/ S

    3 \: W. x, V9 a. L9 S<2> Connection/Thread Pool
    9 N7 u9 n2 ?# h6 dMySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。1 x2 F) P( _3 ?- A% N* ^9 Z& Y
    - `( p/ R% T# y: v& }0 F+ ]
    <3> SqlInterface,Parse,Optimizer,Cache/ }% `1 k) K9 r& r
    对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
    9 `/ A5 G, ?0 |# B1 v5 l- ~% v; w( C7 b$ r2 X0 y( H, k
    <4> Storage Engines+ K, M" v& D2 j" \0 g# }# |
    负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。
    ) j5 q( R! }# |1 R! l: q& `
    ( M& }9 y7 U! r( ~" ^三: 源码分析
    ' _$ Z; Z7 f! J# E关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
    3 i" |" M" [3 q3 {8 l- G% v6 Q) I9 X  t3 m' z
    1. 了解mysql是如何启动监听的& ?7 r; L. Y; v% x" _
    手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。0 t8 o6 o$ C1 t( `# T% X3 ?

    2 v, N4 e0 b: Q+ o$ m7 E6 ^. p" j

    3 B: j7 g. _. U7 B; g从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
    6 s) N/ [) H/ ]  d# o* t
    2 x! F: X* f* a2 z8 y  t<1> mysqld_main 入口函数 => sql/main.cc
    . D* x: U+ r3 H+ n+ h) }- t1 W: J& |; e. F( r- Q

    + |' o- s( n7 V- fextern int mysqld_main(int argc, char **argv);$ _. D) q1 S5 G* X6 ~0 i, M+ y% k! j

    ( A: ^* w9 S5 g0 ?! _+ T% b0 Cint main(int argc, char **argv)
    - m  {3 g! T$ s4 H" E; f: C- q( E{
    ! G  X8 w+ S3 r  _* B  return mysqld_main(argc, argv);
    ! _+ G0 T: ?8 B- x  x  i}# s. k( C) k. k/ ], u& l& N# J
    * }6 e+ ?8 j) c& y1 ~) L. }

    $ J* s5 m9 _& Y* S( M这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
    . t+ y6 Y/ v& z( K7 I% {) u# V! Q9 U  N+ ]3 F# A
    <2> 创建监听& q) a4 k: Q5 E

    5 Q! A7 W! r+ o  y8 I
    8 c! x; G8 t# U/ ~, ]int mysqld_main(int argc, char **argv)
    . m# f: ^- ^/ T{8 A& f  C- k; U* b1 T
        //创建服务监听线程
    8 E  A$ A* a5 v    handle_connections_sockets();7 \) u8 x. {4 \
    }4 w2 @8 n$ p% n# o: }7 `
    # w! G0 X2 x" Y# f" x0 L; T4 _2 r$ R
    void handle_connections_sockets()
    2 ]9 Y1 A4 P/ J& N7 N{  R- X. P5 {5 r" N' v8 `$ l* w' e
         //监听连接6 r7 R5 t, M9 |7 W# t: p' {
         new_sock= mysql_socket_accept(key_socket_client_connection, sock,
    + v# {: x  Q/ Z2 x+ S6 c! B$ u# Z                                    (struct sockaddr *)(&cAddr), &length);- n: D: j! X% e5 m

    9 z; F/ S% R8 h    if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
    : x/ E) a! ~+ F8 Y2 H: O% \* \, A      thd->security_ctx->set_host((char*) my_localhost);/ u! i# c- O; w! I) \
    - ?% ]7 ?/ j+ L2 z/ }" \0 u
        //创建连接$ h4 d5 X2 w  f" z. S, W9 o
        create_new_thread(thd);/ L2 J' D0 M7 x3 [. y
    }: ]; }- }  b9 D3 m5 Z
    1 |& j5 {3 l# u' b0 ?' w( Y, R$ L
    //创建新线程处理处理用户连接6 N* M2 A, e& u5 K# d  Z8 Z4 R' c- H: |
    static void create_new_thread(THD *thd){
    7 g! y* B' l- w) V3 f9 \4 i+ \  }" F; W/ J% o1 x; P0 r& E* L
       thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;6 V/ _% E3 W, s. y3 I" T8 n

    # ~0 _8 V! s4 t" h" O: O   //线程进了线程调度器
    1 C  B! d+ J+ y( c, o. L; E: P   MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   
    / h9 B% Z. w7 k2 T+ a( I}
    - Q# R( D8 A+ e5 x; G8 Y% o8 c$ d/ M* A( m' J$ M, P  [, U7 q
    # }. }5 \1 A5 |6 N0 G1 @
    至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。9 A3 f& Q3 R6 t( n! I
    & K6 E* q7 K5 D  C% R0 _# R
    3 h3 Z4 L/ u/ ?& O
    2. 理解mysql是如何处理sql请求
    ! w0 N4 r  i8 q+ D0 j这里我以Insert操作为例稍微解剖下处理流程:
    # q% H0 r9 y- m" u) D5 b6 @
    $ R: N: i7 ?% l& V8 ^当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
    ( ~& j5 J$ S7 g# o1 R8 V: b7 l
    % d" \4 D- f8 \
    ) x/ ?5 d( P) }  F3 r: ostatic scheduler_functions one_thread_per_connection_scheduler_functions=
    - x) F  ]0 y: K% j" s( ]% G{5 @* q4 h+ Q6 k% V' ^
      0,                                     // max_threads
    ' Z, t6 }. `. n. F3 ^# _- f  NULL,                                  // init
    5 w- }. m$ U( U  init_new_connection_handler_thread,    // init_new_connection_thread
    9 G+ |1 r/ l1 |$ z  create_thread_to_handle_connection,    // add_connection9 Y" u5 l/ X2 B9 ?( E% U
      NULL,                                  // thd_wait_begin
    ' l$ g5 J3 Z, o/ ]/ H& y  NULL,                                  // thd_wait_end: {* F4 K6 U- Q# X" A
      NULL,                                  // post_kill_notification# `% h/ I, J& a0 O3 r
      one_thread_per_connection_end,         // end_thread# ]. }/ n7 u2 e2 @2 }
      NULL,                                  // end
    7 ]2 x! f. a/ y+ A};' `% v# q# m, h

      N* z8 I! E* u6 M* ~' X3 C/ j* `5 ]- r6 L
    从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。. i* i6 }$ |+ f; Y! }

    # z4 P: a7 Y; \- ?- J<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪: S9 ^1 `  K- R7 N% P
    3 p: F8 S; O0 J& k! F
    void create_thread_to_handle_connection(THD *thd)
    3 G. A% b/ G) B{5 M1 t* Z; O' w% ?9 |
         if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
    7 z* a3 u0 k: T; _/ b                                     handle_one_connection,(void*) thd))){}
    2 n# |: b# s% a0 Z! G3 i' j}0 |3 H1 v6 Q0 a% n0 i# _' h
    //触发回调函数  handle_one_connection
    5 {( Z6 M& j$ W7 [6 rpthread_handler_t handle_one_connection(void *arg)7 ~7 \. F3 E/ ]* T# E) u
    {
    : c$ G* K8 ]8 u     do_handle_one_connection(thd);( s' P8 r! O: j! R0 H: T3 f
    }# k% |+ T8 v" j$ a7 k# K7 W0 p- F
    //继续处理# N* d$ `2 C5 ]: c
    void do_handle_one_connection(THD *thd_arg){5 _2 y( p5 c4 R4 A
        while (thd_is_connection_alive(thd))
    $ m: u  T% [: D; k, k% D6 M    {
    - t4 d) |, W* n) p1 y+ i* x9 N      mysql_audit_release(thd);
      {2 p7 t1 p7 c" h. N. i/ B      if (do_command(thd))  break;  //这里的 do_command 继续处理
    % {6 k8 D2 e) h: A2 K    }/ m; n& s' o( n8 A( E
    }
    1 C9 [+ Q) n$ a$ u, z//继续分发
    " L2 s; _* l7 Cbool do_command(THD *thd)
    + M& B! O+ R5 k  v0 I: F3 |{2 C9 v$ V1 W  C( V
        return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
    $ z- |) V1 R  h! u& O% j+ P+ O}
    $ R0 o. R0 u$ B1 F- O. s. N/ U3 h% `bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)* C( ~+ r" q1 @3 V/ d" k$ g
    {
    2 h) e; ]* Q' R6 R( y: e      switch (command) {, B1 ^6 O: O8 q$ L# e8 [
             case COM_INIT_DB: ....  break;
    - }7 x. @1 y! n4 U         ...+ s' e, ^7 c" w' `7 c! j2 c+ _
             case COM_QUERY:   //查询语句:  insert xxxx/ |& x0 b, |* I9 V" R
                 mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析
    " d: I9 ?& Y3 K% D$ G1 M# w! {           break;
    $ w, k6 n# i* ^' f      }
    " u2 {9 Q. v/ \; k% |+ I, M! n# \2 `" @}
    $ U$ H' [4 G' U2 V3 k$ m( N//sql解析模块; i7 [0 Q" A- n$ H( e
    void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
    7 O5 a4 X+ X9 |{6 Q# d2 Z4 v5 P* N0 b; z
          error= mysql_execute_command(thd);
    ( r( r% ]5 {/ |3 E8 }}' T$ g# c  X3 C& w

    & V3 k$ Y- Q% l$ t1 ^" l& x2 ~1 L& L7 G5 f3 b+ [
    <2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。+ D% ~( ?# l: S
      W- z) M) g0 o" y
    //继续执行7 H, {* l7 F! V" n- S  V
    int mysql_execute_command(THD *thd); V% U+ a2 d& }- d
    {
    ( o6 E  L( Y0 X3 s% t7 }  switch (lex->sql_command) " l/ j  j/ V/ r- B7 W2 z
      {
    " ~% X2 g" N/ {0 Q8 Y      case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;
    6 b/ f; K2 S; Q( x' l* p" s9 r* f3 q# n7 r& ?
          //这个 insert 就是我要追的
    # [8 q4 G  P) v/ n7 m. Y* [      case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
    / U. c2 q( K( r% T                                              lex->update_list, lex->value_list,
    # r  x& s% H+ k                                              lex->duplicates, lex->ignore);" ]' y) a  v) q7 T4 h9 f6 @
      }5 `4 y  R; s3 F) g
    }
    ! V% W/ e" W1 ?4 C, D7 f+ ~6 k//insert插入操作处理8 O+ T5 g/ \7 `  _
    bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
    5 L+ X% P; u! ~0 \( j) c4 ?                  List<Item> &update_fields, List<Item> &update_values, , t- F0 k! A8 A9 I' l
                      enum_duplicates duplic, bool ignore)
    / k1 E: ?6 `; f( @  D{
    2 g7 b. Z7 t/ f) c1 [      while ((values= its++))
    * E# G/ h& x3 K& o1 v9 I      {2 p) l: s5 G7 G/ L0 q; N+ t  i
               error= write_record(thd, table, &info, &update);
    4 ^5 f8 Z, m0 ~( K9 k      }* ]% z. f3 w/ ?  D7 y. q
    }
    & u( z2 w5 r6 p: z1 B//写入记录
    9 b# Q& h) F. {2 |5 }, b8 S3 }int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
    ! ~/ J. r2 V# a' }# N{; X# T) T% H2 Y3 t1 x  }1 [" I
        if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
    & W7 B9 @7 u: V  U$ t9 Q2 |$ a    {+ d( C2 p2 P1 {5 n* z) D/ |/ i  [- S! P
             // ha_write_row  重点是这个函数* ~- u% @5 |& K, H: ?7 Y1 G6 o
             while ((error=table->file->ha_write_row(table->record[0])))
      Z  b' i$ Y9 T/ |5 }5 \         {
    8 h: f) X. J  d; o             ....; ]4 N% }* j  G$ S( H5 I8 d
             }2 l; L0 ?4 w4 U+ o0 k! K* K$ W
        }
    . b" {9 o. g- a! A6 b}; T$ Q* G, _2 O( q

    & L5 v  L; b0 e3 Y1 @
    5 T; S# W+ c5 I. r4 S1 L! R7 X
    2 j7 q8 ~; s6 @% Y. p/ d8 p# L可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。6 w# d; [, ]/ \" r; E! \6 j

    ' E  r% O  J3 n3 I* ?! W9 r<3> 继续挖 ha_write_row" n# D2 m* g8 S$ [7 |" a
    - ^* U6 o4 a3 P  R/ ]: H/ J
    int handler::ha_write_row(uchar *buf)
    1 M$ {/ y# n& t* V{3 K8 Q6 r; D6 n# D) e
        MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })$ Z8 R  o  f2 f  S/ ]
    }4 [1 `8 ?" N- j0 P* V1 z  h( L4 b' K

    " R# J5 n. d, f0 R6 w( X/ g5 |, m//这是一个虚方法' l1 N( j( n# ?7 T7 _- h
    virtual int write_row(uchar *buf __attribute__((unused)))
    ! F1 }2 X$ G1 S7 r: ?2 L4 M{
    ! e3 e6 j- D5 p# _0 u: c    return HA_ERR_WRONG_COMMAND;
    . C  t% K2 |1 v. t. e, B}
    2 J2 m8 c2 t6 B0 k  X8 [. o
    3 J8 M! j9 h1 k3 j) `9 _9 H" @& z7 D% L: D9 d$ l
    看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;
    ( E- z0 i; D) k$ V$ ~1 h' L
    : }5 I2 Q7 F( h; o3. 调用链图
    - _. @7 Q) R9 }! W2 F5 k这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
    - b8 r3 }1 M6 D  h! b/ ?0 O6 w% t4 C" m6 v, h
    + F6 g& A5 t2 s3 T- r

    + R/ M% z7 \( b7 u7 W三:总结# Z. k' L: J8 X" }
    大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。; ?$ |$ W; b7 T( P
    ————————————————
    ) w9 S- e; C# `: y( G2 U版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。+ t  k4 w9 B0 }
    原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
    # `; z( J# H. D8 o3 T  q7 W
    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-8-1 03:40 , Processed in 0.448049 second(s), 51 queries .

    回顶部