QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3515|回复: 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
    一:背景
    $ i. t$ c$ {: m! U( Y1. 讲故事
    & M( N( z  m! I% Q) K1 s最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。7 |( @. t( ?* O7 H+ E# O1 `3 E
    ) x$ M0 W  h; o. I. P1 j8 s
    二:了解架构图! O6 s/ u: m6 z* m$ n& F4 s
    mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。. j$ T0 F* h) _7 b- |. k4 W

    7 Y. H: E' T& V2 ?5 g1. 从架构图入手& _0 h3 x8 G% K* @
    大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。# }7 ~5 c7 O# r
    ( e7 t, s/ [7 E( ^
      b( ]- b6 ]9 @1 l9 E! A
      X7 |6 K8 u' X1 H- e
    其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~$ v( G* Z: X4 x2 T' f$ u6 R2 k

    ; C( b! Z3 `8 O! `6 d4 p4 c2. 功能点介绍# r, Z4 p" }1 u9 P# `7 h' C
    MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
    3 M" T7 J" j( i7 F
    ) e5 Z' Y4 c8 `8 w<1> Client2 Y7 K8 M, p) B, t7 v9 K4 s* h
    不同语言的sdk遵守mysql协议就可以与mysqld进行互通。6 `# v6 d$ T* O6 f
    7 O9 e5 n' Z4 e9 f" K  P
    <2> Connection/Thread Pool
    + `9 Y3 {$ W7 K6 F! xMySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。8 F! B4 h, l4 _; {$ x7 y/ D4 d& q& Z

    : w/ D6 J! q& A5 Q<3> SqlInterface,Parse,Optimizer,Cache
    ' B/ K4 ^3 ~2 h' ^& a' ]  @对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
    7 c1 G# @! ~; c3 O9 t1 X8 M. x  w& ^  X7 W, g6 Z
    <4> Storage Engines
    ! s: X+ H; {# W* [3 y! q- E  U负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。
    : N: Y( D0 a; v3 }/ }' t
    # D& U/ C( K1 y三: 源码分析- P$ u3 Z& L6 A, S/ z* O, _
    关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。4 e7 I* E  F- D- |; i9 M
    - q, V: J- s4 q0 j# l" [1 n
    1. 了解mysql是如何启动监听的
      }2 F* k* I0 B& b9 I手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
    ) @- n* Y! O# K( d+ M8 j3 b  k8 e3 l: u6 z( `, J0 f: H3 s, ^
    ; k3 {4 W! o6 x! w- R# q( @& ~
    $ N( k" a/ }/ m, |3 c5 L. X* j( A
    从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
    * V1 N+ a% R8 [3 T7 i- R  `  P0 c" d' D
    <1> mysqld_main 入口函数 => sql/main.cc
    1 r+ ?; K8 O" w" J% Z' I+ Z2 ^3 L# O+ M! l( d' @

    - S0 W9 x3 Z; J3 `extern int mysqld_main(int argc, char **argv);
    4 L/ M! P; g- l
    9 l7 Z4 ^4 G7 K: Yint main(int argc, char **argv): C8 \( J, c, b: f  A
    {
    % ]9 h; D# ~5 y8 @3 V, X9 Y. M& l  return mysqld_main(argc, argv);
    5 s: k9 e, Q% D6 Z}
    , l9 Y' M1 B  T# d' n7 F( m$ u/ h, @4 r5 V- E
    ; a0 f, n6 d- `0 `/ V9 [" }
    这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
    5 H# u, H/ j. Y
    ; C2 O& H/ Z& H# Q; I  x<2> 创建监听1 q2 b6 }+ W# J4 C* F9 x
    : y. R+ ]  U( f! `- A; x

    7 o* L& J3 E9 `) P% D3 }& Xint mysqld_main(int argc, char **argv)
    " ~+ c9 K0 a5 y; u  {4 x{1 j  @0 v$ x, g2 M; B1 J
        //创建服务监听线程
    * ^3 E- X0 @5 @$ |3 ]( s- H    handle_connections_sockets();
    3 I/ C: U9 M/ `! ^}- d3 e# ^6 t3 {2 }/ }

      f" t* W; ~  v$ Q0 T+ evoid handle_connections_sockets()
    9 H& t/ K- b; k. Y. {6 d2 o9 W0 h$ s{
    - h) w  _5 n3 s% i. t' O. G" ~5 v7 Z     //监听连接4 B8 w% f5 Y- x1 Q* I' x; g0 K
         new_sock= mysql_socket_accept(key_socket_client_connection, sock,
    * i; U: [3 N" x" a; f5 h/ V: w* O                                    (struct sockaddr *)(&cAddr), &length);6 f+ y8 i& \# E: A# T0 P; n

    9 e1 \( @1 `) a, y" d( a    if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))& I. T' ]* d8 B8 `* }
          thd->security_ctx->set_host((char*) my_localhost);
    5 ^8 s1 N  }; A5 k* {4 t# I; u( H3 ^, ?
    ; Z) v0 ^* F7 f6 _    //创建连接6 A5 C$ U4 g" u2 D: ~
        create_new_thread(thd);0 S  B& R/ D4 I% t/ l
    }
    # c: a/ v# O; q$ A2 \$ S" p* e/ ^8 I! X* F! p$ W4 Y6 M  s) v( y
    //创建新线程处理处理用户连接/ S/ ?: H9 d+ P0 W1 j- z
    static void create_new_thread(THD *thd){
    9 }# B+ v( G, s4 N6 K8 f. ^
    * i; h- A1 k( X) U3 M; n   thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
    ( x- M- f* ]% M; z6 s0 @9 i2 D5 a, T$ u2 p9 H" u; n- S
       //线程进了线程调度器
    ' g& ?5 J0 t. Z% t: F; S3 t   MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   
    ' d5 S" ]2 }6 h1 e6 L}
    ! z: W, g, t+ d
    % ]" e) H6 {: G6 y/ P
    2 ~, l4 @0 Z0 C至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。1 k- p8 W7 u4 S

    2 M5 R; g# f# s9 o
    / J7 C! z8 U9 ]) ^5 W  Y: D" G! h3 u2. 理解mysql是如何处理sql请求
    : [4 w0 X, p- f/ B! r! @9 d' H这里我以Insert操作为例稍微解剖下处理流程:2 R9 n9 q) n. H8 I7 F$ t
    " [- A9 t; Z, \. ]# R: ^1 e  G
    当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
    ) Y1 N; z6 x* C0 u. d
    ) Z' n0 v' B9 W* u9 a
    9 I7 j7 P) m7 g! e8 w' ^static scheduler_functions one_thread_per_connection_scheduler_functions=: K/ f" [+ ^( T" f% E* E
    {* x9 |+ z; U  p2 f7 o
      0,                                     // max_threads
    ! B1 a) A* ?7 `+ f0 H. }1 b  NULL,                                  // init: c1 u; e# J( {$ K
      init_new_connection_handler_thread,    // init_new_connection_thread
    - d0 S8 O8 ^5 ]' v  create_thread_to_handle_connection,    // add_connection
    5 g2 \* f. `6 v# x' W9 T% d% a2 T  NULL,                                  // thd_wait_begin
    * n3 U' v1 j& d7 Z7 B$ t  NULL,                                  // thd_wait_end
    & V& |8 L2 r: I* u% L; A0 B  NULL,                                  // post_kill_notification2 I- h3 W2 d" X% D" _8 K
      one_thread_per_connection_end,         // end_thread
    4 ?  @& R* [$ V2 B# `+ J/ l, s  NULL,                                  // end% y2 v) A1 I+ R3 A( S
    };7 F0 M) E" K/ T5 ]! c
    9 N3 ^5 x% L3 n1 _- }
    ! N, _; F7 J; L9 Y3 ^+ V) ~0 D2 e
    从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。) [- i* u* d( h! c; |

    / i4 g0 A! |' k/ T9 ^3 y4 t& C<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
    + {, H. P4 L* ~& _$ {4 r, [; ~  o: I$ \# ^& x9 Q; l
    void create_thread_to_handle_connection(THD *thd)' ?1 _0 x# D1 R+ T  W+ k
    {# q4 [3 l% W% d
         if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
    , Y, F' W. e3 N                                     handle_one_connection,(void*) thd))){}
    0 {' T, X9 I$ B- w# A, V: T}
    4 a, \& _2 ?1 u6 v2 B) I//触发回调函数  handle_one_connection4 G& l& E8 W+ V/ F6 }6 c0 x/ Q) [
    pthread_handler_t handle_one_connection(void *arg)5 N7 t) Z1 f' x7 b, j5 X
    {
    ! b0 |9 A. l" H& U2 q2 a. a) F! E     do_handle_one_connection(thd);
    1 z& z/ f; h2 k2 J}8 Z* m4 j( a+ U* U& ]2 L! S
    //继续处理
    4 W+ r, S8 @* R* E" Bvoid do_handle_one_connection(THD *thd_arg){! p3 I4 V4 p( Q; {* [7 r$ N
        while (thd_is_connection_alive(thd))
    ! m' L% U* d& L) u% s( ^& x    {0 r9 U6 [3 O& ~3 Z+ {
          mysql_audit_release(thd);$ f  v# Z% `& {( o
          if (do_command(thd))  break;  //这里的 do_command 继续处理% N6 d$ Q, v, g$ a/ L/ V' \
        }
    * ?% F0 a+ V$ i; P}
    : _2 v8 S* {* R3 p7 [+ |5 B5 |//继续分发
    8 r2 @5 z8 D6 H3 W- v1 ]bool do_command(THD *thd)8 l. {; q- {0 v7 l2 @4 k+ J, w1 |
    {" r; a5 l4 B% i! V
        return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
    # [. S% h( q" t/ l  D4 v}3 ^% ?0 H' b- a' G$ J
    bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)- J3 q: c! T2 s) [/ M
    {  M  P( K% f* C7 u% {
          switch (command) {" e5 z: t: }& g7 z0 _! P$ l
             case COM_INIT_DB: ....  break;# {7 d6 W5 N9 i$ `& }) Y% i
             ...
    3 f  d# O3 t5 n         case COM_QUERY:   //查询语句:  insert xxxx
    ' }; ~* V1 N) S; O+ O9 u             mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析
    2 X; z3 J  R" f% U4 F           break;
    & B/ ?2 K& r5 c! [; b9 l      }; w4 S' h& P; U+ x% [% }: r3 F
    }3 @! P  C  }, p
    //sql解析模块5 d$ _# a+ O  a& |
    void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)# V4 L3 i. J; o* u9 N! P
    {
    - \; l1 w8 y' s: `2 O- x4 Q* \      error= mysql_execute_command(thd);
    : s- X/ v5 [% k2 d% }, {}
    7 u3 e- I# S1 {" J$ j  k3 c8 `/ H; [( G' v! O, X
    ( n& K* }8 X( ^! C" E
    <2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
    ) X7 @0 I( C, Z4 f4 |' d
    ; T4 F) a# `. `+ y8 q( \' V" W//继续执行3 S8 V0 w- Q7 h1 @9 _" u0 Q4 i
    int mysql_execute_command(THD *thd)
    1 w9 R  H, N. M{% K: G4 e4 i8 @# _2 u
      switch (lex->sql_command)
    9 z/ F$ s  W0 ^( g. Q' U  {
    1 n/ G# S! g. ?5 N& T% l. r      case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;
    / @$ }" B6 p3 H$ c3 C/ A- e, w
          //这个 insert 就是我要追的
    5 A) m9 ?: }( J9 J: u4 R2 \9 F      case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,# P3 E# I* }" P+ {1 t# c, _
                                                  lex->update_list, lex->value_list,
    / B. g. H) t3 |6 m& ]: y" c) J                                              lex->duplicates, lex->ignore);5 C# [  B& |* i/ G# v- I! M+ [
      }
    $ _0 q! f% H. @& _}
    8 R- w8 k. [- \! D; H//insert插入操作处理* }1 O0 A1 Q$ u( g7 w" W# P' x
    bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,$ |$ x$ ]1 S1 Z
                      List<Item> &update_fields, List<Item> &update_values, 2 m# @# \. ]" d3 z) A4 Y' T
                      enum_duplicates duplic, bool ignore)
    4 S. g7 H- L: G: b{
    : c6 N3 b/ c" P1 R1 S. u      while ((values= its++))
      F; J' U4 T! a6 ]      {
    " Q+ T3 C, m- L5 b           error= write_record(thd, table, &info, &update);
    : }2 n& E! P* {' v- k& q      }
    , U1 h' n# I1 a/ N}
    - w+ S4 a6 k" @, Z//写入记录
    : ~# j7 p: g" @  _# q% }5 Z# bint write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)8 q8 Z/ Y5 u  I& Z! g
    {
    ( ~) B( ~, y/ L7 B2 ]1 H    if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
    - S% @+ N9 c6 R1 m8 m& ?; w0 O    {' m/ t, b( e/ j0 e
             // ha_write_row  重点是这个函数7 z% W3 O" C& o6 U0 f  R3 m
             while ((error=table->file->ha_write_row(table->record[0])))+ |: c. M3 k' n+ M( _1 l
             {
    % I" @  O+ {5 h) T$ J+ \1 j             ....) d7 W; u# e  p. `
             }
    ! O" _- p: x! j( E- H) t    }
    6 ^! F7 W) X+ U- p: I" |}
    $ p; b6 K. E' v0 q" M* }& y- C& M& R/ X; L5 `& x( _. Z

    * ?; I7 o% G) n$ I7 g5 B
      |0 S- }, ^- x3 |& p; T8 m0 T可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
    ; b7 @9 y6 Q! z9 u! M/ a: j8 ]7 k2 F4 P1 [  p6 G
    <3> 继续挖 ha_write_row: a4 ^; r0 D4 q8 j4 u

    : h$ t) E5 x- l! wint handler::ha_write_row(uchar *buf)  V' B5 O7 |8 J
    {
    1 [- b" M( x" L. _    MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })3 I% t3 \( t/ N, @
    }+ L8 j3 A$ u6 j2 t+ R' P
    ; C* p( o7 A- C, {+ S1 }! {
    //这是一个虚方法
    3 [) K# G7 E+ dvirtual int write_row(uchar *buf __attribute__((unused)))
    3 M1 ~- V& K3 }* s4 i{/ ?  c* \) P3 e5 ?) z2 k' l: _$ W  L
        return HA_ERR_WRONG_COMMAND;! ^3 i+ [* I8 R/ v. K. U6 i# C
    }+ F! q* W: R( U, _5 C3 ^' U( X0 {

    5 a7 O* [8 V6 p* @" L# @7 d# o5 e8 s8 F7 j7 ]0 g' r
    看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;" x& a4 E- w0 k3 f0 m0 q
    $ p) G" J) w) `; @2 I. y& i2 e  H
    3. 调用链图
    * {7 q- H4 `' n4 {! a# ^- A这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。2 |7 `) j4 a" K, c
    5 b& F' D7 E% F$ s& B: `! I  t
    4 N. l1 P( @" h  P
    8 U+ c4 G5 k1 c: M2 c" I% e: v* L
    三:总结# x7 R5 m, m; l7 o
    大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。
    + W& u" ]; u7 ^# @————————————————
    % @3 H+ z, a/ M6 S5 X: Z& R0 C版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。  m% N. e- H! [$ Q
    原文链接:https://blog.csdn.net/huangxinchen520/article/details/1064874151 e# a+ K5 S( E# q; p
    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, 2026-4-26 12:05 , Processed in 1.815692 second(s), 51 queries .

    回顶部