QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3628|回复: 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 I9 E4 W+ _! j2 H
    1. 讲故事- f. E0 d% R0 U# |
    最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
    0 p  N5 P1 _! B7 ~  p! |9 C) n$ x8 d- O: E% N( `5 X- Y
    二:了解架构图
    8 ~  o% o: P: J7 W1 ?mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
    1 Z, |1 b3 \, Q! ?( y& b- {4 u3 {2 e( M) k: g, G/ M% x+ y  {
    1. 从架构图入手! a' D/ c5 [" @2 V" F/ g4 {0 X
    大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。* k- ~5 H7 }' _4 ^  I

    6 z+ r" A# \" `) l3 W! H  X$ k4 I, o2 i( H% `4 a4 W5 g; [$ J. }
    ! B' F3 V1 k( F5 D6 G' |
    其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~+ L! J, n. d! J& }7 q# p7 x$ _  s
    ( n* a" v2 z5 D; j( C1 \
    2. 功能点介绍) c7 A3 l' f& h
    MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。7 ]( K: x* z, k, ?) x
    ) G0 i+ K' z) Q- x4 u" W0 @
    <1> Client
    . q) A, v( L1 e0 a+ |; P不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
    $ L! s  Q3 M) v( h
    ' c8 L+ m- Z( F# B+ A<2> Connection/Thread Pool2 Z# u9 p2 ^3 g, \8 b5 Y- f8 ~1 X
    MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。. e3 Y% M) a9 ^% `4 S. k6 V
    1 p7 s7 \4 y- i- V1 C4 m) {
    <3> SqlInterface,Parse,Optimizer,Cache3 q- d# ~& E* B6 i
    对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
    # K4 O1 Q2 F8 B8 g: ~+ D! Q5 A* a" @2 {+ c
    <4> Storage Engines$ u" w& l$ ~/ \5 t. W
    负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。) P1 P6 D; d7 U8 y3 |, w( `

    " _& L6 _! {! f三: 源码分析
    1 G. f8 {3 Y. B1 v7 r0 `关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
    ! q/ {7 U- ^, l: M9 a
    2 s, w" m( G& e1. 了解mysql是如何启动监听的* v1 I- e- |- ~% w% U
    手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。: `6 v; M2 |* c  \; E. l- B

    6 _" ]. M! E$ |- Q# W9 w5 {  H+ H3 b- T3 ^# D5 o5 G* a

    7 R1 q8 h( M& G0 m- O! x. ~从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
    1 y  t3 q; b5 k7 I$ S, P$ f* q0 p' o0 @; r5 @& y+ Z& ]
    <1> mysqld_main 入口函数 => sql/main.cc
    3 e0 n5 v) c: w+ O+ X
    ' T1 E5 B, E% R. \1 C9 l
    6 \$ K: Q" v& k4 I* k5 T4 D' Mextern int mysqld_main(int argc, char **argv);
    ; O4 E: U( [/ ^4 V
    / I" v9 n  a% Q8 t2 a' Vint main(int argc, char **argv)' l9 @4 S" S% N# u' w
    {
    $ U3 D- g0 S  {' g8 W5 Q  return mysqld_main(argc, argv);
    " g+ F# b. M* l4 G# X}
    . [" c7 v7 C7 z3 F# |* m, _: z1 }7 u
    1 t( f8 s  S6 o- `/ W; H0 p8 t( R/ ^3 g, T$ p6 W
    这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
    6 O. T# ~; B* M/ g" `( f
    : G* R( O% ?. W. N2 e6 ^<2> 创建监听  i5 Y9 R1 |3 B3 e

    & p* i' v; ]2 _1 s4 ^& Q6 \2 {$ n8 Z6 K
    int mysqld_main(int argc, char **argv)- ?9 l: Y# E8 O  P7 w
    {8 s: }. v  ]4 O1 Z3 w' q
        //创建服务监听线程
    2 T2 p, A5 D6 Y$ T6 D4 n" ]5 F. Q' }    handle_connections_sockets();! r" G& L( P7 k8 e- g( t! `
    }. m% e! N8 ^6 W- n  ^

    # z+ p2 |% p: j( t3 Svoid handle_connections_sockets()
    : k2 c) N2 D7 D' @{
    + Q. D: v5 f8 x) b( k     //监听连接
    / @: h  f! }0 R: a     new_sock= mysql_socket_accept(key_socket_client_connection, sock,6 @! y+ w3 J; N
                                        (struct sockaddr *)(&cAddr), &length);
    5 _; P, W( h& z5 Y3 D' |* K! _
    - Z6 @4 ?' e( z    if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
    1 n5 `$ S* A0 ~& \& ^' w, Z" D      thd->security_ctx->set_host((char*) my_localhost);& X4 q: F1 }2 _9 P  |

    4 E4 c3 A7 R7 W2 [4 O$ X3 m    //创建连接
    5 c3 ^/ t# v8 D2 R  O    create_new_thread(thd);
    % \% e/ N3 V% W0 T: W}6 L3 O0 K$ n/ \: s
    6 _  p* i! P9 G# u: u7 U& S/ s
    //创建新线程处理处理用户连接
    ) @$ z! ]" ?  |0 O( @! K' J* i1 S* astatic void create_new_thread(THD *thd){
    6 d+ q" m) [4 i& G, x! m) M  s3 t1 b2 r0 F% K
       thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
    * C* E- Z" X4 u& A0 I  L1 m
    5 G) O0 c' Y  S; z+ S7 y4 h( C, h   //线程进了线程调度器
    2 ]% B0 s9 m1 a( m   MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   4 x. ?" f' a) q* G* c$ \
    }! K, }' q' l7 _% k* k- D' u" Y1 X

    : k- q$ W4 P. y! Y5 k6 ?3 [
    / Z# \# j$ M; ]  K4 ~# r至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。3 P* I2 T. x& s# O

    - S+ e" X+ F/ R; D* c
    0 E$ i8 v3 n5 ]2. 理解mysql是如何处理sql请求& g* R+ O& I7 C+ a, P9 q* ]- O
    这里我以Insert操作为例稍微解剖下处理流程:
    % A, @9 ^6 K5 [( }& S/ ]
    ; w: K* \2 r' v0 H当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。/ a. D/ K, ?/ e
    ! I% w8 i. j: n3 s6 ^0 S; ?

    & ]: `& o5 s) c7 A% [static scheduler_functions one_thread_per_connection_scheduler_functions=# p% |: H5 m" P
    {
    % R5 c) ?9 n, i) a  0,                                     // max_threads
    8 j* |8 r9 W* w# I; n7 x5 U  NULL,                                  // init
      a4 q3 U  w) K8 \  init_new_connection_handler_thread,    // init_new_connection_thread
    8 D4 O# i- Q# J0 n/ `, Q  create_thread_to_handle_connection,    // add_connection
    8 \6 b) b* q- V0 P( Q) i  {. A( W  NULL,                                  // thd_wait_begin
    6 w+ n4 l# r6 D3 ]  NULL,                                  // thd_wait_end
    % {6 Z7 I/ e3 R1 w; n+ D. u0 G& f- U  NULL,                                  // post_kill_notification, y' ^( z' m6 C9 ~; ~. w
      one_thread_per_connection_end,         // end_thread
    1 G5 S) n2 D* D! ], r  NULL,                                  // end7 ^- w; D- o/ D, R/ q9 O
    };+ K& i1 l0 d8 h1 f4 z/ C2 x7 T! C

    3 L6 R2 v' v) F- s3 h$ h! N( H
    & k8 f# r" @. D从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
    ' |; F; C0 Q: V- F* Y: B, B# S& ]$ ^( K6 r: }5 }, R. W
    <1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪" d6 ?/ P8 R# P, Z
    + l0 i1 b# I6 M, G5 Z
    void create_thread_to_handle_connection(THD *thd)" ^* V( C2 ~+ i6 @2 p
    {/ x% g  T; U( E* l- P5 A
         if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,5 u1 w$ D/ B( T) u: R! D
                                         handle_one_connection,(void*) thd))){}6 p! l8 X! {+ W; _" l5 _
    }
    " `2 H7 }8 n( i//触发回调函数  handle_one_connection
      N; f2 N2 q+ r& b- Kpthread_handler_t handle_one_connection(void *arg)
    % v% W2 I/ l5 m! C, A8 b1 i  n7 A{
    / Y+ u, r; G1 H% M1 T     do_handle_one_connection(thd);6 Q; r1 \) B2 |  j# b. R3 G; S
    }/ u6 o! i/ L9 l
    //继续处理
    & e; F% R1 c$ evoid do_handle_one_connection(THD *thd_arg){: _  M  D4 U+ j( v9 U
        while (thd_is_connection_alive(thd))/ ^9 }7 h+ F9 Z+ }' C( j$ Z
        {
    / N: o8 w/ }- Q      mysql_audit_release(thd);9 t0 m3 X, U! [$ L" m
          if (do_command(thd))  break;  //这里的 do_command 继续处理
    9 r8 o) q2 h# l3 J4 S    }+ P1 E3 L* \% j! O' x
    }0 c( r% v1 A' @9 _8 o+ m) ]
    //继续分发; a/ L( U' K7 t9 D/ U5 t0 r2 z
    bool do_command(THD *thd)5 q/ i3 m# B$ g) R) G% ~3 D5 f" z
    {
    ; V" A$ e8 X0 u& p" U1 z    return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));' L/ P, c8 Z  ~4 W
    }; `% \0 h& v+ b
    bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)( A, T( J7 a& H/ N' A, j0 {
    {3 O2 B" i+ v' {" L
          switch (command) {  q1 `) f7 x/ s+ E
             case COM_INIT_DB: ....  break;
    / c/ l0 p8 t. p& }         ...3 |. v; V2 a1 v) _1 v& [0 ?
             case COM_QUERY:   //查询语句:  insert xxxx
    + O) I! Q; |9 K9 I4 I7 D% @: |5 \             mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析
    : I% d$ I" |$ [) [           break;; ~/ X- ~3 w, `+ v
          }% D1 T5 ~2 @. n% e- L2 |% O7 H" q
    }$ C4 n: Q, N) o
    //sql解析模块
    ; v" a9 V+ v! n/ R" F' v: ovoid mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state), Y9 X7 M  W4 Q7 V/ X9 Z7 d, }
    {+ g: w) E* b* Z
          error= mysql_execute_command(thd);
    3 Y! p% L  Y8 Q* d* M1 _; O( l}. t& S* d- C5 a* O" C4 w

    + E% M% Q; X1 a% u
    - s( }" I* g5 y+ T) }; ^<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
    3 \* E$ r5 _% W5 O$ ?! ~) q& ^( X0 {" g3 W! E
    //继续执行
    ' C$ K3 t6 G# ]5 zint mysql_execute_command(THD *thd); _, z! x2 S  T/ _. ^3 u% o4 E) [9 K
    {
    & {7 }, c( l8 L& o& [  switch (lex->sql_command)
    1 s, ]% ?; u/ l( d) s: W  {
    / L6 R  I7 v9 |+ s8 J$ @      case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;6 r+ D" {0 e" [% {. Q

    ! G3 R$ \( A8 }0 C      //这个 insert 就是我要追的4 [- B6 ?9 C% p3 V( o% _
          case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
    ' P" f$ m7 ]& V  m                                              lex->update_list, lex->value_list,
    ) n6 |) b! ?8 C# q: |                                              lex->duplicates, lex->ignore);
    : w# R) l7 A" O  }
    / B4 X: A/ ?6 d. g( F! N' K4 u" c. D; I}
    ( b& s; S, a) k: W//insert插入操作处理- W. ^9 t) k8 Q( ?* R- `3 P4 u) O
    bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
    * q6 U' ~4 y0 h7 w                  List<Item> &update_fields, List<Item> &update_values, , O! h' {- m" F6 d' D: z( B8 T1 |
                      enum_duplicates duplic, bool ignore)
    % Q% \$ o, p8 L( _- q- u{
    : p: H$ s% M  L7 l% E' r      while ((values= its++))
    9 ?$ ]; ?/ p( F1 }      {
    " U/ |1 H$ o, G/ S           error= write_record(thd, table, &info, &update);* v& y7 H5 ?3 T2 i2 [7 D
          }
    7 J+ S5 j: n% Z3 o7 r1 N4 I' a}4 G1 g  Q8 M$ I, z; {2 d& Y
    //写入记录
    4 h% c, ]6 x. V4 i' sint write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
    $ L$ e6 Y  o2 \{) K0 P. A$ ?$ H. @+ D! H# c
        if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)' Y& @8 }) S% _; M1 e8 \
        {
    6 W- g5 s2 V9 E' i4 u         // ha_write_row  重点是这个函数  p; [- C' s9 {! I7 b
             while ((error=table->file->ha_write_row(table->record[0])))1 s4 y" N4 w) C( h  Z
             {
    " t1 }5 c7 w  X5 `; C5 \* \  E' H             ....
    : W/ r1 [' q/ g( J. K$ F         }) P5 k0 R" R: A7 ~% J9 j
        }
    . ^+ _7 X, d+ h! S}' D/ }5 n# l$ I4 Z

    2 I$ k. D# h* d3 ~, ?" D5 i* P
    3 r/ d" L& m* Y2 H$ ]/ t( S6 i/ S# i' M2 L9 K8 H8 N5 z! C
    可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
    ; c4 [( z; L9 a- y9 G7 i3 y7 \# d9 o) z  ]4 p  J' C  J
    <3> 继续挖 ha_write_row
    % J8 o# }$ A+ c) e: c: U1 w8 A( C: ?
    int handler::ha_write_row(uchar *buf)
    ! I/ @2 o. J! {" i( k{
    , H! q3 H( O) N) z3 t8 |$ h    MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })( l3 p+ Y( r. z. z
    }
    3 `$ y, \( v% E/ V: {
    ' i! ~3 F2 t" B" X* @//这是一个虚方法( o! p$ C/ \$ D; r( d
    virtual int write_row(uchar *buf __attribute__((unused)))7 R( M. j0 A* h; S& O
    {  w# B, T5 x: F& i9 Q; o
        return HA_ERR_WRONG_COMMAND;$ P0 c/ I$ k- b- h
    }# Y4 q, ~) t2 G5 k

    ) F% y3 g3 ^8 g) j6 F. u" B5 @2 k- \! n+ W# A) S& F9 V$ v
    看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;
    2 M  @, c) h( r3 b& k. D' z1 u' o9 F- x% ]+ W7 |7 S7 i/ X4 U( r  I
    3. 调用链图5 t- p/ y5 ?* ^( a
    这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。! e$ Q: c% g' D" `$ `' K  Z( M

    ( W* G5 t. K  e- q6 Q- h8 U8 d7 O* s3 B! \

    7 O' G; ?6 Z& F  x( c8 {: n三:总结+ |7 T# R  j# H$ U, @* v
    大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。. ?  v5 D7 E! o8 E0 s9 {
    ————————————————
    ) b0 D8 l4 z3 J* y" m& o版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    0 p" K' k& m5 ^; _9 t' U原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
    ) ]( k$ w) R# [) Z, V. x6 F
    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 16:49 , Processed in 0.474419 second(s), 51 queries .

    回顶部