QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3632|回复: 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
    一:背景1 `2 N7 `3 R! x2 M6 L; i
    1. 讲故事
    : g' I: X* n7 O" H9 t最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
    - V. V! u  A8 b) I# L' m  t: e" Y, u: _) J5 N" {0 A  }2 N
    二:了解架构图8 L+ R& e4 Y" A- U+ K
    mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
    7 U0 J* p8 [) }# J( M
    % x! c8 k) ~1 D( {; ]) o1. 从架构图入手3 Y( q: L2 e8 \5 M/ b3 U: ?: j/ n
    大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。9 O! A, E) S# Y# f- c4 {$ z2 U

    1 t- f$ e- \0 f3 h6 p- {8 {1 \1 V, M2 \3 k

    0 T$ j0 H" R' W+ v4 d其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
    - h3 r5 Y1 {- U
    " ~9 k( {9 u$ v. V/ c! g# u2. 功能点介绍: b$ ?; ]2 r0 v) T+ j" p4 o! S5 J
    MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。8 W1 x6 S- J+ U- g0 V4 N
    - r" u, o) X% o
    <1> Client
    % m. @& b3 P9 D. J' Z4 c不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
    8 X/ q+ m: I" J/ v/ t4 v7 P( p0 U3 J6 e) |/ {
    <2> Connection/Thread Pool- O2 o2 l) p* p' F! ~
    MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。2 m' D+ \* ^. u
    5 G6 Y4 k' u2 N# D6 X( [
    <3> SqlInterface,Parse,Optimizer,Cache
    0 B, j- F3 A) D6 P! j对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。$ t+ y/ F0 ]" H  h) j" N( w

    % A2 }$ {8 e, N<4> Storage Engines
    5 ~, S/ @' C3 c7 ^; e9 \$ e' U0 Q负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。; N  k( b; `8 t5 O
    7 h( O  X. Q9 Y0 m5 L) A7 j
    三: 源码分析; v( j) @7 _6 N4 f# E% n, |+ o; m( |
    关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。9 _; \9 \- q2 X' L1 N

    " S% v$ ^8 K) W  a1. 了解mysql是如何启动监听的( t4 l8 r2 N! F: g0 p
    手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
    - W2 f- n) G/ O$ t+ d: ?6 n; J
    5 [: z- r0 t8 y) [. _. ?# X2 N4 p' W4 o; k) r3 W+ n
    4 y8 E# A8 ?* B; f
    从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。# M0 v8 w# w$ f6 ^$ ^0 p

    6 |. E! H2 w$ T+ z& ]<1> mysqld_main 入口函数 => sql/main.cc
    ( _6 B* ^; m* N+ x5 Z% {0 F& R  i0 M+ \

    / |# O5 y- F9 k4 yextern int mysqld_main(int argc, char **argv);
    ) p- J$ a0 W8 L
    7 o# S% B5 W% T+ i3 L8 d( Dint main(int argc, char **argv)
    8 o' r* l5 y. I: w3 q{7 [5 f2 R5 Z) q* I% ?3 h5 t
      return mysqld_main(argc, argv);
    4 G6 K3 b9 s, T4 E2 R' X0 _}1 f: _6 d: w! k; W5 q

    " T) |! Y! X+ Q9 X$ x0 D" ^3 J, _3 Z* x% q# p( x+ a; C3 |) {" z
    这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
    . r5 W" y6 _8 [- r% B( }+ r
    & u# i5 E+ u+ d- r+ v* i5 t5 E<2> 创建监听
    2 ~! h6 t$ X+ [& I" h5 D3 c* r
    $ f  Z# r5 q  Q# w7 m, ?5 X
    / V! I* r. [" D( r, V3 w! V9 iint mysqld_main(int argc, char **argv)
    : @6 j6 ?$ [) B" B. W{' ]8 X4 E8 C' w4 s6 l3 a
        //创建服务监听线程
    ! q( q0 g  C9 f    handle_connections_sockets();* P$ A0 h/ o; P1 G% T2 a0 M
    }/ C* |0 N7 a% ]0 _. N8 S

      g" ]1 R/ e/ x( }/ ^# G; f( Cvoid handle_connections_sockets()
    " E  \6 a- j! ]- ?( w, p4 F: W0 W{
    # h4 V$ O  I" i# s1 V& \7 W     //监听连接; }9 p7 J+ {+ v9 E1 {% @
         new_sock= mysql_socket_accept(key_socket_client_connection, sock,
    7 ~1 S7 e# H- y- B/ U- c5 C8 ]% W                                    (struct sockaddr *)(&cAddr), &length);
    9 ^1 C& N7 s1 \& T& p% N$ ^, H
    , s0 J0 x# Q  E4 l8 r. s    if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
    ( C. X; h* K4 Q/ x- _) H8 ?      thd->security_ctx->set_host((char*) my_localhost);) U) i+ F* I: D4 b2 u
    & H$ G, h# a! g/ o0 J1 z7 I( D, v
        //创建连接
    9 C3 j: n- h  p    create_new_thread(thd);
    1 z3 y6 H& N) G. ~% ?' S}
    5 ]3 |% o. w; B$ \, c5 M: Z# @  n  P8 n0 L: i
    //创建新线程处理处理用户连接
    # T4 S( @" A- ~# j; {static void create_new_thread(THD *thd){
    ( H, o' \6 o! x( w: g2 M6 N3 y( N$ @: z* ?
       thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
    9 l3 o3 z( N8 Y) e& w8 R# y1 h" [' J/ v# q2 V* q, \( K/ k6 u
       //线程进了线程调度器+ q6 b! r' C0 Y5 k
       MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   
    6 m2 d# @4 w; S( K) i8 E}; A1 b0 M) z+ q8 k! ^/ e: }7 `

    0 ^: s4 O5 J  d6 O5 j; z- o  \& _  F" f( j
    至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
    3 ?. P5 i% q, u& i# m: U+ P
    $ ]; I3 {% r; I' }
    & z: u, F, M2 Y2. 理解mysql是如何处理sql请求
    ; V1 \: v8 S" y) `( ^6 h6 |: @: ~这里我以Insert操作为例稍微解剖下处理流程:
    ' C; b9 S) |; g5 e
    ! J) R* |$ Q. z. |. y' T/ @当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
    7 p8 y, l9 c* ~$ |- b/ ~" w4 E% X: H  O+ D0 z

    2 c2 x- }( N# G% }# k' ?3 vstatic scheduler_functions one_thread_per_connection_scheduler_functions=: O6 m1 H+ I" J* p$ i4 w$ d* m8 o' t
    {; ]) ~) X/ N( [4 a
      0,                                     // max_threads
    / U5 a2 F; _% X) N  NULL,                                  // init3 k# n6 Z. j- K6 u! m4 \! A" g
      init_new_connection_handler_thread,    // init_new_connection_thread
    & x: Q! Y- I3 P/ h) o5 |  create_thread_to_handle_connection,    // add_connection
    3 d7 A: v8 ?& z* t  NULL,                                  // thd_wait_begin1 D  S1 j  i3 t
      NULL,                                  // thd_wait_end
    % n. f, r, y" u5 p1 C6 ]( a  NULL,                                  // post_kill_notification
    ( J+ x2 q9 t; \9 ~" h# M  one_thread_per_connection_end,         // end_thread
    & g; C( A7 ~' `( \1 }6 @" c  NULL,                                  // end  D; [  |6 i' o2 K# }" \( [
    };  k, [  {8 ]  t" V0 W

    % Y( k0 B' l  k" p# r( _  _. L- Q9 }6 {0 D3 H+ M
    从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
    0 l# [  h5 o( a& @$ h- X7 j$ }" q. U- a; i! `! R9 W! S9 H
    <1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪6 Y9 E$ e! ?8 o; ?9 o0 n4 j" x

    9 _# d9 N3 ]/ |$ tvoid create_thread_to_handle_connection(THD *thd)
    2 H  ~2 P1 x  c9 N" Q1 K* r5 Z8 ^8 q{
    + A3 a$ E& N5 k     if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,* T! V9 g0 G: S: }# g' y2 q( _. C7 n
                                         handle_one_connection,(void*) thd))){}) R7 Q3 O4 m. w, N
    }
    - i' [8 z1 b, r6 z//触发回调函数  handle_one_connection
    / E: N6 K0 J6 t% vpthread_handler_t handle_one_connection(void *arg)
    , _# i5 T6 B6 Q: j{" H) F3 o5 ]& A4 L8 O+ E& a. _
         do_handle_one_connection(thd);
    / p, y( ]8 K7 G6 z' i% }. R( R$ n}
    $ b7 R, |" C7 p) ^- ]//继续处理9 U4 |* _; ~: ?& n7 r
    void do_handle_one_connection(THD *thd_arg){7 v% J# ?/ O; q5 |8 S3 a* g
        while (thd_is_connection_alive(thd))
    ' K! q7 ^: \8 g' l2 G    {
    ) ^  T, `! ]; t6 X8 N& H" i. |; L      mysql_audit_release(thd);
    ! E  }/ p- ]; {, m6 A      if (do_command(thd))  break;  //这里的 do_command 继续处理/ E  P" x8 s1 [" i  ]; j5 c' Q
        }' F6 Y0 q$ b* Z$ }. m; D9 Y
    }
    ' g+ Z* i# u3 V( l* h//继续分发
    - v+ Z+ K! Y7 w# H, ]; ubool do_command(THD *thd)
    0 l" r7 q$ \8 F# [{
    0 q2 y# T+ R1 K5 B; K7 }8 a$ v    return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
    9 r# I& a& B. ?+ Z7 K2 u% D+ [  S7 ?}. V% m6 f7 E) ^9 |# O% O" z! [
    bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)# p# S1 K' e$ V' |$ o
    {
    0 t7 [6 K3 m  b0 A      switch (command) {% @: _" `  l& u8 ]' |" J
             case COM_INIT_DB: ....  break;7 f/ b9 P0 C( p4 I
             ...
    , i* u- a+ _  p, G) ~% E" w7 l' `         case COM_QUERY:   //查询语句:  insert xxxx- ]6 b% L0 i' C2 s. T
                 mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析  h: V" n  E/ o* S
               break;& i8 ?5 }6 P! O3 F* S5 C: `4 Y2 P% g
          }/ x/ c. w9 J2 p, t
    }# Q6 u8 J7 a) `7 W# n( y
    //sql解析模块
    5 {8 C  M+ {  S6 {8 c6 F. C7 ?6 svoid mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)- c4 i, m8 N- {3 Q+ u
    {
    # C* K5 U- k5 E6 N; W- t1 [      error= mysql_execute_command(thd);/ D" P; f% Q/ g" f* x
    }7 P4 t% t: r: K4 }+ j
    % A4 ]8 h" z' T) j

    8 U) e2 Q* R( a7 w# i<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
    7 S& F: i/ `1 a, S; p% \& O6 v1 w  G3 ], r1 a) d# H
    //继续执行
      _9 Y1 ?1 R) F( F  U' @int mysql_execute_command(THD *thd)
    + q/ V. A3 q- i{7 {* t" m7 O0 [4 j; p2 T
      switch (lex->sql_command)
    $ q5 Y/ s& Z  _: D5 Z( E6 L  {
    9 y( g  ^) W( V8 u  x5 r      case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;# R) W# D( D( X  c, H
    6 c* r/ Z2 ?9 V% `# p! X
          //这个 insert 就是我要追的
    1 o4 I3 A: w. ^! U9 l4 I0 K      case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,  E! U( c" T$ N& P9 `9 ]. |% S* ^6 k
                                                  lex->update_list, lex->value_list,
    & X$ d: D6 p- a6 D( A                                              lex->duplicates, lex->ignore);
    % _  V0 o/ L8 i- O2 r  }
    , ?, l" a$ Y; Q9 u}4 D5 _& V: D( G) D, }% W
    //insert插入操作处理
    2 d6 P# w' v- V/ L4 V, S8 _- Xbool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,, ^* [  |1 r& ]% d9 u
                      List<Item> &update_fields, List<Item> &update_values, * s; P2 V# P- f$ \  \- c) R
                      enum_duplicates duplic, bool ignore)  y8 S7 x( I% m) K, D# b) s
    {
    + \  k0 Q% f0 q8 k2 U6 x      while ((values= its++))" m3 j6 y3 r6 \" z3 {
          {9 w2 r# N! \/ F! l" I# x* o
               error= write_record(thd, table, &info, &update);' f4 I8 ?+ ?4 ]1 x
          }
    3 F; d4 p9 ~3 \2 I! e0 l, G/ m4 v( L}' R6 y  `7 Y' f9 ]  |& t( U/ I
    //写入记录. y! f, X2 D7 `: z) _6 S+ `
    int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
    + ^( k# |8 t$ _: ]/ X{
    ( k( {0 K9 \& z    if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
    ( B4 }. O" `# c* }3 Y    {2 t5 W4 u# o4 c
             // ha_write_row  重点是这个函数9 P7 k$ c8 u8 {) [) ^, t# n. i0 P
             while ((error=table->file->ha_write_row(table->record[0])))
    7 m) t  ?9 z7 M8 ]; G0 o0 v         {
    - G* d. \( D) A* ^; Q6 P             ....
    ( H7 J+ H4 J/ g% M) I, X+ r0 ~         }
    3 |+ [' i2 }5 b( \4 u, V# H4 p    }
    & [' l9 r7 }7 e5 Q}
    5 |! g, O! \" h. q* m0 {8 ?2 F1 Q! L6 U2 ?6 L! u

    2 ?; \9 v: W# o$ j
    " l: J  {* z; m$ D! [8 t可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。; e8 N# [5 R3 p7 j: g! V% a

    3 A5 d' D9 P. Z- t4 F<3> 继续挖 ha_write_row9 ~" L  \7 h/ a3 `6 h% ^

    * R- {1 i% `1 t: C% R# \int handler::ha_write_row(uchar *buf)' |$ h/ R/ a; z
    {
    ! c# [6 U7 a0 M$ O; v: N    MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })8 y# K7 u: q9 J5 Z% {
    }0 R- A$ s, n9 Y6 {! y

    2 h, p% ~5 d# D# P% w- K//这是一个虚方法. j! ]0 C  _. i* y% Y$ ~% y
    virtual int write_row(uchar *buf __attribute__((unused)))' t, ~* c  L1 ]  i5 M
    {
    # ^+ G0 U- q" M    return HA_ERR_WRONG_COMMAND;) y' U4 K8 y$ T% y
    }
    8 ~7 R$ n+ v- Q( f/ p, K" B6 \
    . y  F% \+ ?, W: a  Y  M6 F4 }6 a# v/ ]" z  r
    看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;5 j$ z( P' |! K/ O
    9 A% W' H6 t9 J
    3. 调用链图
    1 q9 W- g! A) [" W$ u' M+ X这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。" k+ y* B4 k+ l+ D" N7 _  D

    3 e) e2 i: U" w4 V# n& U! ~7 Q

    , P0 b9 B8 h( e7 T三:总结1 R. M! B* E& {& @7 F! Z3 L0 J
    大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。; l" X" t) s$ `) f4 S2 _
    ————————————————
    / ^/ F) B* ~1 I+ D3 S/ A5 f版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。# o# J) F7 F0 m4 i2 c
    原文链接:https://blog.csdn.net/huangxinchen520/article/details/1064874152 n# @: H. c1 K- L. v
    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-9-24 21:10 , Processed in 0.332207 second(s), 50 queries .

    回顶部