QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3631|回复: 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
    一:背景
    * z1 g" U9 w; q  R) n1. 讲故事
    ( Q6 R* O+ ]4 t2 c* a; a* D最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
    % @" Z  X& u% A
    0 h! {9 n% z4 c$ t$ |4 [二:了解架构图0 D. i* a& W9 Q: p6 `# g
    mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。) O7 [$ l2 r& c8 R
    3 S6 y/ n' ~2 M( V
    1. 从架构图入手. H6 ]" ^2 m& ~1 r* z
    大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。0 Z5 Z  N& f+ E/ o1 p$ D! [5 g

    # ^" x0 v2 b; t; V1 `6 e* P. m8 U
    # ?4 ^  L  C# d7 B3 o7 R
    - `8 r: I: [- X! m8 [其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~- y7 T- q1 ^' I% V

    0 W6 P( Y5 g: N* z6 s, W2. 功能点介绍
    # c& V$ ^* V  IMySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。0 y0 E  ^, G" E: o" z9 t
    2 V; v" }- _4 u& [1 m# r, ~% |
    <1> Client
    9 X! [5 Z( J' X, E- |* o0 d不同语言的sdk遵守mysql协议就可以与mysqld进行互通。: z& T7 f  k. K$ J6 G# z8 F" r

    $ \+ h7 F4 S3 f$ }4 [+ o<2> Connection/Thread Pool
    # S. }+ P; j4 j9 c; v- c! ZMySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
    0 D& d4 N7 l6 Y5 J( p1 Q2 y' J  v) d# A' J7 I. f- X
    <3> SqlInterface,Parse,Optimizer,Cache
    ; j: k4 ?2 O; u- m7 I对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。+ p" _) e0 w' N! }3 V

    - _( Y; m! D3 o<4> Storage Engines. N+ x& ]7 ?/ W* {  ]
    负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。
    % p5 J2 k/ O* J9 l  Y- A, K/ o( }4 Y
    ! F4 r$ X. y& p三: 源码分析* R- X) F3 s% F" W
    关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。5 r8 x4 L$ j3 R
    8 ]* @3 K& _$ L. e
    1. 了解mysql是如何启动监听的
    3 J! K; x2 f3 y4 p手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。$ E; t3 y" c8 e" M
    # D" h* p# A6 h' O$ {, y  K6 \

    & a4 r5 r- ~# j" m0 \/ `7 p* ?) [8 G- U* n6 B: j
    从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。: e2 m- C/ b& i) s

    / F+ p/ f7 j& [, w; q( W9 Z) b* ?<1> mysqld_main 入口函数 => sql/main.cc
    0 M! v7 x5 k  q# \# A! Y; j/ R: n* K, v$ M4 J4 z5 c$ A

    ' s5 ^* c% N- n( s( p! p. rextern int mysqld_main(int argc, char **argv);9 m( n$ A3 x8 j9 M1 b

    4 v* ?- ^: w3 Q, Oint main(int argc, char **argv)" J$ I! z( ]1 J( H/ U
    {6 {8 b! A9 P! l8 U& ^% h# Q
      return mysqld_main(argc, argv);3 A& I8 l5 ]  [& `* u7 c
    }
    ' g& O% k1 c, \
    3 z) ~7 s- }) f; j, {& ~# m
    4 {# n$ O: _* G" G8 t9 }0 A这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
      g; q  W6 F- o% H7 e% I
    + ^/ m4 y% y! u6 r+ D/ s<2> 创建监听
    ! S# ~1 ~4 s# q) i9 j: ^; }5 k5 z6 R
    4 e$ j7 ?1 @0 Y" m
    9 ^; u) i' |6 p4 w. }( D' Y+ Tint mysqld_main(int argc, char **argv)
    / v  k2 f' A  }2 `5 I+ h( n: ~{& {& p5 }1 v  y$ T8 x# N
        //创建服务监听线程9 ]4 y. R! O; m1 i- ^; K. E6 {! _
        handle_connections_sockets();1 G7 {: @+ _/ M( [3 C/ G# D
    }
    4 Q* Q3 Y5 u! ]4 g' q# }' M2 t- B( f( ^; E9 n
    void handle_connections_sockets()! S! |0 m5 R, ^) K% P) e4 Z
    {7 ]! g. d/ j" b1 _9 M% _% [
         //监听连接, P- c7 \1 r4 s0 y) H& Y: Q+ ?
         new_sock= mysql_socket_accept(key_socket_client_connection, sock,9 I  M# E9 D$ R' G- a' o+ m6 M
                                        (struct sockaddr *)(&cAddr), &length);5 z  I+ K5 X' f: g% x8 C" z
    ! t' {: {1 A3 d; H* M3 U
        if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))1 Y8 g4 ?' F( x5 i5 I
          thd->security_ctx->set_host((char*) my_localhost);+ ~5 v5 p3 @$ [- l- Z2 O( N
    8 V5 J  p8 c6 Z/ q$ I, _
        //创建连接
    6 a3 U- p8 o8 \4 V* h    create_new_thread(thd);
    - U4 q( \% w3 N/ u}
    ! ^6 K$ {; L; R$ M! r" i" ]" @" R5 }9 {( p4 y" \  w; l
    //创建新线程处理处理用户连接' K3 ]0 v7 n3 U! T# B
    static void create_new_thread(THD *thd){
    " t. j: |2 a" _6 L
    ( |( x$ S' w) t- x- [! F8 k" z   thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;' v- z& a! q* @' X
    ( p: ]* v6 F: I( T
       //线程进了线程调度器$ @3 p& l" D" d) I$ k
       MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   ) c; ^  f8 W/ i6 p6 O- g3 O
    }
    % K! S  ~) I- s& v, b: Q
    1 h$ \) {; J" ]
    + @  a  o. }; z. I至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。' `3 F* e; n! `" G1 Z

    & @9 C3 K3 T) m0 [' S) ^4 t% I5 Z5 M( B# ?5 @  F
    2. 理解mysql是如何处理sql请求
    ' |; ^. ?. \0 Z4 f' q8 h这里我以Insert操作为例稍微解剖下处理流程:
    ( Y* X7 [% c. h8 A5 E" ^% ~
    * {& p: d- s! W1 m7 |: G/ g当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。! X3 P" a( k0 @8 t0 a6 z3 b# t
    0 Y- f8 @( P  Y, w
    & p" i  R8 f$ d5 x, v, k) p$ o
    static scheduler_functions one_thread_per_connection_scheduler_functions=
    $ `" `/ H3 K0 {9 v/ d3 C& h{
    $ L, D5 H" A4 [& Q& i+ {  0,                                     // max_threads
    1 h0 u* t+ V) v4 X$ r7 d: p  NULL,                                  // init/ [9 k' z. @& Z7 _
      init_new_connection_handler_thread,    // init_new_connection_thread
    1 X$ C' Z' U6 E" s3 s  create_thread_to_handle_connection,    // add_connection
    2 l" s* v' `; H  NULL,                                  // thd_wait_begin  p2 k1 X8 y; A9 T* G+ A
      NULL,                                  // thd_wait_end. z# Z; p/ ^5 h4 a( f3 Z
      NULL,                                  // post_kill_notification0 H" [% _5 b% h; l3 x8 X' z
      one_thread_per_connection_end,         // end_thread
    - T0 X/ P6 v5 D0 m' B& h/ f  NULL,                                  // end
      E) A$ ^1 P7 |# y7 _  Z  O- e};6 p9 W) _9 L6 v& w5 m4 J

    . d: t, K% D# [# Y0 z
    5 q- G3 Z$ H& i7 }: O从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。7 ~  J/ H. J& _: x8 Z

    . a; g7 N  n+ r<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
    + H# K% Q9 D8 D: W% D1 K0 D) C4 y( V
    void create_thread_to_handle_connection(THD *thd)
    ( @; h# i  j4 d3 ^$ t) P6 i  S. y{
    4 D" d0 s2 v0 `# p) k% C- H     if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,5 J0 M7 N. J4 _9 J& s, v  x
                                         handle_one_connection,(void*) thd))){}& r: }$ X9 \! o7 l6 w
    }2 i/ I* H% X% n; N
    //触发回调函数  handle_one_connection
    " R6 \8 s5 }3 R# `+ npthread_handler_t handle_one_connection(void *arg); z# Y8 ^4 d2 u
    {( q1 ~) a! W$ l3 Y# H
         do_handle_one_connection(thd);9 D0 L6 {9 r% x* z$ I& E" ^! o
    }
    * N% X) X" O9 D" J) I8 X//继续处理! w2 K" ]1 Q( A6 k/ f% z9 t( j
    void do_handle_one_connection(THD *thd_arg){5 S& |  F* j; d' Z8 m0 r
        while (thd_is_connection_alive(thd))
    - r: R- F% n; k, k8 s/ y8 v0 `    {
    0 d9 w8 V9 }6 c" ^# n      mysql_audit_release(thd);3 Q1 |4 X4 x! K7 _. W& `
          if (do_command(thd))  break;  //这里的 do_command 继续处理
    : d/ B$ W" P0 T, s+ C    }
    : g1 _9 j! O9 Q}
    1 K! I0 o) }9 Q. a, w$ K2 u7 A" y//继续分发
    , h+ W- z* J) x9 R: a9 ?$ I  Zbool do_command(THD *thd)
    7 Y6 B; K! v0 t6 K! m7 U: I+ G{+ b8 l) e9 j! C
        return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));, v/ ]8 N- R$ @6 m  n
    }
    ! ]5 h- O) U9 @8 Z4 f1 tbool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
    ( H6 ^+ x7 N1 L" j  r& m6 u{0 L# ^1 w, [/ u; X! e
          switch (command) {) m1 `7 T: v! B* p8 A* b
             case COM_INIT_DB: ....  break;! L5 r+ }3 i# s& h/ J# X
             ...
    6 U" D/ f& {; w5 Z! X, H         case COM_QUERY:   //查询语句:  insert xxxx
    ) N3 t0 |9 N6 I2 [) [             mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析; d( C8 M" }) D( g* ~* S+ _$ G9 q
               break;5 Q, D$ ~! g- D+ Z7 D
          }
    5 Y: k& r1 ]4 V9 S4 J}1 N  F  E1 f& l8 h9 i' ?! }* }, K
    //sql解析模块+ M3 i* |7 ^7 V, G
    void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state). l* u" j& k  v' t4 C  u
    {8 ]  A6 v) {' x
          error= mysql_execute_command(thd);! U6 m% p/ n$ N
    }
    - X: _. O& c: [6 x
    $ h" ]7 I* y9 I9 P1 _* N: v# R6 |. \( u/ J& B
    <2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。0 d3 T- Z; I$ b
    4 ^) s' l; M* p0 w
    //继续执行- N1 I# e7 N8 T9 {6 b
    int mysql_execute_command(THD *thd)' x- g$ s) c4 U! n# p
    {" n. _7 F: @- g: q
      switch (lex->sql_command) 0 o+ }. i- [$ x8 M
      {
    9 w$ a! h" h# n+ q      case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;
    8 {0 Z* l. W2 C+ T+ C+ d
    ' ^+ I. r/ {, D: q; W      //这个 insert 就是我要追的
    : [$ l$ O+ d- q( }- R      case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,! \9 f/ I8 Z0 l9 D
                                                  lex->update_list, lex->value_list,
    . I. I6 p* ~; F                                              lex->duplicates, lex->ignore);3 ]8 a- d, s. D9 l& j, F
      }
    4 P. ^! P) E% n, z3 P}2 h" p8 _' ]8 e
    //insert插入操作处理
    # }7 S7 ?$ R0 z' i; t; S* }# Ybool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,& z9 \# {( w9 l* v+ M) @2 @( G" Y* _
                      List<Item> &update_fields, List<Item> &update_values, ! e" s5 [2 X7 q. o& d
                      enum_duplicates duplic, bool ignore)
    - @$ k0 a1 g9 L{
    % e6 i, V* u: j$ t      while ((values= its++))2 p3 g* h& S9 y6 O1 `: h
          {6 V  h! ]  m" s2 E) b6 J, I
               error= write_record(thd, table, &info, &update);5 ?2 S3 e; N& R8 e$ g, D/ v
          }: n4 \& p+ k# w3 \; Q/ m% T
    }' G( M+ Z  E9 C# l! z' K0 |- J; T
    //写入记录
    8 K6 `7 q: s1 V  Tint write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)" u( O, e* B1 V4 V* G" |) d
    {
    3 R& n# g9 _( O% [    if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)& y3 l9 B+ r  k
        {4 J5 [$ Y- E) m6 T5 Y4 I7 ]- z: S
             // ha_write_row  重点是这个函数
    ! [9 v7 }0 `; O2 Z  y0 E4 }         while ((error=table->file->ha_write_row(table->record[0])))% J( Q2 p( h# M2 F4 x, n
             {: c* ?; f2 T1 g$ g8 P1 K: @+ U1 ?
                 ....
    3 h1 Q# V1 \9 D. H$ H         }
    9 v# b! L+ T) H    }* i3 t" t) L) w9 \
    }, Y) Q: b0 G% R/ u% e9 Y

    8 V0 a5 ^6 h/ ]  c" l* s2 ?8 c7 A. E

    * u: }# w# j& {' C/ J$ O可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。; q6 Y8 X- ~! b  r

    " E% z* k" q/ b; U- i/ j9 }5 e1 E  I<3> 继续挖 ha_write_row% a- \/ b) j6 T7 s; P

    0 l9 F: \8 u) w$ S5 u' y9 xint handler::ha_write_row(uchar *buf)9 [# F. x0 R8 C+ A4 k) v% \+ J/ I
    {; e  z- ^# N& S" b' ?, Z
        MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })2 o7 ?6 F( r9 e0 e7 F) u% f" v
    }2 T$ R$ {; [6 Y: ~3 P
    2 J7 V" \! v7 y
    //这是一个虚方法. V3 [! x' s  g" M
    virtual int write_row(uchar *buf __attribute__((unused)))2 g& M, @. l  q
    {
    - C3 X" P( C' r7 j- w1 k$ |    return HA_ERR_WRONG_COMMAND;
    # g. i6 {! P" v" t( K  ~}
    # s# i, c: U. @( A" l) Z( T
    4 c4 Y* @/ z. h8 r" d7 F/ N# K( M0 F: p3 Z5 b
    看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;( V& n4 x6 h5 _: j. Q

    ( C: [+ H# N& c, l4 L* i3 ~" Q! R. x3. 调用链图
    ! z5 F- S0 J3 s0 d- T! q9 ?这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。( W% g2 |$ K: Q$ ], u

    ' t6 t3 N/ v9 `4 ^5 S
    9 c. v( X0 L$ m  f5 a; O
    5 O3 l/ c! L- U+ o6 y1 U, x. H; C三:总结
    * q  N! e7 c. \0 d' l  b5 M大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。
    9 f: k: N. V! P1 U————————————————$ I4 ~; ^! [' A8 R! p9 K2 k9 A
    版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。' T9 M" B( v9 S+ J5 z2 \. }! @
    原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415# m% Q; v: Z! ~/ Y
    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 20:57 , Processed in 0.442833 second(s), 52 queries .

    回顶部