QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 1473|回复: 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
    一:背景
    9 R7 j% I7 A+ n% P9 ?" y1. 讲故事
      W2 p: o) K) D  s' i% C最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。& ]4 x- U1 G, f( {3 L
    7 [8 e. G3 E$ X
    二:了解架构图
    + e$ P" y; V% X/ P5 U8 Q- Fmysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。, U4 S. b/ I2 E$ T5 x/ d( |: v
    9 g6 x4 R' `' @, ^3 U/ |
    1. 从架构图入手
    1 j3 h7 u, l- y2 k2 N1 r& a大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
    & z* W. }% r& @" s
      i. D1 k9 R" T9 m% l  w8 j
    . b* N8 T; w3 v2 L
    1 A  v2 I) o1 }5 F7 `, J3 U" Z+ m  G& }4 V其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~2 D1 A# h" m: N3 Q. G; [
    # D4 m4 R1 P$ ^
    2. 功能点介绍
    / Z9 Q2 L3 x- h# o: RMySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
    2 Q& ^$ y( \# K7 S: S
    * {9 p! b5 d! ?5 G5 Z<1> Client
    + L) |* ~$ `6 y! f$ `( w# q% Y不同语言的sdk遵守mysql协议就可以与mysqld进行互通。) S2 T$ S$ U* c% D" F7 X6 }

    ' Z* }$ Q" z7 n: P" M9 d<2> Connection/Thread Pool  t% P# \6 g7 t" ?1 }* e
    MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。6 I# Z% s8 O+ K
    , X1 x# r" I1 n- v2 f3 t7 r" _% d
    <3> SqlInterface,Parse,Optimizer,Cache
    * W1 o9 X  Q5 P6 @对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
    % b* T) i* Y( Y/ _5 m2 o; V# z+ d0 i9 j$ ]& P
    <4> Storage Engines
    5 ]& T9 s, V) I9 ]+ p负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。
    9 x5 U8 J; M3 @5 Z+ W% j* l5 l- a3 O8 ^4 R, G
    三: 源码分析
      Z% r& ~. M7 x& e0 Y7 n( h, L关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。' S% W7 G$ T$ J% v4 ^+ E# I2 W3 z% n
    5 r. @2 B5 S. x# N
    1. 了解mysql是如何启动监听的
    , _' `& Z, d0 u. g; i& a& d手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
    ; T7 M5 {. C3 Z# |- u3 P( y& l& i( z4 b; t( R6 {

    1 v( W# m9 o8 p6 s$ d, L; {& \+ y; u0 h- Y, _+ z! Z! _$ {6 r- H
    从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
      G/ i4 C; M" j1 S/ s4 m0 e! W$ t5 v- e* Y6 D! @: C8 i
    <1> mysqld_main 入口函数 => sql/main.cc
    & [' w# m6 Q  t# `  q; C' o# ]& @. [- e! a4 m
    ; u- J2 ?2 O: s! o) m7 i$ g: s
    extern int mysqld_main(int argc, char **argv);5 I9 y. L4 {1 v0 O* n8 Q
    + e  s7 K5 j- l8 r- t6 d/ X& u1 c6 D
    int main(int argc, char **argv)
    0 y' ]0 g# k+ W4 z0 n% j{
    7 F! w" [- ?& n' @* v! H4 ~  return mysqld_main(argc, argv);
    5 K8 f% L% S/ w- b! b5 W2 z% @}' Z1 D! ~* W, O0 q. H
    1 v5 K" `/ \% R9 D) C% G1 @' K  C
    ! D) I7 c: W/ K+ x
    这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。% u1 A" I( B  |' V3 K
    5 X; G3 ^. G: X6 v& W- i9 I1 J
    <2> 创建监听" b6 N8 m: s5 I0 l! _" H; s
    + ?3 F8 C5 f/ v1 H* X7 o6 Z; s

    ; c5 ]: ?' I- a! D" a  i8 a  Mint mysqld_main(int argc, char **argv)  v' s. X3 y3 G& {) ]: `& }) {$ R
    {
    + m+ Z5 q. M  D" M& C) O    //创建服务监听线程6 O# u1 _1 j. `
        handle_connections_sockets();' ~/ I. H, `; c7 ^
    }# P, C$ K( f% x
    * |- V9 p: g6 K7 b( e3 X! q
    void handle_connections_sockets()
    0 }8 x+ r* J( p( P{
    , Z/ q, O- G" j' T9 j* `) F     //监听连接/ H1 S1 D3 z0 R4 P& W' B+ E+ ^
         new_sock= mysql_socket_accept(key_socket_client_connection, sock,8 E6 R; ~- s' C5 H4 F6 t
                                        (struct sockaddr *)(&cAddr), &length);, O# i+ _3 r$ j, M0 J
    ( _" F/ p. k1 |" G! x
        if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))4 u$ c" @  ~; S0 I; @3 T+ @; E" Z
          thd->security_ctx->set_host((char*) my_localhost);- W" O8 R- \0 q

    ( p% I: m) S( B& j- o8 o    //创建连接
      [8 ~3 T9 H# m7 G    create_new_thread(thd);& C( \2 Y% Q. p9 S/ @. J$ i8 F
    }0 s2 S; a, v5 v8 g4 E" ^- J4 I

    6 o0 {8 m" Z( S9 D& e//创建新线程处理处理用户连接' I5 Y1 o# Z& F: ]
    static void create_new_thread(THD *thd){% n: Q! r' v9 h2 n

    0 n/ C) ~+ E( ~; y, l) Z# s! c0 r+ y0 m   thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;* h4 `! }* B: l0 M7 A5 J6 h
    / u% o4 k7 ]+ t6 J$ E
       //线程进了线程调度器3 _" ~" y: r/ g2 \, F( y3 Q
       MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   " F: ]& r0 X, O6 f4 }+ C4 d
    }
    * w4 p" [6 o, b9 S" j1 o# B9 A' E) p' P! v" S7 F! G
    6 F3 {6 l8 D" R2 k1 R/ e7 F
    至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
    4 c  z( `' z" M2 X) d) \! a! X7 U) u

    6 f* N' ~/ f7 ^, i2. 理解mysql是如何处理sql请求. n$ X. A9 Y" \" ]
    这里我以Insert操作为例稍微解剖下处理流程:
    9 ?2 k8 j* N$ V$ V+ F1 \
    7 U& L) A6 Q) L: a当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
    2 {7 m# H: K. @+ e
    3 E) F) P* G! R" I. A9 R
    ( M$ i" e' a. m. N' Pstatic scheduler_functions one_thread_per_connection_scheduler_functions=8 }& [3 }" ]- e! t0 J! h& t, a: w4 }
    {
    % T# B: x, g% ?" r  C  0,                                     // max_threads1 H0 l. d$ H9 r9 H! K
      NULL,                                  // init& _8 h: J% M3 Z3 D7 P: E7 ^
      init_new_connection_handler_thread,    // init_new_connection_thread8 J" w, g# B; l( ^- J
      create_thread_to_handle_connection,    // add_connection
    , s1 t* B/ z. O) ^/ o3 _1 ?  NULL,                                  // thd_wait_begin
    / G5 m3 w4 ]& j5 A  NULL,                                  // thd_wait_end
    3 ^6 Y5 ]7 j4 ~/ w1 K  NULL,                                  // post_kill_notification
      n3 ~0 z0 f8 o- {; V  one_thread_per_connection_end,         // end_thread
    8 _2 e6 p. }' a. E* h0 @" R  NULL,                                  // end8 o9 Z- m6 ^$ A% Y
    };6 {9 g9 l7 P" f

    7 ]4 K) R/ r8 K, }9 }) q0 ?: b; G7 I. O$ Y, j
    从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
    , M% y, {8 l8 S# `6 V
    / u" ?9 B& m( h: d: K) z<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
    + e( K" a( i; O5 ^/ _- H! h% _# B3 v/ i8 q5 p. p
    void create_thread_to_handle_connection(THD *thd)
    3 o" R: U  ~# @* k9 O. E* Q$ Y$ P% l{
    1 Y5 I7 N5 c3 d) l     if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,, V3 Y1 h- X6 Z- [* N; {: h
                                         handle_one_connection,(void*) thd))){}
    7 X) N' B+ c  W8 U8 M$ x' I}
    $ M3 Z+ j- h- ?' D//触发回调函数  handle_one_connection
    & j. m% s$ w" X3 Y' w5 L- [  `pthread_handler_t handle_one_connection(void *arg)
    3 {# T! L( O+ C% x) r' D- ]$ M+ A{; d0 n( ]) @! I; [
         do_handle_one_connection(thd);* M8 D( p, I, c( c; S
    }) {; U' h( c6 m  w5 M; K8 E
    //继续处理2 s0 z# V8 E/ x
    void do_handle_one_connection(THD *thd_arg){2 m, T- v2 y  D
        while (thd_is_connection_alive(thd)): |7 G7 A" f( }1 P: W- D9 V' a8 r- \* ~
        {
    0 Q6 {9 j! e. y! K) W" B      mysql_audit_release(thd);
    6 r  [5 |6 X% s4 S  j. V/ K      if (do_command(thd))  break;  //这里的 do_command 继续处理3 c* \/ ], z' e* C7 g
        }+ ~$ n( `* r2 h2 F' Y
    }
    / H7 Q6 B! `# I/ w//继续分发4 _9 s9 E7 o1 M* D
    bool do_command(THD *thd)' m0 C* P2 y* F3 K+ b2 L
    {
    * x* O; E; e4 H$ o    return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
    ' b6 S; G$ E5 r2 G}4 N1 f& n) |' X
    bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)! R( w5 Q6 P, j
    {9 g% J7 D7 c7 o. p! ~
          switch (command) {0 K1 H! d% d5 H/ [$ V; a
             case COM_INIT_DB: ....  break;
    9 Y  A5 m9 d- T& d7 R) A         ...  `0 |0 P+ w2 F+ k7 N4 i
             case COM_QUERY:   //查询语句:  insert xxxx$ a! r' s) b8 L- c
                 mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析, s/ r9 B6 W, j  C* H
               break;& d4 P& i+ Q0 S' Z
          }
    % [; C6 w0 ~. j$ }# r( n* k: s% C}
    % ]' Z# u( G8 _//sql解析模块
    ! R' P) s" X$ x3 m( ivoid mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)6 e" u/ d: K# X  i& R' Y! _! I
    {
    ; f3 n5 o% _! P: O      error= mysql_execute_command(thd);4 N# n4 D  m5 B" Q" T
    }1 D( z( u: m' W

    - }- b+ A7 A4 y; g& {+ ~' U) U7 y8 o2 t/ ?. }
    <2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。. g. z$ O( j7 O! Y( s

    , q: K' Q% B/ l7 H8 n3 R4 v# {//继续执行
    + ]9 @' w% j8 @4 u4 R! W. Fint mysql_execute_command(THD *thd)/ [& k4 K: x1 @% d% ^9 Y/ i
    {0 T, q) Z' |6 o- k. v7 s
      switch (lex->sql_command) * q% w- n. X! C$ C# u
      {4 c- r+ D9 f& J3 ^% ~/ l: G
          case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;) N) D$ t0 q* Z3 W  |

    8 K- P8 W8 ?3 \, X( V! S      //这个 insert 就是我要追的
    & c! i' _% O* q. B# g. m! I      case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
    : k1 [4 P+ P1 {  ]! b  ]; _9 T                                              lex->update_list, lex->value_list,
    - E: k* Z3 U) Q+ ?) A2 t( S                                              lex->duplicates, lex->ignore);, a% V. |* m0 e% K2 w% o2 a
      }
    2 L5 e1 J3 `+ b) D}, Q5 n$ S' k: o: k2 ^$ V' _) R
    //insert插入操作处理
    & o5 N" v# G; Dbool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
    , Q" H/ A9 g4 q7 l# u; K                  List<Item> &update_fields, List<Item> &update_values, ) s' N1 l; Z" y, L0 G
                      enum_duplicates duplic, bool ignore)
    1 f) _6 e5 P% ~8 A+ l/ N{! _$ E/ h5 T- C" V1 M4 b; o
          while ((values= its++))
    % I( U8 Y8 ]0 ~2 V/ x1 f      {8 I; x: e, D1 A/ D
               error= write_record(thd, table, &info, &update);8 `" \" [+ W  z5 @
          }
    $ p# ]" C2 i/ i5 ]+ b( D0 X}. ^4 v0 H* I5 F1 J% h* S# Q
    //写入记录& s; ]- d9 I8 O$ q) C# t
    int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)6 N/ t( P6 O+ U4 ]
    {8 ^* S9 R; P' s2 R
        if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
    ' V; p$ O5 v; N! |! g! n" N    {7 }! E( t, x+ ~" J
             // ha_write_row  重点是这个函数3 s5 A8 b# d/ K+ {; X
             while ((error=table->file->ha_write_row(table->record[0])))1 _0 }3 c8 ?1 e+ M9 i1 I& C
             {; i  {+ r8 J6 I' W
                 ....
    2 A% t- a& }% [         }
    3 r8 P$ e9 y+ f2 l# q    }
    ) T; k( n: ~! s& q}! Z# V: H7 o  M; W2 N0 ]

    ! n: x/ k- I. M! t% ?+ o
    ! n. p' v! V9 T" W; u3 g0 d3 Q9 i& q
    可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
    ; P3 L! w8 C! k+ g; I
    $ ^1 D- ^5 [* E0 t<3> 继续挖 ha_write_row# J  D( W" \0 k, l9 d+ h

    ! E- r6 t" }, P! G* ?int handler::ha_write_row(uchar *buf)
    1 Q+ Z' ?. c0 H4 X$ W{
    2 r# h  U' t: ]2 E/ A    MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })( o3 f: u& f, i) e$ G% W) V& B
    }
    ; u7 C  x- G$ C" `8 t  u& @+ \3 i* B4 W; [: M6 _# j% G
    //这是一个虚方法
    0 f  T/ Z% d  ^& ]virtual int write_row(uchar *buf __attribute__((unused)))
    % K' e" @4 k$ F) Z; n. M+ X{
    # G$ w% }% U. h$ I8 f    return HA_ERR_WRONG_COMMAND;6 @: u/ X* G1 J
    }& p' W0 m7 E2 c3 b2 S3 G
    0 q: F$ U" y- y2 _

    % C0 [9 t1 n8 ^& J" r/ O看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;* u# O9 R5 y0 ?7 s
    ' A4 Z1 r3 b$ N* F5 g: D
    3. 调用链图
    ( X5 ^1 w3 E" |1 l- \& s2 H这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。1 I* c7 [" J5 v  P' P
    6 v: f2 h& O. j" I' |! {8 z" s$ i
    ( s& {4 L- R0 n7 F5 `2 z
    , e8 J7 |7 ?5 `1 I: F$ E
    三:总结: `" b# O0 h) M" A
    大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。6 d$ `3 k& }# G% _" q
    ————————————————
    " U, u7 d( R5 J- S* Q4 T版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    ; ^! g) U" k2 t' }原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415' l6 A7 s5 U! \+ u) W7 a
    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, 2024-4-19 22:46 , Processed in 0.578277 second(s), 50 queries .

    回顶部