QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3537|回复: 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 a: o2 W' m+ J8 T6 g1. 讲故事. w  w5 d& m( E7 l6 Y
    最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
    ( ]1 l* Z7 E/ D
    , E5 a' A5 c) l; b! ]二:了解架构图
    4 }9 M4 P- e" K0 l3 M' E( imysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。" n/ ^& X; k8 a1 t3 h. u. j5 D5 a

    / v  ~. x/ J7 p# u' o1. 从架构图入手8 K! s2 G" c' ~# I$ ^9 z: B: d
    大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
    % d# U; C  n  H8 D) u
    8 T) S4 |' {2 I% J1 Q2 u* r0 o. ]
    8 r2 Q- {+ ~6 X& q/ J5 O
    其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
    5 D6 A2 c+ E! Y6 Y6 Y& g" p8 ~2 N2 s
    2. 功能点介绍
    2 o6 l8 Z2 ~! B: S8 bMySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。% I3 U% U  Y& x7 j
    : z8 A4 w. `* ?: a2 Q; ?" D1 l
    <1> Client. f+ Q+ q0 i2 D( d) f0 g- ^
    不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
    : {; R& j" Q. Q' R
      P9 n, \1 X  E+ ~. t5 r/ N<2> Connection/Thread Pool; b% K( M5 z/ z- d- j
    MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。5 N' U) g: t% ?

    & [3 Y5 x7 h" f<3> SqlInterface,Parse,Optimizer,Cache
    " X% j& N2 n4 R8 ~  Q# G" m对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。( M- [1 n2 C: w) a8 n; i1 T4 n  {5 R* Q) j

    ) A7 h/ B1 J: |4 K0 k' \( B<4> Storage Engines
    0 w, g4 @0 M+ R& U0 F& R/ x5 F: E负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。
    - s7 k' S! H- q+ [' R1 a0 v6 o& e8 w5 r' M" W" X
    三: 源码分析; \3 i' y$ F6 ~" e9 t2 |6 j( A
    关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。5 H% k: f% x* }! a8 ?) T
    2 s: `4 {+ p1 S, B/ V, C  j
    1. 了解mysql是如何启动监听的. t7 ^  ~( p( j$ A& m
    手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
    ( i6 t/ D" d, g! n& x* F+ J; x$ |* g* `( o2 z. _! b7 J
    0 g; c4 ?1 {- o8 ~% m. y

    % f' v0 A4 k4 I1 [, o% Q/ p! N从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。/ ?7 u) _6 W5 j

    0 n# u7 v8 k0 m6 h2 {; v1 L' P<1> mysqld_main 入口函数 => sql/main.cc
    # q4 Q7 n4 ?+ r' u9 b  U( e/ P1 w! K" ~% m4 n
    ( c. h! C& ?. m! |# y4 w
    extern int mysqld_main(int argc, char **argv);% m6 O& J  A2 b5 m  F& X6 z" r

    5 `  \  a4 i1 L0 c; A/ ?3 uint main(int argc, char **argv)
      r5 k! J! ]/ K{6 Z8 S8 [) U( ]5 Z) ?' I0 J
      return mysqld_main(argc, argv);
    , _7 l: W; h0 H3 g- c& |& _}6 n! U* b: }, j' f/ V, M4 n& @
    ! A+ O; k. z9 a4 z2 g" G2 L& M% G& Z# f
    , t( Y  E9 i* a4 c( Z3 B  r
    这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。$ n$ m/ [: D* b0 V  E0 B4 g

    ( x. A$ o  k- u<2> 创建监听4 \* l& l6 o. C! _1 ]" X

    : y! t  o5 o8 _, q3 m! T+ ~
    ' H# O; j) B9 c! w" A! h" f- i6 @/ zint mysqld_main(int argc, char **argv)1 b  c/ l4 w9 l* e9 `& o
    {
    : n$ I* h' C; K+ ]: l7 F    //创建服务监听线程4 M6 _2 m/ d4 ?
        handle_connections_sockets();
    1 I6 ]5 [7 W6 E}% p/ ]: s. c5 i' E+ n  r# }8 m

    $ G$ [# ^; s: R7 y( ?void handle_connections_sockets()
    + ^% Q1 P0 r6 q/ @/ \3 H{7 ?5 n  G- p7 {+ Z
         //监听连接8 \! p3 U  v1 h* T! e; @
         new_sock= mysql_socket_accept(key_socket_client_connection, sock,) k( B5 K4 f! v
                                        (struct sockaddr *)(&cAddr), &length);
    9 G+ B6 J8 V+ |4 y/ v/ F* D$ J2 E( y" J: N1 x
        if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
    # Z/ `+ }& g6 w& h      thd->security_ctx->set_host((char*) my_localhost);2 J+ g# a! Q' x3 V/ ?" G
      c( F: q: N2 Y/ [- j0 c. g
        //创建连接
    % j0 }) b  F/ H1 G    create_new_thread(thd);
    . D* t) {. K& l0 o- W4 \% S, W}
    9 ~: V5 f' F: U' J8 w5 s" U% ]! E% ^
    //创建新线程处理处理用户连接0 H+ H3 Q' F/ ~6 c1 F/ X4 x
    static void create_new_thread(THD *thd){6 H* j5 z$ n. |. b2 f7 n
    0 R1 y! X, F% ^- u3 i, v: V( O3 \
       thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
    + c+ e$ \+ U1 ^- H3 X" ?" Q/ U
    ; P3 C! K/ ]* ]8 ~# ]% }8 y7 k   //线程进了线程调度器6 @& F) v0 B- f. c! ]) K* P' _
       MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   
    - O& J; _( R! q8 m}, K8 k4 L6 f( Q- U* S( g
    ; ?' q4 k: ?  \4 {# S; [

    . A1 P: n2 t! V6 X: s至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
    / L, h. x  D* B5 R# I5 I" |
    1 ~6 [& M  g- ^% G
    1 D+ |) y/ p% |# r2. 理解mysql是如何处理sql请求
    + f$ r5 l+ b9 ], v这里我以Insert操作为例稍微解剖下处理流程:
    4 i3 a' @  p9 Z. i6 R6 p! M0 K! ^
    6 ^8 j6 X4 ]1 L0 n) z; s# x( z/ h当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。; K2 m7 I* v! q9 B9 `* o; \/ D

    5 U5 [9 _9 Y1 q9 Q- m1 R' w: C6 s1 T, g1 M# p8 G
    static scheduler_functions one_thread_per_connection_scheduler_functions=6 n' r0 {. [! M; ^; F
    {
    - p" ]  e1 C4 ?" o7 f  0,                                     // max_threads
    ) {% C6 v, B! o) C* F# H9 ]2 u" y  NULL,                                  // init$ f) P7 S/ z9 K0 v
      init_new_connection_handler_thread,    // init_new_connection_thread
    3 v# Y  \( y3 @1 ^- w3 z  create_thread_to_handle_connection,    // add_connection, [6 `: b  c# n) S
      NULL,                                  // thd_wait_begin$ O+ q8 n& W3 y
      NULL,                                  // thd_wait_end
      j( p8 b* E4 B; S1 A  NULL,                                  // post_kill_notification
    ' K1 p. L) |) ~  one_thread_per_connection_end,         // end_thread. f! X7 t3 T! |+ t2 C
      NULL,                                  // end
      m! F5 _  R1 O# R, G: }6 R};  e% ?! u" V8 q4 T- }" v5 v
    , h" a/ ]/ l- n6 J9 ?6 ]* ^# m

    1 F6 N$ ^' N4 D/ j" |" w从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
    ' v( B5 O1 E. i& `/ K1 G+ G
    ( V9 |, }- a0 O; Q# O; Y* S3 E  B<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪5 L0 E& f1 Z/ ~8 ^- i
    , Q5 d. W) i% |; u' K
    void create_thread_to_handle_connection(THD *thd)# s7 i4 u( a" @# l: Q5 K
    {* A8 n6 @# ~. Q, y5 Y% D9 d& c, b! N
         if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
    0 T1 E/ }) Y  Z# I) B                                     handle_one_connection,(void*) thd))){}
    ! ^5 V+ t$ ]9 \( A, m- {8 d* a) K}
    , \6 j% q# J8 D//触发回调函数  handle_one_connection8 ~8 B2 Y8 f- D
    pthread_handler_t handle_one_connection(void *arg)  M% |, z3 S' ]
    {
    9 {" V2 ]1 b% ~. H3 \  o# W7 ]     do_handle_one_connection(thd);% n( t( _% \, [- C
    }3 t( [+ p  `$ I$ p0 X( `
    //继续处理
    7 N6 p5 R) o" Q9 ~; P" Zvoid do_handle_one_connection(THD *thd_arg){7 `  X/ d$ v, I1 x- R" ^! ]& n
        while (thd_is_connection_alive(thd))/ i% |9 A# j. @' M: h0 k0 j( a
        {
    / d; Z2 R+ V8 |& B      mysql_audit_release(thd);8 [2 \; d. v: r% z  B* F
          if (do_command(thd))  break;  //这里的 do_command 继续处理0 Q7 i: g# l) @, @( O2 m, G" @
        }
    ! X+ e" ?3 a6 v0 i( Z& v( D}
    2 b- k+ q. Z. a0 b//继续分发
    7 a( w1 `' f) n6 @% B8 L8 abool do_command(THD *thd)5 o7 F- l3 F# k
    {" A  d- X! }& Y1 C; i# f9 }
        return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));) a- @' e4 g; z( U
    }
      q! ^* r, h( p( ^1 O$ J1 bbool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
    1 \/ J  [+ h+ e5 m0 M{
    , E) o1 M% \3 g- q! Q9 N      switch (command) {/ r! K. `" s& K8 p" Q) x
             case COM_INIT_DB: ....  break;
    8 k; V; I; U0 k         ...% g9 Q' V) [' @
             case COM_QUERY:   //查询语句:  insert xxxx5 n1 E, V9 V1 g- n: t' ^& S
                 mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析
    5 J1 ?' [% o) e6 A           break;
    & `5 v$ E5 O" G% ~      }
    3 f" @# V- N) m4 S5 |0 }}
    ! U& W4 C7 ~6 _//sql解析模块$ ], v" Q; E1 K6 y( @* f
    void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state). k$ _4 a# i$ J# U7 G
    {1 G, F5 ?1 H2 V2 Y$ Q9 ?" Y+ j
          error= mysql_execute_command(thd);
    7 K; ^: T: {: h+ G}6 @# |( b+ p1 g+ q& u. v; {: X
    4 n$ V# I8 i( c1 K

    9 R; H+ \' }" Y/ q' b+ x<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
    , Y  x6 n  N# V! q* A' {# M6 ?) e& H" b5 r3 o; O: A
    //继续执行% D/ u. }% p" ?8 x
    int mysql_execute_command(THD *thd)
    3 |& R) D5 U" r- L9 h- T{
    " x1 m+ f) l: R- G$ B  switch (lex->sql_command)
    : q' |9 N% j; @4 Z* j  O$ R% ]* j  {3 b* H1 p  S! J
          case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;7 r; a: ^5 x; @- t" e& T6 X
    / [3 H5 Q) @9 I! B
          //这个 insert 就是我要追的
    + a$ B; J$ F7 u" H& H% r. _      case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
      P; p9 k" ~9 l                                              lex->update_list, lex->value_list,
    " }8 x3 q3 A" Z( B9 V; y                                              lex->duplicates, lex->ignore);8 Z4 q& W( ^1 W5 v$ G, [
      }4 t6 m$ z& S# k# E1 S0 w
    }% D6 ~. o0 M3 ^
    //insert插入操作处理
    / V7 ?( _+ w! @bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
    . r# c7 @$ ~2 |: D" I6 S                  List<Item> &update_fields, List<Item> &update_values, # `  L; j1 a: t
                      enum_duplicates duplic, bool ignore)3 n# ]3 n% y6 _) E
    {
    5 G4 i# H" a) ^1 Z# x      while ((values= its++))
    8 Y) s, U4 @* a* P( ]" D      {
    ' ?5 c. }* b$ W% L7 Z  W2 r& k           error= write_record(thd, table, &info, &update);
    ; A- h. y+ s$ s1 v$ T( V! b      }
    9 F7 U* t4 j, Q4 X) M3 r}
    8 o5 L, `7 w. {- g7 q# ?1 {//写入记录0 t: t* j' F+ Y' _( N
    int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)2 t* j# x2 t2 U+ X4 t; I1 k
    {
    ' R8 x- k4 r( V0 Y3 X/ x. y    if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
    " W8 L, y) K- N    {7 \- s+ o/ S* N: `0 ~: Q
             // ha_write_row  重点是这个函数
    . _3 e+ H* q4 e9 P3 {& O         while ((error=table->file->ha_write_row(table->record[0])))
    + H2 j6 Y9 R  r' n- y; d7 @         {% K% N) R1 D3 P. G
                 ..../ G" |) b+ ~3 s
             }
    0 W9 M; J+ l2 a6 C/ m    }, \  i. E& F; d5 o3 H5 T+ B. H8 k
    }
    . d" ^8 |6 q5 i  y/ f' L1 L) W  T$ w  P7 C) P. k8 @, [

    7 d% G: e. a! x' S/ _
    ( |6 ]$ n  N) |可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。2 B1 x3 `" a4 Z" Y6 h5 U0 J! j7 L
    9 }4 W* ~6 r$ \9 r2 l
    <3> 继续挖 ha_write_row
    7 f: F% \) f+ y1 L
    % S4 J  q' ~$ kint handler::ha_write_row(uchar *buf)
    7 N/ d6 b- I8 n  B: s{+ w+ d9 E3 v% |) `# R, M
        MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })! N( C: l& ?3 _5 S
    }+ Y0 B2 t, N7 X' Z8 D# Z
    2 y% X" U3 H4 l! ^! q
    //这是一个虚方法
    + R3 N0 A; d) i1 b; Y3 }9 A5 i: C3 Tvirtual int write_row(uchar *buf __attribute__((unused)))" k* r' Z0 C; w! k
    {
    & A' }/ W, {1 p. U# [3 T    return HA_ERR_WRONG_COMMAND;
    0 ~7 G- c, W" W4 Y}" l# M. p/ f" m

      g' |. [% U( [/ p0 t/ o/ ?
    $ d; D+ {% S2 a  \# v  J# Q. L看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;
    " U9 o" q. Z# }  c; C0 E3 _, n+ }+ f2 Y2 T* K( V7 b. m+ X+ F
    3. 调用链图
    9 A* F" J% b* }  N/ k# B这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。; ]- b( T$ o) R5 \
    $ x& Q5 W! o& O% ^1 l% Y+ z' A/ l
    / c. r; u5 t+ ?8 y6 T9 U0 r- v

    : H# W" F9 ~' c! B3 H三:总结
    * @" q4 V) l5 j3 \- x( o( W大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。  R. t4 k2 w% k8 E1 x* }
    ————————————————' u$ D! Z6 r! A  S0 w  `5 Q) Y+ x( n
    版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    5 S7 |& G( j* S$ L% \原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
    " T; |! |7 ^5 \8 \/ c. m! d) D4 n
    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-22 15:04 , Processed in 0.425429 second(s), 50 queries .

    回顶部