QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3703|回复: 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
    一:背景3 C+ `( S5 L5 [, `7 p* A: ?
    1. 讲故事
    2 c, s, l  S! v0 c最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。6 a' Q8 k2 I3 p9 C+ ~! b* s9 o3 p
    - G+ `) Z% N$ f) S; n( S* l7 n8 ]0 \+ T
    二:了解架构图5 H& @) o8 [; T0 P: z
    mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。# y9 O, ]# h$ D# q+ x# y: \: V

    % a9 k1 O$ |  A0 s! r1. 从架构图入手$ A, G" O; {" c9 j  j. H) A) h
    大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。' z3 Y; X6 E/ F9 E6 Z7 Z* b" a

    9 k" r& ^6 P: E% ]' U% o# r! T" Q  B$ j. B
    ' \6 F0 {. W: ~5 j' v% c* h& }
    其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~* I: O& J! g6 y0 l. O
    ! O" t6 t. ]7 s9 s5 h, I+ m1 \; I
    2. 功能点介绍  [& O8 y- j3 H! L, }
    MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。& Z5 b& |+ v$ J, l& j7 @7 V

    3 d2 A/ d( L- i<1> Client
    ( l2 n# |) J; M5 O( ^$ B不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
    / P) Y- t2 i: r  y0 [- M1 h4 C& j) c4 N8 a! [9 a
    <2> Connection/Thread Pool
    + Z5 Y) E, j/ f2 f8 c  tMySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。& \! m5 N& j" _; T# E! i

    # D/ p" w! g# J1 d- [# c<3> SqlInterface,Parse,Optimizer,Cache
    ' o+ f* I& e  F$ L( ]对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
    9 @2 K( U/ B* V% y3 _7 l) x9 u7 {+ k7 K
    <4> Storage Engines6 h) I+ @  `+ Q  v
    负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。  Q/ Z( S' t" t
    # R) b1 L9 L8 F( Y3 }5 s
    三: 源码分析
    . g! a* A) `, \* A" L- i关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
    1 K+ r5 Q+ j+ M; T2 V! g" B+ X9 W& V* P& d
    1. 了解mysql是如何启动监听的; }* q( I# |! v6 y
    手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。. M* A* [) Q1 U8 I4 w' |9 Z) `6 s

    6 I- N; b: V9 [" K; k
    # y9 I4 n- X2 V5 Q1 }( F6 p3 m. F6 P7 F7 o' }
    从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。, G# `( h8 ^' T

    # U* c! v: C, U7 `<1> mysqld_main 入口函数 => sql/main.cc
    # W# W0 `- b5 E' P" c$ {. O! ?3 ~- M) |5 w0 O, d+ B1 v

      Y8 J/ D; c: e- _7 o3 p" `4 l; _extern int mysqld_main(int argc, char **argv);( D8 J9 c: X* l- Z

    % G/ G3 ?; E5 L" w4 N8 V+ vint main(int argc, char **argv)
    * C. h/ c# \/ k{+ R8 f) n, s1 t" B, N( s
      return mysqld_main(argc, argv);
    5 g9 Z* q" ]+ k( ?}
    + p2 ~2 \2 g5 u0 r0 r0 P9 @
    + `3 H  c$ m  {; v' m* e; Y" r. y# v' R; x, i' ^" X/ q, U/ X) P  J4 O$ a
    这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
    / W& e$ s" I; q% ^/ m& \& R; |2 b' k5 l
    <2> 创建监听
    5 p9 ~; d; g6 y. g) |* W4 j& R* G3 c1 j7 u. u3 w( z
    2 c/ m3 r% F0 C
    int mysqld_main(int argc, char **argv)
      E3 |8 T" I3 ]0 F& d/ \2 n' j{: P; N: V/ ?& e. o. G
        //创建服务监听线程
    0 ?" u$ l% |  R) F4 ]    handle_connections_sockets();! y5 n" C5 h- I2 g5 S3 h  \
    }
    ) j6 m6 w" P5 O4 C3 k
    % [" h1 e& [6 p7 g5 i; y/ nvoid handle_connections_sockets()% E3 e: F5 N/ i1 ?. y- V
    {5 a3 s2 o; D- Z3 a& ]7 k
         //监听连接
    1 d5 m& O: k! V# a) a1 |# E. q     new_sock= mysql_socket_accept(key_socket_client_connection, sock,
    : U% b! _3 x1 j% h                                    (struct sockaddr *)(&cAddr), &length);
    " v/ z/ T, w4 O2 s  a- a% U/ G( i
    , X+ i( E. F  @* f! m/ T/ g    if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
    * J/ N7 h) N) C      thd->security_ctx->set_host((char*) my_localhost);
    : ^" ^. C7 P  Q: @/ i( f; l3 E! l8 [  {
        //创建连接
    / L3 A& s) W0 \% g+ p$ F+ A    create_new_thread(thd);
    ' s1 @( h& x5 V6 @8 ?# T}
    5 j/ }; F( e& y6 V) ?/ S5 e9 e" W" k! X7 |" G
    //创建新线程处理处理用户连接" U+ H7 q. a$ N* c/ l! h4 Q
    static void create_new_thread(THD *thd){
    1 o2 i, A) P6 i7 k, ?5 t4 ?
    % W+ W" Y* B+ I   thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;, [* m' D" h  j. |4 F+ Y% K
    1 l/ r6 D( T' N# e) I( s
       //线程进了线程调度器3 {( [( l# e% f' D  ]
       MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   % m% }  M* _1 c3 M, {+ u
    }
    ; q/ P* C2 e1 {/ y" r2 b. K9 ^7 P. K' f" w) I/ Z/ q" D
    * R  }2 i/ a- ~+ Y4 @3 r- J& P
    至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。0 _, H" U! v4 R/ j

    / G& j0 z' v$ e# L# j  W+ H' B( i* P: y# r! w6 u
    2. 理解mysql是如何处理sql请求
    ; n8 W& s1 R7 _1 g* n! K  M这里我以Insert操作为例稍微解剖下处理流程:+ |# L7 ^; y, h0 N# t8 J$ m4 i
    5 O3 f7 Q: ]: a9 w9 `
    当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
    7 n( J) I" Q: ~( N2 J  a, C
    # w2 H2 t# _' \- I# e  @1 ~$ |
    3 g* O0 a: L7 T& sstatic scheduler_functions one_thread_per_connection_scheduler_functions=
    , i% _+ j# Q  _& ~8 U" Z{
    $ l( E) t: _0 p9 o: L9 O  0,                                     // max_threads
    9 H8 s: o3 n1 q6 c# K  NULL,                                  // init
    2 |$ a$ I# w4 @* O  init_new_connection_handler_thread,    // init_new_connection_thread
    2 {6 P& |3 P' O! T  create_thread_to_handle_connection,    // add_connection
    . b* K9 R+ `, W( b6 n7 K. l/ |& D  NULL,                                  // thd_wait_begin3 z, ]+ e4 L: X6 k
      NULL,                                  // thd_wait_end
    $ C: n" B- O6 p8 m9 x  NULL,                                  // post_kill_notification& j3 J, W# N  ~5 X( Y3 m8 u) Z
      one_thread_per_connection_end,         // end_thread
    , u" p* L% i2 {0 m4 ?5 P# S0 B  NULL,                                  // end
    6 P3 s8 z* Y" W6 B: j0 G};
    $ O% T6 M3 }( `" r$ C8 D, ^  h3 \: q/ o

    ; K! L2 s- Y+ g& Z. Y+ y7 Q5 H从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
    % {  D& g& m& s
    : I9 e/ K0 t  l: x5 p# ~* v# `<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪* S1 ^+ u6 R8 f9 r! j& N: f7 A$ t* [

    ! }9 G& n; J" L: \void create_thread_to_handle_connection(THD *thd)9 |: a; t! i' ~2 S; a" V/ \
    {6 S  v6 N' O& ]8 V$ @
         if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
    $ [& w  b. V8 }                                     handle_one_connection,(void*) thd))){}
    ; }2 Y4 D  w# e2 L6 c2 T}
    / w& w* y" `% W# \6 P: q//触发回调函数  handle_one_connection
    6 s; A& X9 r3 d3 c5 Jpthread_handler_t handle_one_connection(void *arg); w* ^9 i' p4 ^( I+ e" u) w
    {
    4 U" P' W* ~9 A     do_handle_one_connection(thd);
    0 b( O8 M$ b( k/ i7 ?! s}; Y: ~) K$ j+ P: B" Y( L9 J
    //继续处理
    6 I4 o: i3 ^6 Bvoid do_handle_one_connection(THD *thd_arg){5 @, b) m9 ]5 q
        while (thd_is_connection_alive(thd))
    3 G+ a# E5 W) n3 k3 [. {- |    {
    : L* B% c; P5 F      mysql_audit_release(thd);
    2 `1 _) N' N' q      if (do_command(thd))  break;  //这里的 do_command 继续处理9 l4 [- ?. x; l* u2 k$ y) |$ F
        }
    7 g8 Y( f1 c6 _% ?; S8 e. Y( ~8 U2 C}+ X" o/ S8 x" y: F1 g* T( k# h" l
    //继续分发. |$ c# M+ n9 F: X; b" W5 ?
    bool do_command(THD *thd)
    - ~8 v$ u3 ~8 m) d{
    0 K7 F# L' r9 w7 l    return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
      ]; s, z: K  C& b% l, z1 R}" N3 N; q8 d' s% u3 g2 ^/ A
    bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
    , w& P9 R; h. O! C1 l{
    + |! `+ I: A4 e% X      switch (command) {
    * V3 f( L8 m: E/ `( `1 ]) ?         case COM_INIT_DB: ....  break;
    " R/ B; n2 Y% k: s  T# R, Z         ...8 M% E4 [8 f4 o0 e7 B  V
             case COM_QUERY:   //查询语句:  insert xxxx
    5 B: o& A3 u( H9 ]' {: D8 C             mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析* `1 F* ^2 m2 j9 X" o5 B9 G
               break;
    % _" d/ R. ]. D8 \9 S' X2 a$ p      }5 Q9 P6 g$ ^7 c& s& ~  E9 h2 ~
    }
    . |- v7 f5 H* K//sql解析模块9 w  h* K. n4 G, v  w2 k( t
    void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
    8 }2 N4 W& N; J6 g/ R3 q8 B1 H{
    4 C+ D9 r7 j/ C  K& R6 ?      error= mysql_execute_command(thd);
    3 U0 _8 Z; K% u; V8 L}
    2 n3 l3 ^# O0 w: q0 j: t+ O6 M/ j; {! Z+ P: ]/ Z

    ) n, A% b4 B0 }1 f/ n2 R0 ]  u<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。: V% d: S0 _* w9 s% E( h; Y
    3 g% r; W' t! z! C! g
    //继续执行
    8 p6 P+ [2 l6 n: l% J5 g0 ^int mysql_execute_command(THD *thd)* Z0 E6 `$ l" R: g, D$ _
    {
    $ X: s! U: q; S  switch (lex->sql_command) 8 Y- r0 r' k7 K+ Z4 A: B' h
      {/ }: J3 L. G3 ^' n. V8 w
          case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;
    + ^  y* W# C$ a/ J+ ^1 F7 \# X1 j
          //这个 insert 就是我要追的) I$ ^, n8 M8 ^8 ], L
          case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
    , i# y8 T! u8 y" a$ j) ^# Q( s                                              lex->update_list, lex->value_list,
    ) [. ^" l: g, J: ?. `                                              lex->duplicates, lex->ignore);
    6 h/ R( x$ g' ?- C/ b( k  }7 z6 r/ V: |7 l) s+ c8 b$ |
    }
    % \1 z$ ?) `: Q//insert插入操作处理
    ; [' l& c* _0 D7 }$ S$ D5 Cbool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
    % B9 h# o: ?: x! U0 ~                  List<Item> &update_fields, List<Item> &update_values, ! j+ v  m! d( E; s9 x  k3 Q7 \
                      enum_duplicates duplic, bool ignore)' r! W2 O) f1 a7 t" A
    {- P+ I, I+ v" K6 c( C- m
          while ((values= its++))
    + C6 A/ X1 B- |" o7 V$ ~1 p      {
    : O) E5 x3 ?/ k8 r' H. s9 S2 W           error= write_record(thd, table, &info, &update);
    % x0 `" k& z# Z. V) ?      }$ v' w! ]# L& [% k
    }0 [& ~6 Q, U6 ?8 [" D( u4 N
    //写入记录
    . V* j- M0 Y6 Q& V/ }int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
    , d. Y0 Z  x/ G{8 x' D$ k! \# ~: e; x
        if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
    3 W0 q( }# `" e7 n5 ^    {
    / n- }8 O, f( r) q! i         // ha_write_row  重点是这个函数
    ' ~! D+ f. X+ u) x. L         while ((error=table->file->ha_write_row(table->record[0])))
    8 e6 Z- Y. N" I! w         {
    8 E+ Q& l8 [7 c/ c! y             ....
    ; K4 V0 e7 l% }8 C2 b( R) `         }' }# e2 i" E$ S1 F8 y. O
        }
    3 |  j( c+ c& v4 }* Q}- b4 J/ z$ H: {9 a8 T( V; V

    . ]7 {! u4 e6 h
    3 j: A  _' V: s" C# [7 ~. N0 i4 |
    可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
    * U& H7 S. t7 U4 q) s  _8 i
    3 W: V4 s' p$ L% `1 i$ @<3> 继续挖 ha_write_row
    7 {7 k% X+ x+ W* g' h% k3 B2 o" `! N6 g& ]2 u2 V: N
    int handler::ha_write_row(uchar *buf); y9 i# u7 X3 o. ?1 v
    {0 j; X$ n7 q+ ^4 }9 ]
        MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })+ @& f2 g2 v9 }1 ?& I+ g* g8 w
    }
    . R3 h7 M& T, ?* N% i8 P/ B
    4 j) J1 W- ^7 b/ Z/ b//这是一个虚方法
    8 H4 J! {; X2 g$ k3 B3 |virtual int write_row(uchar *buf __attribute__((unused)))) _) q" U6 b/ e7 J6 Q9 Z; t' h8 S
    {* S' s3 y$ i& S$ i
        return HA_ERR_WRONG_COMMAND;
    + p6 s0 H: B$ i! `& e0 Y: A' m}
    - A: k. y) X# i' n8 _
    6 l5 X) S; @7 e$ q& Q. z& T! a  C/ P/ n
    看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;
    $ K- F" J% ~# Z* Q" S( \
    ) R' a' a9 t5 ^& x4 D3. 调用链图
    ! B& p7 i0 `+ |/ `" M这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
    - V+ Q8 J% `* d) U# O8 p+ w, T" A" r2 r( M) L. U
    4 i. u5 m! a0 h8 O! Y# K/ M

    $ t1 t" p) D6 w8 I: d3 p8 \- o三:总结" O3 H- y! `  D' z
    大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。) a, b, }/ ^1 h& T' K
    ————————————————1 P% B$ P/ L2 g  a* q
    版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。9 c- ?4 ^7 i2 M% h
    原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
    & d+ H% `1 x$ k
    zan
    转播转播0 分享淘帖0 分享分享0 收藏收藏0 支持支持0 反对反对0 微信微信
    您需要登录后才可以回帖 登录 | 注册地址

    fastpost qq
    收缩
    • 电话咨询

    • 04714969085

    关于我们| 联系我们| 诚征英才| 对外合作| 产品服务| QQ

    手机版|Archiver| |繁體中文 手机客户端  

    蒙公网安备 15010502000194号

    Powered by Discuz! X2.5   © 2001-2013 数学建模网-数学中国 ( 蒙ICP备14002410号-3 蒙BBS备-0002号 )     论坛法律顾问:王兆丰

    GMT+8, 2026-11-9 19:32 , Processed in 0.218704 second(s), 50 queries .

    回顶部