QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3527|回复: 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
    一:背景5 E0 }) B4 U- J0 M
    1. 讲故事
    : w5 R4 V5 q9 T  Z: E, U最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。" h; u6 {/ f5 A, J
    6 z7 r* W8 P5 `' @% o
    二:了解架构图
    2 D- P4 M/ ~2 s! Wmysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。6 G4 V  K! ?* b( V0 `9 w: C8 X
    $ g* \; U% Y4 I. B& O7 A9 k/ H
    1. 从架构图入手
    ) u5 z2 A* @( U/ U5 W大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
    3 C! B, u& K  U; B" V" z1 h* n5 T" J$ o( c- M
    ( X$ N2 S4 ^: n4 U& A& `

    . i4 e" b; n$ C4 Q. M' d1 X其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
    8 B8 I4 A' i/ Y7 G& l- Q# w* U
    . _1 b' q. x5 u2. 功能点介绍
    0 [9 T3 Z' A% W/ xMySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
    8 @( S  _5 B) n3 _: u) r# c% ~$ ^0 D, [) g, z
    <1> Client# N+ y0 q7 z( S/ G
    不同语言的sdk遵守mysql协议就可以与mysqld进行互通。- D0 i3 l- K0 j2 z# ^' C3 D+ W, }- t
    ; C+ h" B; X6 }% W" Y- Z  m
    <2> Connection/Thread Pool
    2 ]8 w9 E; v0 z7 Q; f9 nMySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
    ' T8 N5 ]+ I, b, J" n
    1 h7 Z7 Z5 W2 _; ?<3> SqlInterface,Parse,Optimizer,Cache
    1 F* p  X( L/ I- x2 k对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
    % p( C! s' B% o% \4 \
    ) E2 P* j: S' e& y! x& d+ |: l<4> Storage Engines
    6 t$ i( [' Y+ y6 q$ ]" i+ ]负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。
    4 c1 S+ q; M3 ^; _1 s1 v7 J, Y( L- {
    三: 源码分析. T, j+ [* [4 j1 r0 _( O" S
    关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
    * X5 ?7 `% j; d5 s9 q/ z0 P( L0 w2 [9 c4 B6 T/ `) T# D
    1. 了解mysql是如何启动监听的
    * `! V) o, j5 F手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
    4 |7 B1 t( t& @/ [# r/ _+ J3 W6 e' n8 N5 I4 }6 o8 `

    # O- m9 F: |, `7 P9 l' O2 }. ]# F: H$ I! f$ l9 g2 O4 l6 l& Z
    从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
    2 r9 X' C, t) w4 U: Q1 a- d; h2 Y3 k  z" |) e/ P8 ?' w
    <1> mysqld_main 入口函数 => sql/main.cc
    " h* K/ w6 I7 h% L' Y- _0 A5 L3 i1 u0 [' w( n
    ( C: O; g. U! Y% G; Y) ~
    extern int mysqld_main(int argc, char **argv);
    ; N( q6 Z% _+ B6 ?4 `* E, t
      h% c; ^$ r, O& Hint main(int argc, char **argv)% c6 N8 k; Z1 e$ ~8 j  B) \1 E" `
    {4 A- \7 ~6 b( s: c& `& T0 l
      return mysqld_main(argc, argv);
    ( v. k' e$ r' E  D3 l/ s) Y}
    5 r# ?+ z* G2 Y5 g% d/ ?" D9 b0 k; a9 ?/ v2 q* n7 V( M' G' @) S

    : c8 \2 g! x3 H* H2 {这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。' i8 w) r7 I6 L6 z! g6 _; D

    2 ?' }$ j! B$ M7 y. X+ l1 {0 {* e<2> 创建监听
    3 A  {3 f, q: X- a
    6 h6 x& q2 z, U# V0 K! ?; `
      [4 v8 y0 h6 g7 bint mysqld_main(int argc, char **argv)) u: \0 K2 s# N* L/ y* `* {* ]8 @
    {
    . b: W. A/ k2 }, ~    //创建服务监听线程' c+ E" K2 z$ z7 d3 _! l
        handle_connections_sockets();
    6 K; P0 T. k, m/ |; ~8 c}
    ) z9 z3 b$ h. c& {' P, M$ b4 Q4 G6 [0 h& W0 P9 Z7 D
    void handle_connections_sockets()- H1 B* n5 d  e4 A( [% r
    {
    2 [" V' Y! `! P     //监听连接5 T) M* f( z0 C$ d- ~" X
         new_sock= mysql_socket_accept(key_socket_client_connection, sock,4 k# O8 z6 p4 ?
                                        (struct sockaddr *)(&cAddr), &length);  n. |4 R8 G% b0 T" X' ]5 z
    4 a! I* }; @+ u" D/ Z1 l1 G
        if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
    , |" |- F! K( s      thd->security_ctx->set_host((char*) my_localhost);; M8 |( v; N: _  `3 `* F. `
    ; g: B, K7 t* B, q7 A" p
        //创建连接0 a2 i" q9 @; l4 D# I
        create_new_thread(thd);
    1 C6 X( x* s% V7 `}
    8 n; e, Y# v0 y' x6 n) D1 U
    ; H9 z8 A% F  m1 R. _- F. F/ K//创建新线程处理处理用户连接
    " m! M4 N7 M! Wstatic void create_new_thread(THD *thd){" m# `3 @! y. i, T8 V+ |4 w: M2 ]

    6 m! G9 c0 H/ n/ t9 h: a7 f   thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
    $ j/ W, r4 g  V# f1 S# B* g$ C+ L8 w! J8 x/ @9 h, f3 w7 W
       //线程进了线程调度器& o7 l* t0 Z: |4 s( C
       MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));     }, S4 F6 {7 b( @" D6 Q5 S
    }
    4 D" T; R3 O% A+ a/ B# D' u3 J6 g* J
    : s1 g3 [9 n# b- W6 G
    至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。1 C" W; T+ h1 Q1 ?' r0 s

    & c: q. k" H0 T" t/ G
    ; @5 J3 i* H2 z) V  U! W0 c2. 理解mysql是如何处理sql请求
    8 U+ G, M/ c- a. E& k+ Q0 W这里我以Insert操作为例稍微解剖下处理流程:. z" k- I3 q1 w! u" F0 y4 i* `

    7 e5 j. q5 b& D# o! y  K- A当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
      B- W, X# v- T" r7 i2 i6 |4 b
      u4 d( a0 C9 K; z
    , A* ?( k9 O" T& l  ~1 n1 H+ hstatic scheduler_functions one_thread_per_connection_scheduler_functions=1 o2 R4 k: n+ _9 _) {/ i
    {
    : f7 R; u+ Z$ M4 T1 g  0,                                     // max_threads, _# Q" J: F+ E0 T+ x  v
      NULL,                                  // init" Z' H- P: ]! z  u5 F
      init_new_connection_handler_thread,    // init_new_connection_thread
    : f7 V. W; I, y* h9 J  create_thread_to_handle_connection,    // add_connection
    9 U" h% F) ?9 ~, [9 \3 Y7 a  NULL,                                  // thd_wait_begin9 @+ S( [6 N% k2 z* k- H2 {
      NULL,                                  // thd_wait_end
    1 C1 S5 m$ h6 Z6 E- R  NULL,                                  // post_kill_notification
    1 S- u8 A! J" }( S1 i2 M; y5 C# r  one_thread_per_connection_end,         // end_thread
    8 q9 n1 @& U  ]  NULL,                                  // end) \# u5 }! b5 U" c" j" X7 h& F
    };7 P9 J, T) k. C7 w9 X. @

    ( }% I4 G  B6 @7 T! g  c! D/ n
    % L9 ~, x' |% |从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
    0 ^. A# g0 K+ P% }) g9 m! D# V0 k) F* x/ y: ?% |4 A
    <1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪6 f/ {5 R0 P' P6 t! B+ ], [
    + d( e, ?1 i  M, t& p) b
    void create_thread_to_handle_connection(THD *thd)
    : \6 I# p. q; E7 C$ ~2 Z{0 I/ n& \$ f& q! b" t
         if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
    ) `8 e, F; \- s                                     handle_one_connection,(void*) thd))){}
    1 U/ k( a" N) \5 U' x  ^5 [}
    8 I* E6 M3 l' ~% w//触发回调函数  handle_one_connection% U* o) h: J. S& G5 F  i
    pthread_handler_t handle_one_connection(void *arg)
    + V; \9 i. e+ N' h- L0 k% X+ y' v% h{
    + G" m* I$ S0 c4 \" g0 a) E     do_handle_one_connection(thd);
    / O) [5 h! p; O: o9 ^* F$ u}
    / ?. U: d; ?) x0 m  o" H//继续处理! u$ B& q" ?  }( B
    void do_handle_one_connection(THD *thd_arg){
    + v* ~1 ?& K9 }# a% O. Q    while (thd_is_connection_alive(thd))
    ) D; o( R2 Q# @8 v7 l' t2 c    {4 u# E1 N" ]/ P' g
          mysql_audit_release(thd);- `1 I& d" X+ ^& B, K
          if (do_command(thd))  break;  //这里的 do_command 继续处理! g+ q* }! E8 q0 d# R6 S9 r
        }
    ( B: _# j! {6 T}
    % X1 n% B) t3 T( f# {//继续分发
    ' ?( K/ v) |  f6 O/ p# `# Rbool do_command(THD *thd)0 s, v4 U5 J" u; c. z  ~
    {, h9 I5 o6 h6 u1 T# y7 |# L3 r
        return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
    2 s1 Z5 `" t) Q0 J. |6 E7 P( x}
    . w; o2 T& U& o+ ?4 x7 ]* m! a, Gbool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length). ]2 y1 y- v2 D& C3 E0 @! o7 |' {
    {
    # q+ r$ m' Q  D. V      switch (command) {( a0 n9 q4 z2 P: q1 D
             case COM_INIT_DB: ....  break;  l3 L1 z- o# R
             ...
    3 A  R% i* |2 c# A! x         case COM_QUERY:   //查询语句:  insert xxxx
    " h' n+ G7 M5 k0 U  J9 R             mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析2 k1 p$ P- a( T' u
               break;
    1 F3 G4 j$ R4 V3 G  T) E8 r      }1 L, p8 m' O+ Z& _, W
    }# I3 Y: O1 |8 z5 t: t
    //sql解析模块# Z+ L. Q7 F3 p, n2 \) y
    void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)+ Z0 l5 S. ]) g2 e5 U
    {$ @% {& x+ E* j1 m7 ]
          error= mysql_execute_command(thd);
    1 |  o( B9 w8 y# M3 @}
    + U' n0 |5 O' w: v/ X( o' y; t! M" M
    8 e! e3 T! W  l+ K7 K& P0 q5 P
    <2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
    * x' a' L$ I+ ~5 f9 u! r) G8 S8 p) I% a3 }1 V, i
    //继续执行% c: x1 [+ x2 ^; H3 E. z' |
    int mysql_execute_command(THD *thd)
    " R: i" i1 B$ L+ H8 |3 y{
    " t9 V- u; e) `  switch (lex->sql_command) & d0 v* Q( _. {
      {; V1 R9 U5 ~$ o; x/ I5 w) N$ X
          case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;6 D; S! y, a# E+ J
    3 L6 V7 k/ F0 Z% `+ y
          //这个 insert 就是我要追的
    5 R5 z7 J3 S) [' K* m0 N) B! X      case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,2 H( C- {4 x. V4 }" L
                                                  lex->update_list, lex->value_list,7 W/ o9 ~/ J8 k7 q
                                                  lex->duplicates, lex->ignore);
    9 A! y( ^9 ^8 T8 Y* x4 s( J  }' W* F1 o) ]. ^3 X- k
    }/ x& {% Z. }! w- {7 A! v# C: K: X
    //insert插入操作处理( w7 l: a& P% H1 q
    bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,' P! B& d8 k1 a! e6 E5 D( V
                      List<Item> &update_fields, List<Item> &update_values, 8 h$ X1 |& y, Q/ w6 \
                      enum_duplicates duplic, bool ignore)) M0 R6 \! Q6 M/ }
    {
    : w4 _+ b" P# T+ a# F# x      while ((values= its++))/ }6 v" W5 a6 w- y  n
          {
    8 x, g+ V6 Z: s* |           error= write_record(thd, table, &info, &update);
    ; ?9 s6 C3 {; a+ a6 X4 G. L      }/ M, b% G# l' h8 g' W
    }' c! y0 G, Q3 m; X! m# Y8 f4 h
    //写入记录
    0 f8 K! l: t# `! ]3 d1 _int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)( X; ^' d9 m) h" f- i$ X
    {
    3 X0 A# _3 Q- J  A; f    if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)9 h% Z. s* V4 S0 K; F" U/ r+ c( L
        {6 _/ m. @( F) ~; V; u+ {
             // ha_write_row  重点是这个函数
    5 c: d/ W7 V3 H& l0 f: ~! P         while ((error=table->file->ha_write_row(table->record[0])))( O' y: U' p4 I
             {' X. T! q) ]4 C* E' x& F8 o
                 ....5 |: t& _+ j" w8 t3 q) l: o9 _
             }
    # c5 E% ?& E4 i5 k: e    }& _' {3 N6 N1 b- b) t9 p  Q
    }
    8 c3 _* @$ E  g4 s0 q% X( T. ~
    4 e8 c" W; U4 F- q0 Z3 h
    0 P( z% x. C$ a8 o( q0 w1 g2 x% @) {" [( ^
    可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。5 |1 J- x  \9 p- V5 j

    9 S5 Z5 v$ T6 U- w<3> 继续挖 ha_write_row
    9 f0 G9 ~1 h1 I5 D
    9 `) v7 Z( q( h3 T: E" |int handler::ha_write_row(uchar *buf)
    0 m" r: T1 D  n: j. X, b/ U8 Z* s* V{
    6 |9 F  u1 w$ I/ L1 ]    MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
    2 L' Y  p0 u& d7 y- n  g  y}
    ! {% B+ f  |, G# O/ e% H' A4 r( c  f" `1 D8 _5 j! o% j
    //这是一个虚方法
    6 k' R& f5 L0 {+ n5 Tvirtual int write_row(uchar *buf __attribute__((unused)))+ R0 I- G9 z  ]4 Z" i
    {) |7 U$ i$ W  J& y: y9 ^  y
        return HA_ERR_WRONG_COMMAND;- _* E' m# ~' Z& r0 |
    }* E. k6 E: @: y. o
    3 f6 K  \( Q9 [4 s) K: X- O
    7 |% E/ b2 C3 w; z& b* \3 v
    看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;
    ' O* u' o2 ^! p# H2 I3 x! O% o% a9 P& v" k6 H$ Z1 g$ W9 I! ^0 z
    3. 调用链图- U) M* R$ N8 x: ^: [
    这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
    8 V' e9 X8 B$ U# }5 D
      L# g' B5 ~- p& i+ v9 w
    3 `. j% d$ N$ y4 Y- X- B
    1 a7 t8 y% Z8 e; A9 X, H三:总结& B- P8 m& n% T
    大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。
    - n) w3 W0 e9 K1 D————————————————4 _, n' P& q/ ~& F) P% q
    版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    ) }8 y4 n5 y0 z  g原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
    9 T$ A# I9 b2 I$ S7 G
    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-12 20:28 , Processed in 0.395751 second(s), 50 queries .

    回顶部