QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 2976|回复: 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 R% }2 U5 A6 ^% ^3 _1. 讲故事) @4 o  V# U+ L3 K- ]# J5 N
    最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。" M5 J4 f& g- j3 g/ ^
    & R4 I! M! b0 ^7 x% K
    二:了解架构图
    . n" A' [/ A$ Q" T% imysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
    7 K* b; V/ d- Y9 @& g" d1 l5 u/ ?2 Y& }- f* J+ F' V- _+ M
    1. 从架构图入手
    1 W/ S: j' k! u6 \4 f, K. {0 B1 [大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。3 L' U! ?( ~2 F
    5 n3 r/ L( Q4 [1 T
    0 \$ m* W6 r: I$ r

    4 F) b' G; o/ H8 a' K其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~/ e$ `8 d! w: I) B5 L% `" K

    2 w0 `. h' S6 d1 A( q0 i7 r2. 功能点介绍
    : ^& a# ^3 P2 z: V& bMySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。# f' n& _5 W* {1 b: b: j& Q6 U' {
    3 i; x( f8 W( ]+ ?4 g0 X
    <1> Client
    3 \- z3 r0 O: q$ Z不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
    ( e. N4 [& a# ^3 e' I/ Z  d4 K7 [& y: ?* b! N% A
    <2> Connection/Thread Pool: M3 n6 d4 x3 H' Z4 x) d/ w1 L
    MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
    5 ?$ D0 r% R- L9 U3 k
    9 S  x8 s* ?$ c  a& a4 m. h5 d<3> SqlInterface,Parse,Optimizer,Cache
      \! p$ ]3 C8 T7 U' b对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
    ' L3 X# {) e# P
    1 w) R9 ~2 Z+ c) k<4> Storage Engines
    4 m8 K5 f* L. ^- y& h6 B; a负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。+ j' ]0 ^* Q; |3 _5 N3 F4 l# E0 @

    . S6 |$ H" o& n8 J" `: {三: 源码分析
    ) v; C+ ]6 E9 d& ~( P2 n; B, ]关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
    5 @, T9 t! |" ]  i% d# r& c
    ) ^( E) T4 f, D' V1. 了解mysql是如何启动监听的
    1 b* w0 R! [& ~: K3 F手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
    + y0 U/ d! ]: O3 o, @, Z0 p, Z! w
    $ H! k5 S0 i% U: X, ~0 D# a' A( I- x6 w6 d

    ' X3 v* d. l  k6 @从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。4 r+ V) S3 J/ N  g, u& @2 _
    6 e* I( Z1 r8 [; X7 }9 e5 Z
    <1> mysqld_main 入口函数 => sql/main.cc
    4 n7 V+ M, }2 x5 v: U' m
    ) b9 y: o8 r5 Y# b- H0 m( ^
    / l8 ~! O8 P; @  c8 W. s) |extern int mysqld_main(int argc, char **argv);
    9 @  k! i4 [, J9 R* b3 q# j6 h
      Q& D7 A# {+ C# R/ |int main(int argc, char **argv)9 _" z0 K/ }7 C  c. b" O
    {
    ( q1 L; f/ ~4 |" {" M  return mysqld_main(argc, argv);
    ( y. z5 i5 T# B8 r  S% f% ~' w- j}
    3 n# ~" M( V( k6 J6 V6 _
    + T9 M- C7 T6 f3 ^
    0 y$ B% M* ~0 U2 K: D/ \% f这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
    , B) L" ?8 U" g9 W$ s8 q; E. v" \
    # Q. \7 E, O2 `<2> 创建监听
    + U: \+ \; E' }( d# x
    - p1 @4 M5 a/ O/ I# g
    : D+ H; i1 Z/ x5 o$ u  Eint mysqld_main(int argc, char **argv)
    . ~9 u4 T* K9 G$ I1 X{: @8 Q$ q8 O1 g6 J
        //创建服务监听线程8 m9 p3 B( l9 \  g7 f3 a
        handle_connections_sockets();
    , O+ X( p4 @4 t" y; }. F1 w}
    0 W5 S  g# M- d$ N: k# q4 x! y, D& \! o1 F
    void handle_connections_sockets()
    % B) Q7 h! G; A2 H- A# N% E{$ p) u, O7 |7 e
         //监听连接
    # K! r7 s% H; h- n! Z, m     new_sock= mysql_socket_accept(key_socket_client_connection, sock,; z7 V9 @# W2 K1 P7 ~% N/ ~$ F2 i% Y  g
                                        (struct sockaddr *)(&cAddr), &length);
    ) |( D8 j5 A7 p8 ~3 k2 ~
    1 n* w0 S# X- ~4 X. {3 Y' Z    if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))  }+ W/ |* A: Z8 \1 Z
          thd->security_ctx->set_host((char*) my_localhost);
    7 \( b" j% P8 Q
    ) K" C& k! N9 K- G8 ?( }$ d    //创建连接
    7 y) m, L5 q7 a9 n7 f    create_new_thread(thd);  T, {# c% U* C9 ?* `- x
    }1 V6 Z  }! z, y1 h2 J/ N

    , h# }' m$ h9 [. v2 Z//创建新线程处理处理用户连接7 _! }: j' i; q2 d: y7 N" {/ C
    static void create_new_thread(THD *thd){
    ! O# ]0 v$ T2 U* ]4 ?7 R5 ?/ A- e  [! n3 m( ^' D  r7 L9 t" r
       thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
    3 I* X3 B$ z+ f' A9 J
    ) k& P" t4 G& d   //线程进了线程调度器+ l  A/ \( q' y& |
       MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   
    # Z" J* p# z& H}
    * @, t5 g4 R$ m+ U8 x2 N% c0 |, R; Z4 d: Q% F. Z. [: S  q  n

    " x7 h( K9 b8 v7 W! c至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
    6 f/ J( i- h3 i' _; I* O8 Y/ ^7 C
    3 O# x7 N- O; K; o- A% t% {! a6 H" K# S
    2. 理解mysql是如何处理sql请求# N  z1 Y/ K- l4 }3 s6 {% q
    这里我以Insert操作为例稍微解剖下处理流程:
    % i. y9 P; e/ B2 w3 }4 {9 j
    ' [1 A1 C' V9 N6 h% D) L当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。: }% d2 p7 Q0 C+ J
    * h5 [3 ?5 Z4 M

    ! z" A& E% U/ fstatic scheduler_functions one_thread_per_connection_scheduler_functions=
    3 X; o) b8 \! B0 I  x* Y1 E+ k{; V; Q' j* ?+ }; @$ a
      0,                                     // max_threads
    / b. F! T& U3 x1 }7 t  NULL,                                  // init- o% Y* V+ W# J4 i; T( ~. w
      init_new_connection_handler_thread,    // init_new_connection_thread
    4 W# b! \$ a' C6 L: G3 E  create_thread_to_handle_connection,    // add_connection$ X5 I0 ], \( }- d
      NULL,                                  // thd_wait_begin
    " f$ p& W! I% B: W  NULL,                                  // thd_wait_end5 r4 ?9 g) n9 M9 j7 z3 G
      NULL,                                  // post_kill_notification
    - S/ l; G  L0 V" H  one_thread_per_connection_end,         // end_thread
    8 ^% }5 O! e/ q7 J  NULL,                                  // end
    $ N& W- J8 L6 W% Y3 P};
    ' u( h) n6 I. {; n. S* t; x
    * E4 T9 O7 d/ d! w  \+ b) u
    " ~) j" S  ^' v( V  b! }8 @( n从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
    2 l' E. ?0 o( o7 e" ]; k$ m2 C9 I. Y$ F" {0 Q) s
    <1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪: ]# t4 J2 B! {1 k1 K$ q# ?% E! g  W
    7 N+ f/ J' O; s$ @9 R
    void create_thread_to_handle_connection(THD *thd)( G7 W. W" N9 X! Q+ h7 o& M
    {
      c/ e- ?3 Y$ @     if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
    0 M/ J5 ^: Y& r+ j                                     handle_one_connection,(void*) thd))){}
    % j3 S; Y" i' p: |. l+ l5 U}
    ( B# M$ I; ?7 _* G6 _//触发回调函数  handle_one_connection" r; ]& t) [" t6 m' f, F0 F
    pthread_handler_t handle_one_connection(void *arg)( V! X+ L- G. |
    {1 y) S$ y0 [& x' G
         do_handle_one_connection(thd);
    $ m! d! M) ]$ G" f1 y+ f. ^}5 F$ y9 w/ s* U# g; T$ s
    //继续处理4 Y. P) X+ L4 v5 v; j, k; h$ h; O5 {
    void do_handle_one_connection(THD *thd_arg){
    9 g, U# O- x! X0 O    while (thd_is_connection_alive(thd))4 V/ R% H; W9 D7 o6 I
        {
    ' g: x2 B& W: U- a: Z, S5 A& k& Z      mysql_audit_release(thd);
    ( h4 p, u( M3 I4 y+ ^      if (do_command(thd))  break;  //这里的 do_command 继续处理
    5 C1 \; C5 m7 |9 D    }, V9 E. F1 j" i. c6 p5 `3 s
    }
    - m1 V  a# E% _9 z6 d/ g0 V//继续分发8 C+ n' _6 h4 L/ X+ W3 X
    bool do_command(THD *thd)
    8 u6 g1 H& l7 N- f3 G) l{
    5 ~0 O4 k  K0 \$ r    return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));3 z8 d& E) z4 c& G0 o# n: V% m
    }% ]! Y: T; w- {' ]
    bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
    ( }+ Y% m. I, s# i' X* t{7 {. o7 ~" N- o% y& b
          switch (command) {% f# l* }7 ?* x' {( |* O# K0 l# T
             case COM_INIT_DB: ....  break;) m4 Y6 R8 f" \0 ^
             ...% H$ V8 c- i, _  W: |$ c
             case COM_QUERY:   //查询语句:  insert xxxx4 C/ I* j2 u/ ], q& r$ M" x, e) S$ g- L
                 mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析
    - I* N8 A2 N' k           break;
    6 L) k& |) Q( w8 Y      }
    ( N3 x7 g- U, }& G( g1 m! S/ p}
    ! b  n. \2 R. k6 g; Q$ X1 o, d" \& k//sql解析模块# _8 b; j9 M+ T  N/ n! x  o
    void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
    1 G( n& g% o+ M4 f9 `+ T{
    6 H; w; |; L1 p- l2 s3 ?5 d      error= mysql_execute_command(thd);: J! O5 v4 h* T3 l9 z7 |* P" S
    }1 A3 B  {- ]5 \- X: _+ z/ G5 _1 U

    3 a; i. C$ V; H
    2 ^9 e8 \  L6 j2 }/ Y<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。; W9 l8 d  M( T$ g. N
    6 Y: G! H1 N" k" k* d( Y
    //继续执行# f9 {- g+ A! Y" ~) e
    int mysql_execute_command(THD *thd); u8 w) p0 \. {
    {2 N2 }; }4 B: Y, `/ e3 L7 s/ t
      switch (lex->sql_command)
    ) ~$ `2 h: k3 E  {) P, G. Z4 y$ M, C
          case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;
    ; o& ^$ _9 O6 O# q
    " ^" s: t8 j! V5 ?      //这个 insert 就是我要追的5 z- |4 w1 x' }- G( |
          case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,1 o# k; P" q' a: F2 K+ a. p  N
                                                  lex->update_list, lex->value_list,
    7 @% N- d* ]6 H# U& z                                              lex->duplicates, lex->ignore);
    ' G& `6 T2 k$ d" i+ Q+ F9 k  }
    8 a. e# }# ]# K7 R}
    + |& |! s7 G+ P* U8 B$ q//insert插入操作处理- T) ]) |5 b/ b( C. _/ R7 e
    bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
    $ C. ?# b- s0 q                  List<Item> &update_fields, List<Item> &update_values, . b, _$ F$ e$ c9 N8 u
                      enum_duplicates duplic, bool ignore)6 H& Z7 x6 T) ~) I& f( G
    {( ?! S0 d1 Y: L+ T9 c' j. v6 j
          while ((values= its++))
    / D& q# G4 x5 w& s8 [' }      {
    $ F$ C3 h8 x0 j9 T& z& D9 U           error= write_record(thd, table, &info, &update);
    ' }  w/ f. d  }8 @      }3 z4 Y' c$ {) u4 [$ S! v. J
    }
    : o2 {# f) G5 e9 y8 C' W//写入记录; D7 k8 b3 o0 i# I+ {* _6 R
    int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
    % ~0 ^$ {- G( n: `$ c{/ ^/ C; r& _8 \9 P! c
        if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)& c0 ]) ~! K! n% a1 T. @8 e4 ~
        {
    3 }8 O8 y% V$ E1 ?2 H         // ha_write_row  重点是这个函数
    % O" v# T/ |$ k2 ~/ p% {         while ((error=table->file->ha_write_row(table->record[0]))): _( x" v+ u& P3 h
             {: q# A! F" X7 W2 R& u" `2 V
                 ....
    ! {, u" t; j6 M# f+ d$ _$ S0 R+ a3 y         }/ Q' |6 ?3 |. H1 x7 G" Q4 E3 R
        }
    7 ]4 [! f5 J* @( r; b! S  `* {}0 l" V8 X! l6 X' F4 u3 k

    " z' b* Z: A' T9 {- j+ [8 }) a
    5 G1 o9 q9 q' d7 K
      B0 b7 H1 ]9 h; A可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
    " a) d# O* J. t; v* q& f& I( s  s7 f  j- s- d7 z
    <3> 继续挖 ha_write_row
    1 ?+ W; A: U/ {  m- x6 `0 G
    / x( x4 x0 _% l2 Z, I5 B: ]/ @int handler::ha_write_row(uchar *buf)
    3 w- F# ?9 w4 u{
    - ^8 Z; O" j. F: l, I# A; {0 c    MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
    & M  G- O! J7 b2 M: e}
    " o6 f; w& b5 D* z% w: o9 H7 t/ [
    //这是一个虚方法
    2 U: z7 H! O( R' p' i6 E) xvirtual int write_row(uchar *buf __attribute__((unused)))
    : |% ]7 j  T  }+ w; I3 K- v{* q9 V4 p6 _4 L* z; l
        return HA_ERR_WRONG_COMMAND;
    & V+ v& z1 j" _% ~7 k}" j# R2 E, M, z) ?. r* V  ]) Y- v
    6 z, S/ A# [1 H8 M
    ; f. |& O5 W, E$ U' ?
    看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;
    : u2 g+ Y- D; ?% R% F$ S7 J
    . R1 N$ r( t: i& g0 T$ B" Q3. 调用链图3 i3 g" j/ f/ b4 w
    这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。. L6 \5 X0 F$ ?8 x! P+ z5 u9 r9 ]

    4 ?; j- ~% y% c
    3 O) R5 Q' k" d) B6 e6 D* v8 \% K7 ^
    三:总结
    9 ~2 j$ x& s' |" o5 k3 U3 }& K' K大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。
    ' A4 h* D. \3 l————————————————
    : a, {. H0 T/ d4 W* w版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。1 Q/ j) l" r, r7 W
    原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
    + y- Q3 G! O1 K; E/ B# ^
    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, 2025-8-14 04:31 , Processed in 0.466826 second(s), 50 queries .

    回顶部