QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 1482|回复: 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
    一:背景
    / [7 ^# _. R- q( R+ R9 T- z1. 讲故事' y: f1 H. s/ n$ q; V7 W- E
    最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
    * I3 ], I, h/ I5 Q* @
    4 Y# N; [, t; [) t, U7 B二:了解架构图
    5 b: g4 j$ F3 S3 u8 amysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
    , [8 f7 l0 _/ f1 y  k# I/ n. x/ r  i3 b7 P  C! R/ O8 V! e
    1. 从架构图入手
    1 W" y* i! Y0 ?- b6 m大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。9 p$ k( m% F6 ^6 b0 k# `& K

    ) U( f; ]! V: w( J; P2 g% j# E) E6 E2 m' |* r0 X& x

    * s8 Q% k: n- @其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
    " c! P1 \: K: }& A! ]& D( t! i" v! X1 t( [( L$ x# \
    2. 功能点介绍3 E# E, {7 Q4 j5 k
    MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。" z* r' c# \/ i" \% R& }
    % |# x' L! W6 R- r  |2 `
    <1> Client
    % J/ O6 K4 `; j& `/ T+ L: E) [' z不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
    + a1 t) h. Z6 @% [& |; S' ^& l) g* H/ A& _$ ^! B
    <2> Connection/Thread Pool6 n2 U  N. F! S% u; O: u6 j
    MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。+ T: [  z( k2 H( p
    8 Q9 E, E) W; S* e$ i4 a! h
    <3> SqlInterface,Parse,Optimizer,Cache3 f6 x/ o& j! i& N' L
    对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
    ( [, E; E8 o! }  y, |8 s
    : [' _* v: }# ~/ E<4> Storage Engines
    % q6 q& v( ?- g# Z+ b6 ~* c; C负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。: i( S$ W/ Q. [# f
    / ^1 y  v/ z% x0 f
    三: 源码分析! T+ R9 g) Z5 h5 m
    关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
    + Q3 I3 |! f/ W2 Q  l! `1 L- {0 M; v5 C% H2 d! b2 w1 U4 z+ o5 r% B
    1. 了解mysql是如何启动监听的
    3 @3 ~/ `% P$ a! j手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。; B" u7 S3 c( z4 j  J5 @2 Q

    : ~- Z4 P9 b) i4 r: w
    , J/ K9 x! A. M  O
    3 N% J/ E" [& l从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。% l& k0 K) ~: Z7 Q) S) d9 t
      @, v+ {/ t3 T
    <1> mysqld_main 入口函数 => sql/main.cc
    ' h3 \! p  A$ M) r; N  N* X1 K3 o$ `2 W; H& s2 Y
    & s" @0 {5 E2 a7 i
    extern int mysqld_main(int argc, char **argv);
    $ e+ {* q$ F5 a) c! A9 P* T
    ' x& N4 _: d8 ~* H- Nint main(int argc, char **argv)
    9 I! ?7 _4 b: q& `+ V$ Y{
    7 j7 X$ y8 L+ U# Q) u  return mysqld_main(argc, argv);
    2 q0 i1 U4 v5 ^; O2 P}5 y2 a: y3 {+ f' t$ c
    4 V& s2 O2 S( @+ k4 x+ ?+ j

    . B, o  ~/ K! v+ h. N8 W, b这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
    6 x' w8 H. }& c; R- P, j
    7 U8 a1 N" ?6 b0 V& p1 ?% I: `9 c! [<2> 创建监听
    6 O# Q; R& j5 ]5 L9 b- ?% [5 `/ u# L1 V- r4 d' T7 ^9 K- h

    - F& C$ H7 a) ?/ |# Yint mysqld_main(int argc, char **argv)+ y1 ]' w- k2 k
    {4 t- m; |* K) U/ C" C7 @7 m
        //创建服务监听线程3 z2 _( v% p* P$ z7 p: x$ `
        handle_connections_sockets();
    $ ?9 L( i! g% h}
    4 S* |: t- y: R; e. Q1 t1 S6 z. d4 {% r  ^' P: a" F
    void handle_connections_sockets()  y% i8 F$ Z5 w
    {
    ; h% V$ M* Q. Q! ?/ g     //监听连接
    - F' w, D) R" S3 ^: a+ K7 H     new_sock= mysql_socket_accept(key_socket_client_connection, sock,) N) H6 [) H8 G" l
                                        (struct sockaddr *)(&cAddr), &length);
    6 s  D5 A3 o1 B9 h, j# H, F5 w2 O" o: A$ v  U& w
        if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
    % W  C( l- v! k# q$ G0 Z      thd->security_ctx->set_host((char*) my_localhost);2 J4 M' J# V+ ?, Q' G. K; a1 C

    6 r4 u  o1 N7 C3 _/ U! R    //创建连接2 j* s) c) Z. }4 @1 ^2 B& o
        create_new_thread(thd);% Y7 X6 `( K& d" ~1 I. u) T/ n
    }5 b' x$ z) F8 ]6 y# t6 x

    5 Y8 S! D& v  ~& v# n3 ^//创建新线程处理处理用户连接
    3 X, f! m! c7 D6 ~static void create_new_thread(THD *thd){& ~/ H" Q! _: V$ _3 ~
    * @" N& q6 A+ V1 n  J3 t0 ]: L
       thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;) @  R: Z- h9 ~# w# n
    4 \4 q  O! N2 R- A  a* C( W
       //线程进了线程调度器
    : b3 S; a3 R6 }# X& ~   MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   
    0 ^' x! n# p' s. F$ Q7 ~}' u& C8 Y( T) H- {
    $ x% S7 _3 m& K8 {" ?  `$ D

    : u9 v2 G4 D  X! T5 ~至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。) [! D# U1 r( E) c: Q
    0 l: V* e' Q) B  b8 f

    ' B$ ^- Q+ ?! d9 w# x2. 理解mysql是如何处理sql请求
    $ B0 U0 Y9 y1 ~1 v+ r* e这里我以Insert操作为例稍微解剖下处理流程:
    " s% T4 `$ e+ }  K/ a+ Y' b& V
    7 o! \# r4 {5 c* w当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。3 l  K/ F$ n7 E$ H% Q! G4 T
    - Z5 b& k" b8 W; x5 Q( Q# Z

    5 n# m9 F& J! O1 [  \, ystatic scheduler_functions one_thread_per_connection_scheduler_functions=9 M; R5 S' v* j/ [+ d! O
    {
    2 a. |) @' E1 t7 f' _5 u' r! r  0,                                     // max_threads
    6 Z4 [+ p# O! s4 R6 S8 l6 p, L2 @  NULL,                                  // init; f4 r8 A+ b3 D: r; Y. n5 D7 `
      init_new_connection_handler_thread,    // init_new_connection_thread2 b5 Z% c% z- \5 ^: b; |
      create_thread_to_handle_connection,    // add_connection) S% l& _3 d0 B" C5 l6 c: |
      NULL,                                  // thd_wait_begin- |( d; v$ x/ s) D5 c" k! N- Y
      NULL,                                  // thd_wait_end1 Z: Y7 ?3 c% _2 M- R3 q
      NULL,                                  // post_kill_notification
    4 R0 x% ?  x, s2 G2 G3 X  one_thread_per_connection_end,         // end_thread
      a: d' Q. @0 u% d4 q4 q+ P! v  NULL,                                  // end
    3 Y( C( U6 e2 U7 i( D6 B! ]$ s};
    8 z/ \& Q! ~; m4 F; I& e, r# J& W! n! B/ k/ ?) q+ {) w
    1 s! s" [5 i8 F1 [
    从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
    ) o5 D* m, k& Q0 s5 Y$ V0 O, X: c, c7 N# k% u0 ^
    <1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
    5 S2 R. M9 y5 O! d/ r
    0 Y: B' x% k2 Y( N# Fvoid create_thread_to_handle_connection(THD *thd)# I! r2 W5 p9 a' z6 S
    {# U, j# O% n5 H
         if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
    ; H1 W9 G' \* U* b                                     handle_one_connection,(void*) thd))){}
    & u& [6 k1 i+ X}4 Y* N& H+ U7 v$ z5 h( w6 k; r( Y
    //触发回调函数  handle_one_connection# q& q6 S2 a! S: P3 I" R2 |
    pthread_handler_t handle_one_connection(void *arg)
    9 M! U$ V# A6 L6 n) i( k6 N1 W{! `' W& I  z2 y4 {
         do_handle_one_connection(thd);
    $ Y6 B8 D% e) ^1 a}& U5 c; _. i( j# x& c; _
    //继续处理: A, Z& p1 |; k) ^8 `) M8 t$ Q& N% z
    void do_handle_one_connection(THD *thd_arg){8 S7 Q3 H- q* L2 B8 c9 O5 S
        while (thd_is_connection_alive(thd))
    % _6 O1 t* t7 ~9 {    {! M) S% L$ i, H$ `( y! L
          mysql_audit_release(thd);. g4 {' D9 q5 M% {& _
          if (do_command(thd))  break;  //这里的 do_command 继续处理* m3 D& O( r: C* a
        }$ p7 x" \6 V% T3 J) f) i
    }
    $ Z. J8 H1 z# k! i. `$ r" i4 P$ H& ?$ T//继续分发) Y2 h# ?* }7 `3 ]2 g% G% n2 y4 [
    bool do_command(THD *thd)) o3 i# O" a( P' O9 b
    {7 C6 R) Q) Z+ Q9 [6 M
        return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));5 J/ X" B! L7 m0 {7 P) E
    }
    ! M3 l# q% G% |; `5 Wbool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
    + j/ C8 @# y+ c# ?{
    $ J' |( _; g$ I      switch (command) {; ^  y8 q: ~1 {7 O- ]$ ~. M7 c
             case COM_INIT_DB: ....  break;
    * \4 ~( ?  X$ k         ...2 F$ x; G& h8 ]! M' ]8 C8 g
             case COM_QUERY:   //查询语句:  insert xxxx: j3 k# M/ G+ \/ q
                 mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析
      J: u* \0 g0 I$ |           break;
    ; U; M9 m+ _7 |* q' H# U      }
    9 A" S8 ]3 d( x' I}
    * O& e3 Y0 H- L6 {! v/ Q# [//sql解析模块+ @" q6 l  L/ y
    void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
    0 L8 G7 W/ S! n" T{/ h7 ?# K9 n7 [) i3 P3 L
          error= mysql_execute_command(thd);$ p: Q6 ~% @' K" T% P, k% [( l
    }* Q4 I- \9 t) P" s+ ?  {. g+ y

      H: [9 Y& g0 o( ^1 D+ A
    ; F6 x/ N: @3 y1 m5 T0 @& R9 ]<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
    2 y5 ^( E& p3 h5 H$ ]/ B& F" {' l# W3 L
    //继续执行
      B. I, A3 U0 ~. eint mysql_execute_command(THD *thd)
    7 t/ O  x, E  z5 }{# G5 c+ P8 x/ c0 F6 G- _) t
      switch (lex->sql_command) 1 p( c" W' U4 k+ \, ~; v
      {
    4 _* \) E: K+ M6 b5 z! |" [      case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;3 n  l/ Y1 f, h0 n* G

    + \* d  w4 |# E3 y/ h      //这个 insert 就是我要追的
    4 s$ I! P7 C' f5 l8 F% `8 h      case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
    ; a6 A+ ?$ A9 ^2 l: g7 ^! |5 m                                              lex->update_list, lex->value_list,# `' d1 W% K7 f7 j* _* U( d
                                                  lex->duplicates, lex->ignore);
    3 p- W* P% x) m) G$ I! L  }
    3 ^, @; f4 D+ T1 k}
    # a2 z; {2 Q) t5 v4 @6 m//insert插入操作处理/ B: l* d3 |, I# g# ?3 a; L; f
    bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,2 e" t* L& `* f  Q$ X. J
                      List<Item> &update_fields, List<Item> &update_values, 2 g" V$ g. S' c! Q0 ]  u
                      enum_duplicates duplic, bool ignore)$ C* r+ M$ C" ], m0 `5 R% b& p2 A
    {7 v! K6 Q2 p+ G5 \0 C, O; g% ~
          while ((values= its++)); Z0 _0 I3 R; k! w1 m& S3 y
          {7 P' L+ j- m4 b/ ~3 H
               error= write_record(thd, table, &info, &update);
    * {; C0 r: N% @5 L* i4 b      }
    1 y9 `8 O  ]8 n3 [% N( Y; F}0 E* M1 _; X6 k  _1 N" [
    //写入记录& [$ f0 F9 M  w, s( E# j/ C
    int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)# m* E( @& w0 `9 c  W% o
    {3 J) t" J9 S3 z! H) z7 k) O0 ?
        if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
    , ], p+ T7 J- v) W    {
    ) Z( S: F/ a! j+ {         // ha_write_row  重点是这个函数: |! q% U# a. {' m! R: n( P& z
             while ((error=table->file->ha_write_row(table->record[0])))3 {0 I3 n1 |6 M& G/ S
             {7 I9 d+ w" G. m& a* _
                 ..../ B: l7 h& x$ ^1 N, t( b
             }/ H' l$ t# b( j2 ~3 h
        }
    " i+ N3 O! B' k: y}
    6 Z/ N" h7 N9 N6 ?( {! J' \
    & Y2 q+ p% ]& {
    : f% s0 H& x3 W4 n/ c' m  o7 `, \9 {  g7 `2 n  P
    可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
    1 w- b  T; z4 a/ H9 U# t! Y% [. D
    5 f1 P: i& ^+ F3 J5 ^<3> 继续挖 ha_write_row/ T$ d6 d/ g6 N# L& d! \
    : x: F4 r5 D, X/ P. H! z
    int handler::ha_write_row(uchar *buf)
    8 z5 I, D7 ]0 p* i% Q. p{
    % X. w6 |- k5 z( }" I7 M4 ~. _7 n    MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
    % R7 {/ ^7 C6 V+ n5 z. E}
    5 A# f1 t! P4 D) g8 o9 z# V9 e
    7 P% i4 m/ t& i7 h" ?+ u, c& k//这是一个虚方法
    & u+ V: \3 F, P+ S2 a3 C# S1 Rvirtual int write_row(uchar *buf __attribute__((unused)))
    ' p' S9 W7 e+ Z{  h: |5 j- L7 y+ K- t8 @# D& _& O
        return HA_ERR_WRONG_COMMAND;
    ' ?0 D! r% E% P' z}
    9 i, o+ v: ~+ t! N3 l& T% K2 o
    1 v# R$ p% i) k" ^: x; d7 m( g' t" N
    ) a) q$ |+ W( v6 U$ K看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;
    7 Z% h) g* Z6 c$ r/ \# s( K5 s- M/ Z$ W
    3. 调用链图
    - N. v9 X2 ^9 Q( p+ x这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
    5 ^. V$ V' i0 x( y) e
    & C( W+ L5 O* U+ z- E* a' J6 K3 g. s0 Y( e* ^8 `0 t' z
    0 b6 s+ y: |9 F: {- ?% N1 y2 n6 d
    三:总结
    # C  M6 R5 ?5 }, Q* Y大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。
    ! {9 y$ G3 w1 @" E& D————————————————
    ; q1 }; R/ F% B: z版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    9 o6 X0 ~  b3 T. s2 ^3 Z原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415- I' {  S9 w; d% L" T  D0 Z" s
    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-24 20:44 , Processed in 0.430241 second(s), 50 queries .

    回顶部