QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3406|回复: 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
    一:背景
    + i4 t# ^/ E( e1. 讲故事
    & @8 ~. I8 _, Q0 V最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
    # a$ s. x: y7 ?( W6 O" F. `8 C
    7 A3 S4 ~4 l7 ~二:了解架构图
    2 i6 l- J9 ~- }& d, Cmysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。) r7 o0 ]. ^% v. Y. A7 n4 ?% `, e

    3 `8 _( O1 L8 D4 u" o( t1. 从架构图入手7 m; `& y( B' T7 j, w& a# N6 Q- F$ I9 c
    大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。, u) i5 ^. Y6 ?' B/ |0 D
    8 C, l  d; F' [6 Z% S
    3 `' M6 t/ _( a, G* Z2 B  {8 l
    , t$ v" m* ?: J% {  n
    其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~0 ]2 w. G8 Z5 s  L! }5 ^! F

    % \' s3 t( F" b/ @# P2. 功能点介绍
    : l  G5 P$ W  F: J  ~; EMySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。% t- u/ O- m' U1 c- _) o% R) S$ g
    7 m$ t) b' K8 @( N9 q0 s9 n
    <1> Client
    6 J- @8 z2 C: G  l1 Z5 v8 i' D不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
    + O% T- Q0 [  L
    3 W0 C/ s7 Z( h5 N7 V8 t( v- \<2> Connection/Thread Pool, g- z8 J7 K& C: C+ q6 _% C
    MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。6 J. P& r# h' i, }! X" g

    ' y# M' i0 h5 u! |# d<3> SqlInterface,Parse,Optimizer,Cache9 p3 A/ S: d" d6 i/ ]2 l2 {
    对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
    8 I' A: \1 f! _2 C! A
    ; I5 w# x' V+ }: y<4> Storage Engines
    , m0 W1 w8 P* ^5 {8 t) V负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。  P6 a& {9 `; }9 h. Z( A
    / S* t- C, Y1 E( |' l
    三: 源码分析
    ) p6 Z- ]6 ^* P. W; X/ P关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
    ' E" q+ a$ A, O3 C8 K
    + t6 W& k6 I7 J+ q: V# i1. 了解mysql是如何启动监听的6 }4 X( d# g) f+ e
    手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
    : I+ X2 z4 X- N+ O! B9 \' Y* d4 `9 X/ u6 _4 S* j. _5 @- N2 h

    , c2 R3 V4 S; ~
    ; s9 ]; ?+ b9 Q& d从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
    * h9 c1 W# s3 q2 m3 g
    5 @/ b& A5 m5 |* B<1> mysqld_main 入口函数 => sql/main.cc
    : |5 l2 K  N! V4 {* b! X1 {- @- t! Q+ u
    % \- g+ D4 ~$ P4 ]! Q, z
    extern int mysqld_main(int argc, char **argv);) y1 k3 S& g& G  Q( ^

    6 G0 t# j. M' ]9 g* \8 {' Lint main(int argc, char **argv)
    ( s% Y+ X9 f3 E! _- k{
    * x" z8 f$ c/ ?4 m  return mysqld_main(argc, argv);' J! P5 W3 x, B8 F
    }
    5 _' L8 x; A7 K$ h4 M" L' ?! q3 L

    8 C* n  S3 b7 [; }/ L6 Y$ O- U3 ?1 F这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
    $ Q$ y4 V; W5 Q! N; b# Z2 @
    - S9 U( N9 c* _% E  a; G  N<2> 创建监听. h" X  E% P- Z& L: f7 N6 y4 k
    # ~: D3 g! x$ G$ q, Y; ~1 P& N
    ) w3 {8 o* l, M. Q% r" f% }
    int mysqld_main(int argc, char **argv)
    6 U, e4 c! {3 `8 s$ ^- Q& U2 E{1 m+ d8 d- r3 Q& ~# W- F. _5 z
        //创建服务监听线程
    4 U! @# ^! X7 E/ E4 A; w% y( m    handle_connections_sockets();6 ~3 u6 j; L. U
    }0 j9 v+ I, F0 {; C2 H7 @# s$ S2 i8 h7 i+ a& \

    ) a# x4 N6 M  Q1 Z3 y' |: Vvoid handle_connections_sockets()
    2 t2 y: l4 e8 w) Q- G: P# G{5 z) [, l% `4 V0 f
         //监听连接# A( E$ a- a9 a( O: G
         new_sock= mysql_socket_accept(key_socket_client_connection, sock,/ D8 K5 [( q8 z9 X8 C) C/ g# d
                                        (struct sockaddr *)(&cAddr), &length);( d0 u: g1 x1 P0 v% {$ v

    ! K2 R/ I9 w0 s1 \    if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
    ' Z! H' B/ k0 w: h      thd->security_ctx->set_host((char*) my_localhost);$ X6 d! }3 e( e' H0 K

    3 }( x: R. C. P' {    //创建连接
    $ v4 e/ s, U5 l    create_new_thread(thd);" n; J& i8 U7 t0 s" W3 E
    }) w% a: O3 q- Y) `2 g$ F1 R! m
    , M% R% T1 O/ }3 U+ C
    //创建新线程处理处理用户连接
    . U) r8 L- r/ M' Zstatic void create_new_thread(THD *thd){
    8 ~% D, ]$ U6 L+ P5 v
    ; l( ?4 o6 V+ `; K. F! u   thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
    " ~) r1 ?8 \  u% b" O0 I$ \; p0 O; r2 l8 D  @5 q- g6 {' H
       //线程进了线程调度器6 w6 k; x5 m6 Q: c( m
       MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   
    - W! W, y% }+ C$ a}' H' d- h% c; J

    " b5 I" T5 t6 _+ i. ?1 D1 X, b% f, R) r
    ! ?& X: E3 V% W) I至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
    6 e4 I, S; M5 N. G7 b
    7 o! c/ M5 X3 M4 }2 a' `* j- E0 O: l2 S% Z+ u2 y( ~" ?* u! @  w/ {
    2. 理解mysql是如何处理sql请求
    - {- m. Q. |, Z3 g  k. u9 C& X: I这里我以Insert操作为例稍微解剖下处理流程:( d7 q2 [  ~: r, u0 M. a

    7 q+ d$ x/ P. {当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。" _& b( i& Y9 c3 v; `

    , z" v2 `4 E7 ]0 B' M( i
    # I' \! D3 \8 B, U3 d% u2 T5 Hstatic scheduler_functions one_thread_per_connection_scheduler_functions=2 c* k8 [/ g/ y2 L1 W
    {
    * A8 X" ]4 \9 Q' |  X1 x  0,                                     // max_threads
    4 h5 j) p% c6 i3 F; \  NULL,                                  // init
    1 r5 k  c& G/ _. f$ ^& R; W  init_new_connection_handler_thread,    // init_new_connection_thread( h6 o3 L8 C4 |  N  q
      create_thread_to_handle_connection,    // add_connection/ _  c& Z# z! A
      NULL,                                  // thd_wait_begin
    3 B2 k. J+ z5 ]0 X  NULL,                                  // thd_wait_end' R3 v3 J5 g0 B+ l! Q! G+ y: I. X
      NULL,                                  // post_kill_notification
    - ]( c0 i5 l( S. V. W2 D9 Q  one_thread_per_connection_end,         // end_thread/ i" p' v9 \& H3 p5 Q
      NULL,                                  // end6 T8 @! X7 J1 \+ f% Z4 a1 J/ m. U
    };) b1 O0 w6 `0 E5 @

    5 e% g+ O. h4 M/ S# z* A. G4 o3 C
    & |$ Z" |; y, v) p从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。& ]6 l+ d0 ?* r1 O) C

    " g; Y* u9 l& Q& S, y<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪( x& M- s$ ~3 L2 @  p5 {; e  S7 c
    ' v! ~; H, b0 y& @0 {# s* Z
    void create_thread_to_handle_connection(THD *thd)) p1 D! f+ \5 |, z2 m" i7 U
    {0 t) l5 E' a8 w. l
         if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,$ m1 N0 L1 ^  @/ C/ T$ `$ j
                                         handle_one_connection,(void*) thd))){}2 q* ]1 V. a* |# Z
    }
    5 k8 S& d% S1 @; i5 A//触发回调函数  handle_one_connection
    2 M. f6 j# M9 a# ?* S8 C' vpthread_handler_t handle_one_connection(void *arg)7 @- W- ?% l# i, ~; ?, x7 k
    {# S9 {2 L. F. v$ J/ `' n# J$ u8 V2 v
         do_handle_one_connection(thd);$ h$ @6 P* U4 ]4 H" D4 Q( x8 o
    }
    8 L2 {, C* j! a' U4 F//继续处理& B5 O5 @7 \* F8 R, r
    void do_handle_one_connection(THD *thd_arg){
    1 [3 j( @5 d8 Q    while (thd_is_connection_alive(thd))
    ) }( Q0 |" B+ A    {
    , g! H! j( [# i: R6 b6 Q( E      mysql_audit_release(thd);
    7 M6 p' u# q) L) w      if (do_command(thd))  break;  //这里的 do_command 继续处理- a* d/ u  S8 j8 [
        }
    , A2 t' \  M% w6 N* E}0 d) D) Q9 D2 D; f2 U1 C# T
    //继续分发
    + ~4 N9 U5 A. A( M$ f' T% Q8 A3 D! dbool do_command(THD *thd)
    , p2 N& S' j% d2 F! ?6 R{8 m, @1 Z. A2 S: I5 ^$ p: z6 }/ k, \
        return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));, T/ P: C1 ]* }& @! `
    }
    / q1 [2 k! p; c  Cbool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)* i+ i) ?: d  v1 H+ \. a( A
    {7 `; M; @0 o6 E& H' p9 Y; z9 A' ~8 O
          switch (command) {
    4 |" n  z3 @! F) O6 W* S. O2 e         case COM_INIT_DB: ....  break;3 L. g( Z9 y( h& g5 ^2 ^' y
             ...
    : G5 {. T& h' J& ^' \         case COM_QUERY:   //查询语句:  insert xxxx3 ?( M* e! v& }1 ]9 n4 X2 \
                 mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析
    8 X! `3 @/ u" E- ^$ ?' x& f, G           break;
    4 X7 g$ N  z6 W$ J* b      }
    8 G' f1 K2 v) E- ]; y}
    3 g, Y" l# N2 i. K# \//sql解析模块
    6 U: Q  @: q  [9 X, P0 }; Vvoid mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
    / M5 p, A' P( O) M: U+ B{
    + P- T( b  w+ u8 f8 c      error= mysql_execute_command(thd);
    ! G9 U( R) }# N1 @" k' ~}
    5 w1 t- ^% R  C* F* R6 F- s0 f% W7 V6 s1 j
    6 ~! D& l, N2 o1 ~: ]+ K
    <2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。) b4 A: A6 a8 E0 M0 L
    / c" @9 {; G( E1 B+ Q
    //继续执行$ a% ?( v) F' h- x# w% k: ^4 h" N
    int mysql_execute_command(THD *thd)" R$ ~0 e% e" [$ X9 t' J
    {
    ! Q* a5 Z6 ?- o) U& |; E  switch (lex->sql_command)
    ' Y: I/ C" D! k9 J5 _3 I  {
    * Z% a+ _9 }, S: |! n+ R9 O      case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;  u8 Q- g; |" o, u' t7 B

    ! c, L9 p9 s7 n1 K: `* t      //这个 insert 就是我要追的
    & t7 M" {$ r1 I6 f0 ]( W1 M4 ~      case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,; s: b/ V$ r& }& {7 g& ?1 f5 Z
                                                  lex->update_list, lex->value_list,' u& B% h( y! ?+ q
                                                  lex->duplicates, lex->ignore);
    7 ~6 Q$ F' y8 ], W" [( f  }& }3 D& v" N, Q. @3 p" U0 N
    }8 f. j/ C2 Z& g9 y
    //insert插入操作处理) e  L- ~* \  V: @9 k5 n/ a; b, p
    bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,& u& _, e/ _. z
                      List<Item> &update_fields, List<Item> &update_values,
    . v$ C/ P: g' U" P# \( O! P                  enum_duplicates duplic, bool ignore)
    - l; _) e% p) B; S2 y  F{5 Q% L) T4 V7 _5 I& u2 v1 l3 S8 c
          while ((values= its++))6 _' [5 }0 P" u* K, b
          {2 O8 p" q7 N+ @5 Y+ @  ^0 b
               error= write_record(thd, table, &info, &update);5 F5 s; x* B2 k2 ^3 H4 J1 L
          }
    - W4 _  H% D- ~0 d! W5 Q}
    " \$ L6 d0 q3 \' W//写入记录/ K6 E1 W# P$ L. R
    int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
    2 e, O0 f* Z# y+ r! r% H! G{$ a3 r' R4 B3 V( k3 {) k2 F
        if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE), x4 D7 |3 I5 i) z0 `# L
        {
    2 ]6 [! _; Y# h$ ~" Q* t& J         // ha_write_row  重点是这个函数* @% t8 n& q* ]
             while ((error=table->file->ha_write_row(table->record[0])))
    ; ]  J: U5 S" T/ H         {3 V. W" T) x8 q$ \$ [, O
                 ....
    + C3 c7 K* U. g! q# `9 {: I: ]. Y         }
    , V9 f5 v  r7 ?9 Y; E    }$ \5 U. D7 L/ B6 U2 f9 B! K% ~
    }
    * }' ~' G* L& }& s- A6 h; q1 b$ ]5 a6 {; p( ~3 o9 K

    # s2 j4 |+ t9 c* h" f3 n7 o9 t; |$ _0 _, m. B. O' ]& `# b  B0 _
    可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
    . @& r5 U8 O7 O: e  ]; M; M6 H: x4 E- Q- r
    <3> 继续挖 ha_write_row
    & O9 T' k5 ]1 I8 u( z/ E4 W, L8 m0 s% `
    int handler::ha_write_row(uchar *buf)7 ?  p; ?+ ~7 J* d
    {/ A2 d+ I+ i$ v
        MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })9 _  o+ n  O) u
    }
    2 G( e+ e' s$ [0 p" z7 F
    7 n% ~! y3 j- l" P0 K. S//这是一个虚方法
    $ Y+ H7 z- A5 v# [virtual int write_row(uchar *buf __attribute__((unused)))+ o9 {, j# \6 K3 J5 x, c
    {
    ; E& i" `. e* e    return HA_ERR_WRONG_COMMAND;
    - a! K- B- |& ^4 V% c}! J  I0 L/ g- X

    % i3 ^0 {, U4 B6 J% f* F" ]9 q( o3 G; G' e! g2 S
    看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;
    , n8 j& _4 w6 }( _. B: I2 C- g6 Y. U' \! ]
    3. 调用链图# s/ ~+ I: ]* g4 z+ |5 ?7 G
    这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。) t" ]2 @# [, h0 n+ ]- P' E
    , _" b3 U$ O/ G( p) j6 ^* L
    % B. {9 w: M3 G( ]: C, a, f
    3 y, h( K4 }+ }4 u6 g! T( j
    三:总结
    6 `3 L2 g! z, W/ L4 q, n大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。. t$ g# K2 P6 k1 @5 m6 }; b: J
    ————————————————
    2 T) Y. j0 g) G( \* _+ B$ i版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。6 p  F9 ?4 \! M# i+ L
    原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
    2 E2 e% [0 P( q8 ?
    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-12-30 00:05 , Processed in 0.420948 second(s), 50 queries .

    回顶部