QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3707|回复: 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
    一:背景0 }7 D$ y  A- }; y% X% w  e
    1. 讲故事
    3 g9 I8 d( h/ D% r3 c最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。' l0 U2 `3 ^$ u

    . h, h( Y4 W; Y  C9 `) j  O二:了解架构图
    " |0 X* Z" ^9 Imysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
    # v! _, p" q+ \- s
    9 n7 g$ {* P, q1 x' E1. 从架构图入手
    1 X9 A9 |5 u8 l& N+ X, r大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
    0 S! |. X5 D) Y; P  ?" p+ T: t4 O5 K" Y

    1 I# S' V9 v, ^! T1 c+ i/ w) Z8 U; M9 V$ ]" t
    其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~& G; f* g/ u* C6 Y" c+ l8 ^; ~
    9 C4 W! l$ `+ C# o0 I/ _  `
    2. 功能点介绍4 v1 H% Q* @2 ^* M; z& G
    MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。% n9 B1 A1 r$ o
    ' ?" o2 F: c7 o7 r
    <1> Client
    " i' L  P( Q6 M8 T9 D# w不同语言的sdk遵守mysql协议就可以与mysqld进行互通。0 X! E; S& X9 a; X

    / S9 A$ W' o. p9 B. M5 I3 X<2> Connection/Thread Pool
    8 `% o! |2 y8 j) `MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。( _  E' z4 g& \1 y. K

    % l8 b1 f# K. ]<3> SqlInterface,Parse,Optimizer,Cache2 P" R/ S$ Y+ L
    对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
    ' B: h  {& e2 ?; `
    * A& @; e  v9 M1 }- H2 [<4> Storage Engines( j2 d; F5 Y& F4 H5 s3 q
    负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。/ t2 |2 g: z$ Z' [# ]0 n7 N# W
    3 v4 \/ K3 W# S% E3 }' u0 q
    三: 源码分析' q" `6 y+ K. L; T6 t
    关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。, V: m. W! L" Q) D; \+ b) S/ P0 c
    - z9 H: u: P- U8 v; a1 [0 S9 D
    1. 了解mysql是如何启动监听的$ d' c4 `& p2 i/ R
    手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。: y. F/ E4 ]3 q' G2 T% Q& ?* B  v7 }
    ! E! O" x# z  Y# u' R2 u4 D: b
    ; R0 }& P$ {# |5 k1 @4 w

    + O: @6 \. ?5 g从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。9 t( v4 a4 L  t, d( K
    2 @7 q# V* x' }4 o- v& f
    <1> mysqld_main 入口函数 => sql/main.cc
    + L( r3 e- |$ p
    , }8 {! Q* K+ I8 R
    % _( t7 I. e3 s9 U" B) E2 Pextern int mysqld_main(int argc, char **argv);0 D. [5 T/ n+ q% R2 d% F
    ' J0 a, p% G/ s
    int main(int argc, char **argv)  [7 e$ T! D, t( [9 f+ L8 U
    {8 B& b8 {5 [, A3 M. K" R
      return mysqld_main(argc, argv);
    . \" ~! u, P% o( }; O9 [$ m}. e6 K6 Z; O) _5 M8 _
    1 E1 F) [  G0 B" i# I

    4 ~# m' `" s: k这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
    0 t+ a) P2 a6 t+ R( `: [- q1 q6 N" C3 r' H. @' B6 \
    <2> 创建监听
    + D. T) h" X/ v* P) g
    6 T+ T. k' h+ b2 U
    2 o: G$ Y6 b/ X& Y9 q, v4 G# eint mysqld_main(int argc, char **argv)
    - f! \8 F( w8 Z0 T{& o# `  k% W, o/ j% J: U% d  ~$ ?5 t
        //创建服务监听线程6 v% k, a# R0 \0 v9 A
        handle_connections_sockets();
    2 N+ h8 S) n& Z- u6 P5 q}
    5 P& b* L$ T/ e+ j( W0 i  x# U' y* y. @  \" v! q9 L( Q; \
    void handle_connections_sockets()( ?4 X1 H+ V) k6 c  S; n
    {8 x' t5 F1 |4 q/ w3 z2 {- |6 S: I
         //监听连接
    + @3 K4 N/ |  [" X* P: s5 L) q. F     new_sock= mysql_socket_accept(key_socket_client_connection, sock,1 N/ }& Y3 L6 u; V! I, U4 G+ {' Z
                                        (struct sockaddr *)(&cAddr), &length);- Q% y- D6 @3 t& q& t5 ?

    $ r6 [7 r1 V1 P    if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
    / _3 d6 L5 r9 Y9 |8 T      thd->security_ctx->set_host((char*) my_localhost);
      p! Y/ w* A9 W$ s$ B; j' Y, k2 u* z* U+ ^
        //创建连接
    8 A0 f- q, s7 ~& Y' B    create_new_thread(thd);
    % t% ?" M6 h  B}$ p; K! J9 y9 t
    0 ~, [5 K: _1 I6 i) c/ L
    //创建新线程处理处理用户连接
    & |6 R6 e5 r) h; V8 bstatic void create_new_thread(THD *thd){
    0 Q/ R& O* K6 h+ `% g
    ' Z0 L( a4 y3 W* d. k   thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;; d3 G4 Y6 I0 T& W# U8 D2 @

    0 {7 P! o, r% n! o: y   //线程进了线程调度器
    ! V* S: D4 {8 F( }5 b* U   MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   
    9 S, O' ?6 Z6 Z% u! N4 |}; ~& m% E) I1 t

    6 t9 _7 L0 {8 g" x4 b& {) b3 g7 Z
    1 S/ j/ o% |2 Y1 }0 T至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
    # V2 `) `+ h8 k7 v& E: H6 z6 V9 P3 H' r" }) W6 J, _7 @: m* O

    2 A1 r% P7 l$ Y3 l9 l7 d. w2 F2. 理解mysql是如何处理sql请求
    2 {1 v" u0 Y- K7 H: i! F* m这里我以Insert操作为例稍微解剖下处理流程:, S3 l" E. p% `3 t, R; f" S! m
    - M& ^& z8 B5 M- x/ v9 @; W/ R
    当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
    . s& P' H& z+ n- W  p7 H
    1 S6 R6 Z1 `" D& _6 r5 H  b# K* r2 X) F$ J# H* H+ R. a8 `
    static scheduler_functions one_thread_per_connection_scheduler_functions=
    - f1 g; a2 o8 O. V{1 s+ _/ y9 o. Y5 }. f9 y
      0,                                     // max_threads+ d' \) l+ s0 T. t8 M* B/ r6 F& D# M
      NULL,                                  // init
    ' d- z* f% C( ^9 X  init_new_connection_handler_thread,    // init_new_connection_thread
    : ]- e$ j5 |* W* d0 e$ W0 ?+ C  create_thread_to_handle_connection,    // add_connection
    % A- i! A- _! l4 H  NULL,                                  // thd_wait_begin7 {" c! T1 N5 k: t% N
      NULL,                                  // thd_wait_end
    % x- i! y2 T4 Q* Z' P  a  NULL,                                  // post_kill_notification3 C. F; S  G% X8 \; b4 Y' O
      one_thread_per_connection_end,         // end_thread
    " b. ?$ x" R- Z2 a4 ^  NULL,                                  // end
    - }, H8 b! Y! \; z2 [4 V};
    ! C4 q; ?# A. c/ T6 N% e! K5 s" w" G0 ]% R; o. o7 A

      }) i! u8 Q% M$ G从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。5 X- x% h; E& k8 R8 @8 P

    1 p9 |8 f# x2 W+ l<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪# s7 B5 l9 z+ Y; S- L$ c- e& a
    & i; M* g) A( [) |! f2 @' x  O
    void create_thread_to_handle_connection(THD *thd)
    ' y! G1 D: U0 X$ D- ^{
    9 J2 J/ x! r# m6 |     if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
    5 s: p# Q& ]' z( v+ V% Z                                     handle_one_connection,(void*) thd))){}
    " Y$ T2 b% E- H+ U" R4 a, y; ?4 T}6 H1 E& e; D7 Q+ G
    //触发回调函数  handle_one_connection
    4 H# R/ L; [! q& e/ f: J9 ?pthread_handler_t handle_one_connection(void *arg)3 l4 k+ b& |8 E7 u5 L% G9 A/ I% O
    {! t% E! i1 j1 q/ d: v* a+ H# T
         do_handle_one_connection(thd);
    $ i. s2 W6 L: A# Z* h}8 S& |; g5 q6 Z8 O: k
    //继续处理/ Q* S# ~, l. m: D7 ], n, x; b
    void do_handle_one_connection(THD *thd_arg){% H+ M. M- V% u$ `1 o. g2 K; p
        while (thd_is_connection_alive(thd))
    - Q2 l! m' G( @+ U& F: @& G2 |    {1 |, A! p8 r/ l, J. t
          mysql_audit_release(thd);3 b% g! t- h* x& Y; G. |. w! C7 G8 o
          if (do_command(thd))  break;  //这里的 do_command 继续处理
      |9 U1 L4 b7 |    }
    % B  O3 c8 Z3 o, ?+ X3 D}" U6 x1 Y1 Y/ Z- u1 a# N
    //继续分发5 t4 c* a" U0 F" k
    bool do_command(THD *thd)
    ( {! ~) j- @* i) ?0 \{
    ' l( r$ L* a. R' \    return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));3 j9 x8 B  `! w5 O2 C( H/ e! @0 ^
    }2 T0 [: g! X" E" L" G/ T, p5 ^
    bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length): {& p% d% A) f% Z
    {
    . \4 ~1 ~, V5 L/ L' `, o      switch (command) {( c8 Y" a/ b- P
             case COM_INIT_DB: ....  break;: u8 U4 O% }! k: e
             ...* \* s+ t. f7 U8 I
             case COM_QUERY:   //查询语句:  insert xxxx& y7 ~! k, H7 `8 y$ j! x  ?* b; p
                 mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析" d2 s0 @' C) O# n' m$ |
               break;
    6 T- j" @  ^; J      }
    $ l) m' p" B, @+ ^0 O}' w6 A% l& t3 |
    //sql解析模块
    5 {& m! N2 D3 p# m/ ~. cvoid mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)# g; j% R1 c. W6 r" K4 m
    {
    3 Z( b6 k% p3 `; z      error= mysql_execute_command(thd);
    : Y% v& F& ?, {# c9 q1 n}
    3 D/ m/ U( ^$ A7 U, r, X  `- h4 m

    $ r# h* Z( n8 F3 w2 Z6 O<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。( e3 m* V$ R3 x/ S8 m# H
    1 V# W7 k0 }! s$ U8 q' T, h
    //继续执行0 U- d7 s& Z; S$ ~" A9 ]
    int mysql_execute_command(THD *thd)
    ; R  X; c0 F; @0 T$ q2 i{
    " [8 S1 r6 N- T# T, E; h  A  switch (lex->sql_command) / E/ |1 d- F1 P
      {4 O1 j9 S, r  P/ z' O
          case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;. ~. S$ |) B( w- \* p  B

    9 y) ~+ T/ |. J) g: P      //这个 insert 就是我要追的' i& J4 M9 k+ x1 D% v9 y* }
          case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,' ]) U5 G# X/ @4 S; F
                                                  lex->update_list, lex->value_list,, w( s) N- e, ?  N
                                                  lex->duplicates, lex->ignore);# v' w  U. s$ O1 B6 c
      }) |: w# T0 C* ]! N
    }7 L* P, v/ O( X/ `5 X+ _3 y# ^) n
    //insert插入操作处理
    ; z1 o) a* `- B8 `bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
    ' Q* a: w6 Y7 c8 }                  List<Item> &update_fields, List<Item> &update_values, % a9 B  w, k! z+ ~; j5 u' O
                      enum_duplicates duplic, bool ignore)
    6 p( `0 G2 ?* w. t) w{$ B: G, m$ M: ?
          while ((values= its++))
    3 i$ G# ~* ~4 Y" v5 V; O      {2 f! ?% M2 U" m* T
               error= write_record(thd, table, &info, &update);# G7 k+ h- @& d
          }0 f# H  `7 X$ \0 @/ a. j, H$ {) G+ p
    }
    1 Y3 A, F, k/ l$ A$ c$ y# d//写入记录: z" X9 O5 U9 e& U% t- Z, k
    int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update), `; T" j  A" @7 r
    {% X9 H  t: q2 `/ b
        if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
    4 L: b- f: \) Z6 O( y    {; W8 C, ~5 ^) p/ p& T
             // ha_write_row  重点是这个函数
    . {: Q: O) |8 v" u, f1 p' D: A         while ((error=table->file->ha_write_row(table->record[0])))' U5 T3 g- H/ r
             {
    5 W# X  d" }6 k) c  ^             ....7 ^. b  u0 ~) m; p& W
             }3 b1 X; q6 w, O
        }9 I! ]/ n1 p" _" g  |# w
    }9 C2 C" m4 k- C8 e& C7 T! t  |
    4 T2 |% d. B3 l8 `/ J2 e

    ' L3 W& m/ f9 H( C9 a% A6 D5 W( e# _/ a# T8 O. r* v
    可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。; T+ \3 B& g  {: Y. j- o

    3 s; y# E% o; q. J& {3 ^: @<3> 继续挖 ha_write_row0 i2 c' O. G9 q) Y/ J

    - W9 h& i* ~% _/ e, V) ~- Yint handler::ha_write_row(uchar *buf)
    8 V4 u6 h& n, Z/ r% ~  }2 l/ n4 O{
    5 N$ \6 P6 w4 w( w7 n    MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
    . i! \/ k9 {) r# r}
    0 a8 q' K# l& ^8 x" J- v* `) y  I3 f) o) X- T" B+ g: }( U
    //这是一个虚方法
    6 U1 X) W+ m  ~* Q4 svirtual int write_row(uchar *buf __attribute__((unused)))
    % q1 y1 U' v" q/ v9 b{( s7 Z) @4 T8 h/ g7 q: R
        return HA_ERR_WRONG_COMMAND;  A- ]1 V3 d, O" i+ ]
    }
      W, U( J% P' h/ B: C
    ) Y9 S; u. K) _
    # z/ r2 W1 t- L' A* [看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;: @5 Z4 [4 R! N

    ; U8 q2 k& r5 d7 u, A# Z1 K% q3. 调用链图3 t! \, H5 ^5 P
    这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
    % @9 N5 e8 ?1 E: ?% R4 n7 H. L1 ]8 i0 `% g: a/ i$ v
    * g3 n7 Z$ D% ^" S
    ' b9 B  y7 H" y& n! S) B5 g
    三:总结
    . D, _! G( C" e大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。
    / K# i" I- z* P& O( U' Z————————————————) o6 K% ~4 l7 h7 P' C6 m0 j- Y
    版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    1 `3 `9 c+ b% w  K8 \' u  p5 H/ p- ^: I原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
    8 F3 O% C0 Y. p) d! ?$ f' {
    zan
    转播转播0 分享淘帖0 分享分享0 收藏收藏0 支持支持0 反对反对0 微信微信
    您需要登录后才可以回帖 登录 | 注册地址

    fastpost qq
    收缩
    • 电话咨询

    • 04714969085

    关于我们| 联系我们| 诚征英才| 对外合作| 产品服务| QQ

    手机版|Archiver| |繁體中文 手机客户端  

    蒙公网安备 15010502000194号

    Powered by Discuz! X2.5   © 2001-2013 数学建模网-数学中国 ( 蒙ICP备14002410号-3 蒙BBS备-0002号 )     论坛法律顾问:王兆丰

    GMT+8, 2026-11-10 16:53 , Processed in 0.246642 second(s), 50 queries .

    回顶部