QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3529|回复: 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
    一:背景* Z5 j; l) p9 a
    1. 讲故事
    0 \' H! t* v0 I* ~1 [最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。: r% p+ E2 `+ S* e' l9 _! c. K

    ' j) B, G, s. }3 h. I二:了解架构图
    9 N4 x& Y' t0 Mmysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
    3 h+ C& j# v! o
    * w& x% R! _' ]4 G; [' C1. 从架构图入手3 U2 q) k: |( C7 c) i# Z
    大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
    4 b9 p6 Q7 G! x; i
    / g9 h0 Z& l8 l2 [# \
    $ u: B1 e6 l3 \; \+ i. O) i% g) O# _2 v" f* L0 U% A
    其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~. {' e) ?( W' s3 i
    ; y3 y1 S/ G4 x* m8 {" t
    2. 功能点介绍1 x, G0 F  L8 ?  C( ?
    MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。1 [$ B! q- P0 z9 S& v) _* T

    1 e. Z# ~( C) ^' y6 M4 l* L<1> Client# l( q$ n" E) g' V
    不同语言的sdk遵守mysql协议就可以与mysqld进行互通。1 r- ?9 j" `" H9 u; l: P" }
    6 D" W8 T3 [6 A" Q& b+ `4 k5 s
    <2> Connection/Thread Pool
    7 I" a- L" h- cMySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。- ^; E2 d* e1 R3 d2 e+ }2 g0 K
    ( W! T6 r( s% D  V1 P+ _! |$ `" {
    <3> SqlInterface,Parse,Optimizer,Cache7 O/ J# M& {& n& s$ j* P
    对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
    : @" k: L" E0 `- G
    / @* _! R# P0 x# `# `<4> Storage Engines% }+ a+ ~- R4 ^5 z* c6 u1 e
    负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。
    8 S2 j! j. h  \. j- E: w" X6 E( }
    三: 源码分析' a9 O- a+ q6 `( ~( v- R) t, U
    关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。) t  E+ |, X3 c- t1 x) H9 y1 V

    + Q. v2 p1 Q, f& J8 c) w1. 了解mysql是如何启动监听的
    5 B  f* R- k3 f1 x/ ~9 ^8 V手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。: w- P* ]2 g& i6 f
    1 i! N. R$ X: _9 J9 x: X1 h/ i) t
    : J# p2 b! [+ A& N" |! t2 E
    + [$ r. b' _; k8 i
    从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。. G9 H1 j4 \  K$ W( T: u( M$ `

    , f4 M+ N5 Z! O4 h; M<1> mysqld_main 入口函数 => sql/main.cc
    $ J  j7 z& @% I. _5 K% m' P6 f
    , M0 a  Z) q- U7 B' h$ ]% E# h' V# o! j
    extern int mysqld_main(int argc, char **argv);
    % Z7 Q3 c) y, L& V2 h# ^4 N
    8 S6 \; O/ T8 _int main(int argc, char **argv)
    $ d0 j$ e4 I- l( {; ]" N2 d: f{
    4 P% ?8 B, J8 V$ Q" e8 f  return mysqld_main(argc, argv);
    ) U3 P' e$ M% Z}' M2 f+ P4 H$ v' ?( n2 x1 f2 l
    3 [8 ^- t( w; }4 l3 A5 O& h4 E
    7 x: o, a  o3 g- B* t
    这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
    # N' v1 @5 M! R' ^( b; T7 s# ?+ g8 q. N+ h1 _5 g3 x
    <2> 创建监听) z9 n1 [. ^5 x2 }& X7 s% L' y
    : A4 X, U. `4 u, P

    ( o, L, O4 V% Iint mysqld_main(int argc, char **argv)! y2 x1 l1 ^" `: t
    {9 o, Y! [3 \% @5 v0 t  B: H. M# L
        //创建服务监听线程- P% @  L# Z- G+ E, H3 d
        handle_connections_sockets();1 n2 r7 y* |4 q0 j9 N
    }: k/ X+ A' p* t+ @7 h
    # E& q: v8 o4 ]# |
    void handle_connections_sockets()3 i7 E. o/ m) c% C
    {/ ?# _% n" k, z  k" M% p
         //监听连接
    ( q! V% N% T. ^, B6 q0 I+ N     new_sock= mysql_socket_accept(key_socket_client_connection, sock,. Q  Y7 T4 ]& e( x: E7 v
                                        (struct sockaddr *)(&cAddr), &length);
    7 n" @, f& M5 ~
    & P; T* U* y/ ~9 m0 R    if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))5 `. H  t2 v' @. t: l$ R
          thd->security_ctx->set_host((char*) my_localhost);
    * h+ x6 d- a% c  k& v
    " o, y" @" k$ j2 z: G8 V; H% m5 C    //创建连接
    6 c) G; e, o, V" F8 G# N& L    create_new_thread(thd);
    * @/ i  S2 d$ G/ U, V$ G}
    $ I6 y& }% D. q' K% U% j3 e2 z  t, Q% @5 }( G0 h) N/ Y3 r
    //创建新线程处理处理用户连接
    , |. ?; {  v9 `) qstatic void create_new_thread(THD *thd){
    ) V6 p) G( k; h! p7 _# p" I1 ~- `) u( {" o& j' F: b% F
       thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
    . t5 \2 \- F# n$ Y; u6 g. d& Q$ N8 F) Q
       //线程进了线程调度器8 Z* e; o; j! ], v5 h
       MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   . z' A8 T5 Z, r& R0 O' A* x
    }
    4 l* g# y2 k3 d# A7 h$ F8 ?6 [, Y  G3 l/ \) t
    5 A; g7 t2 b7 i
    至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
      g( p# c) ]. t. ?& I9 q1 U$ B5 Y2 F1 c

    ( R! o2 |, q8 l( @4 S! y4 d6 u2. 理解mysql是如何处理sql请求
    . _/ s7 `$ L% N5 D( z* @( s) h9 |这里我以Insert操作为例稍微解剖下处理流程:
    3 m' p: ]( t- h8 k! ]$ Y. q5 |6 Y/ B5 ?1 j" ?
    当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。5 R& }; e, V8 v

    9 r/ q! t5 c# K4 B5 N/ h$ K# S: w# s2 {( }" w# y
    static scheduler_functions one_thread_per_connection_scheduler_functions=
    ( Z1 J, L. U( C8 J, y' @, h+ Q4 f{4 e4 N3 t+ o4 T( A+ A
      0,                                     // max_threads9 G& Z  i. X; o8 J5 [- k$ D4 u1 }
      NULL,                                  // init2 f/ h$ l3 a1 b3 S
      init_new_connection_handler_thread,    // init_new_connection_thread
    . W; I9 G$ S& M& p& T  create_thread_to_handle_connection,    // add_connection2 M/ i% R3 ~' U1 g2 M* _: f
      NULL,                                  // thd_wait_begin
    & j3 f0 q0 y3 P  NULL,                                  // thd_wait_end
    $ o0 z6 B- f- t7 v0 J  NULL,                                  // post_kill_notification
    # o. ~* b- k. n; `, E* V0 C  one_thread_per_connection_end,         // end_thread
    - v: L- h7 t3 s% p! N; d' j6 M  NULL,                                  // end* W9 n2 r  Y4 Q- Z  K0 d3 b
    };
    8 A$ `& }, g" Y; G) _+ \$ ~% x& b& _  ]9 Y+ y- F  H) B

    ; L0 g( K+ y4 E# [2 a8 ?* _. @8 e2 |从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。+ I) N# ]7 w9 q8 A* x, Z
    ! b+ M" ]% O! Z- \- G4 t5 \1 z+ ]
    <1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
    / q' }+ V( x# _& G
    $ _2 N. o/ S! X  Nvoid create_thread_to_handle_connection(THD *thd)
    " ]. M  Z" V! j8 d0 {! l; }$ k0 e{. |, R0 `. [9 b1 K1 w& i8 i
         if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
    3 _3 Q- E5 F7 a% W" n& t# Y                                     handle_one_connection,(void*) thd))){}/ \) I, [- x" @  [, q" Q. o
    }* k+ X; Z/ d% X* ~7 A9 V
    //触发回调函数  handle_one_connection
    9 F9 P: Z7 S$ T. dpthread_handler_t handle_one_connection(void *arg)) H7 }; I1 f2 _  j
    {
    ; A  |5 d% z9 A; A# S! l2 V* d     do_handle_one_connection(thd);
    7 F2 C( u# g3 ~$ m- E}
    8 z$ x% I  @7 P$ [//继续处理
    2 S1 z4 `; o4 T% gvoid do_handle_one_connection(THD *thd_arg){4 Q6 b; v) U& t
        while (thd_is_connection_alive(thd))7 m; s/ W5 t. S, m8 q0 f
        {/ B, r, [# ~3 h3 |4 u/ z
          mysql_audit_release(thd);3 ^: r1 J. K; I4 S
          if (do_command(thd))  break;  //这里的 do_command 继续处理
    6 j% }8 j% J6 r4 f8 F( ?& `4 D! b    }
    $ a. O5 j: i! F}
    3 U5 U+ i% V0 |//继续分发: K* r1 C& n" P/ x: u% c$ i
    bool do_command(THD *thd)1 L, g4 {9 p" l4 K1 q0 @$ a$ `; t
    {
    & ~9 w; J4 ]# H; t' _- G$ ~' Z8 D    return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));0 T, P$ Q) W! ~
    }
    2 {! T/ I1 a1 C9 I8 z+ Wbool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)8 D, P! m4 j1 U; G& |
    {; _8 k& G" V5 h2 a
          switch (command) {( w6 J& z6 u! s" ]5 e* T" u
             case COM_INIT_DB: ....  break;; A6 v/ W5 O& ~9 h
             ...
    . ~1 ~4 G: b. Q8 g         case COM_QUERY:   //查询语句:  insert xxxx
    " `" H1 X0 [/ N, G             mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析
    . }$ L) I9 J% t1 S$ I, S           break;* V. Q$ ]" @8 r; ^7 j2 L
          }. a& x0 J$ T$ ^  M1 V2 U; T
    }
    : w  t# P2 ~# |) {" A' V: U//sql解析模块
    9 b9 x9 H( A, O; h/ E8 _void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)8 @# H% }4 P' b# `& E( E. X1 H
    {9 ^$ P& s( M% T4 C0 o
          error= mysql_execute_command(thd);6 C6 ~' k3 I* x* S& R. o- e7 ~
    }
    8 ^+ V  m7 X# {. Z7 H! B/ ]4 {) Q0 D  s6 D

    % J9 u2 d6 M1 O/ ~5 g<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。5 q8 T+ `$ l1 z6 i) M
    ( ~# x" @' e7 Z, s2 O- D$ o
    //继续执行
    7 F& L5 W7 D1 b8 G: G+ [, o: zint mysql_execute_command(THD *thd)
    $ y, g9 c6 X! w3 x1 z  Y{% p* [+ q4 `! G% U; A' s0 F# y# a& F
      switch (lex->sql_command)
    / X1 w8 U" ]& v9 A% y3 ]* S  {
    9 O* @, w6 B1 |      case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;2 v8 L7 a  G' a& C: b) ?

      y* Y6 X$ V/ z7 |      //这个 insert 就是我要追的
    ' E" d. r% r0 ]5 b4 c1 j      case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,  r$ Q' n, `& U
                                                  lex->update_list, lex->value_list,# h6 b  X' m9 `( J/ d7 k/ f: m( r
                                                  lex->duplicates, lex->ignore);
    ( H$ h4 a: @! \( |5 F  }
    6 @5 x+ W& [1 Y, M8 ?) K% x: B; z: ^}$ `" z# f+ C% T6 q5 v6 I, Z2 A
    //insert插入操作处理. k" _7 ~; C( D+ J6 q  p
    bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,0 K% E) n: X) L
                      List<Item> &update_fields, List<Item> &update_values,
    - i' B; B3 @2 t+ j- _# ~8 u                  enum_duplicates duplic, bool ignore)
    % v7 I& U  r0 C- E$ ?5 l' R) C% Q{/ z. x1 C4 E4 l! ~
          while ((values= its++))
    4 F8 H7 Z2 Y3 b9 ]/ M  u& i      {9 R; i, ]9 Y" L- L( b( F) m! o( V
               error= write_record(thd, table, &info, &update);
    ! t; L+ b, K/ \: y7 x  }9 v      }
    % ?! N' X. U6 L/ z; l7 P}
    6 X8 u: S! F6 }# X5 r  t9 i//写入记录7 P" w4 I; k, [: S* _) o1 g( [9 ?; v
    int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
    & r$ T6 H6 P  e{
    1 d% K  \9 u4 X1 k    if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
    8 E; y9 r: x2 ^0 Q, V1 V! _    {
    7 H* N+ ^& @# M- ]" s* P         // ha_write_row  重点是这个函数
    & c% _; A. C) a0 f& P         while ((error=table->file->ha_write_row(table->record[0])))
    7 d% y4 B' x$ S5 l         {
      r9 c, l8 E; ]* Y             ....9 J# {2 v4 {* W5 R, P$ ]4 p' m
             }, k0 i8 U% ?) y# |9 N1 i
        }
    ) w; q: b9 l# s! y/ p}( M7 v/ [! L- O$ W* e& I

    ; w' h; m( }3 ^6 k5 n4 |$ s. j6 C0 y! Y
    0 r7 d* Z/ P: A* ]
    可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
    8 k8 V" s( Y& O
    9 J9 j  K* h- k. B% ~/ q7 R<3> 继续挖 ha_write_row
    , ~% G  c% ?* p, c! I3 X. l5 ?  b5 ~
    " l1 @! W. U: E6 n. Z. pint handler::ha_write_row(uchar *buf)
    . }- a5 O+ s. L; x- l( w; w& p{. f# e2 H3 F( s; V$ w
        MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })+ ^0 x# @: N* U7 i/ T% o( f
    }
    8 p( Q1 j% X2 ]4 [6 y4 B
    7 L1 k6 n" o( t& I//这是一个虚方法
    * S7 _5 E2 p* d9 ?# X, X3 `virtual int write_row(uchar *buf __attribute__((unused)))% t8 M# p. s6 u) p& T' d4 ?1 U9 J% N
    {
    . x" j! ?, [* j    return HA_ERR_WRONG_COMMAND;0 F& w/ R! {  f2 K, I) J
    }
    - T0 g  t6 \  L& a, L
    $ \, V7 r; o7 u$ j, e6 m0 b" l: K: B/ `) K
    看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;4 `9 r- }7 J; F- b
    $ h/ e5 u# x, V; e: K
    3. 调用链图
    6 [# Z6 w+ e! v这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
    + U, C2 X6 M9 F; ~
    $ l/ I4 F. v. }% q9 Y' A1 @6 n$ N! m& n$ O+ @3 i% Z6 L, ]. r
    ! Y  v1 y2 `& C1 x' a
    三:总结
    & Q" L! L1 y1 D大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。) Y  S. Q1 ]8 w" ]6 \7 K
    ————————————————
    * h, l$ \# Q7 C! n1 m* L1 W+ r版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。& M# z: c7 j  D* i- p
    原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
    - O9 h6 N2 p, H" }) j& `+ _
    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-6-13 02:55 , Processed in 0.399863 second(s), 51 queries .

    回顶部