QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3511|回复: 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
    一:背景
    , a% E7 `/ ~- ^' L$ ?# m0 R3 r1. 讲故事# R. `5 [( u; p2 E- {+ L+ J. `
    最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
    1 ^# R2 [! j, F. j3 n* X
    ( f* u1 S5 K1 l4 @二:了解架构图! m* r8 |9 T1 e6 c* f0 _5 g8 H
    mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
    ( ?6 f( d' [/ F) S4 {3 `/ s/ G4 f3 Q$ E$ _$ |
    1. 从架构图入手3 V2 p9 M3 U" I5 E$ y
    大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
    ( w) G/ \6 O- n, T# |  T+ u6 V* |, c0 ?8 }) Y8 h
    % `! n, A5 B% o# M& `
    " a: I# t, y) o; p
    其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~" w9 S- I4 o8 X0 t$ _

    , T# T) a; I+ J8 p1 q$ P' t2. 功能点介绍- a/ S+ V5 I9 R: @4 K% m
    MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。, |  L, B9 j$ C* w% A5 o% Z0 n
    % `/ w5 T' \" E. e/ |! g9 |8 w6 H
    <1> Client* `/ ~" Q/ w6 m5 X- _& N' F
    不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
    ; g! e; T6 S/ X4 v0 v* \, @! Q6 F+ a' _: w
    <2> Connection/Thread Pool$ h8 l1 G5 j- _- q
    MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。6 {& X  e2 X5 @2 o( ^$ \
    ( n( j) h+ O% x9 B4 B2 h. s
    <3> SqlInterface,Parse,Optimizer,Cache
    ; l8 l3 R) j3 o% `4 \" G对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
    " F/ q$ a, q6 {# V  H' b0 R7 ~) c' B9 W4 T" B4 |& {" p
    <4> Storage Engines. z) A; d) ?' D) x5 w
    负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。
    + O/ u: j5 X! [8 |5 `6 I; U. j: I. L% |5 q: d
    三: 源码分析
    7 {( D0 L- H/ n; D" v9 x4 }关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。3 Y4 F/ w  O+ Z- ?1 I8 I4 t2 o

    / P1 {& J: O4 ?: p+ b0 ~1. 了解mysql是如何启动监听的
    + D. Q# f+ M0 q5 {' |$ Y手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。( `; j. a9 [/ k+ C  g  \  Y

    # A# V' X" Y) u, N& h# {% L
    6 O$ Z: Q2 T1 I/ _& w4 V
    # {; ^5 u( c# r3 w" G% d$ p从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。- }' [; p3 e* L5 y

    3 g; B; i# q' D6 b5 `<1> mysqld_main 入口函数 => sql/main.cc
    7 i: a3 B! K) f! G5 Z% b7 H/ i' ~9 P7 `8 C5 J/ }0 y4 `
    3 l0 X7 R+ K( u" r
    extern int mysqld_main(int argc, char **argv);
    5 b$ K1 [" D8 W# [0 D9 O2 y! A, p; u$ h% L, P) u8 V: }" z
    int main(int argc, char **argv)# ], G: w$ q& F$ f, i5 Y
    {
    ! d3 x* R7 N6 G7 k8 @8 R7 `9 E  return mysqld_main(argc, argv);- f, A. |( q9 r( c3 H
    }
    3 n7 C5 f% ?2 Q: V
    0 B  Z; j+ o( _0 Q: h* O! H  X! h* D2 r- e
    这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
    6 b' _& X0 H. N0 K# H! X. k4 n0 n# a. ~' w
    <2> 创建监听
    , o$ p9 y. T. ]  r
    5 H. q) c. Z5 u) }7 Q
    6 |' |% W2 U9 O% uint mysqld_main(int argc, char **argv)
    ! X* A9 S( E+ [7 D{
    / W  a4 d' X' B, z    //创建服务监听线程, x- l3 K5 J# F6 z- Z. N
        handle_connections_sockets();
    . b/ ^' V5 F* A3 A; z}
    7 P2 {; \  I* m) w* g5 q2 G$ O
    8 U: ^8 \/ w- u* V7 Rvoid handle_connections_sockets()
      d- y. J" |2 O0 K  i4 Y1 |{. A/ {4 i% R6 Z! I$ j
         //监听连接
    , x, w+ t9 N0 W3 Q6 m# ~8 ?$ d     new_sock= mysql_socket_accept(key_socket_client_connection, sock,
    6 v2 w9 a/ k* e% a! a                                    (struct sockaddr *)(&cAddr), &length);
    $ Z$ n! E8 g# S1 \% _5 m8 G
    # ]& I5 T' K1 E' q1 _    if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))( @" m' o, X1 b  S* X; }
          thd->security_ctx->set_host((char*) my_localhost);
    ( l0 ^/ x) h" H; q+ L7 `# [" `+ T% q- Z
        //创建连接
    9 |0 p0 K3 A: T3 n( j    create_new_thread(thd);. n) e5 s8 P1 q- O1 P- ^2 t- R
    }( E8 `& c, q+ e

    # X( f: r" M9 X- ?! B$ a3 g$ \% A//创建新线程处理处理用户连接6 k: c& L, i( V
    static void create_new_thread(THD *thd){8 H2 C5 L5 [& y# [# l

    9 ~0 T5 P8 l2 h# z5 ?   thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;% Q7 c5 a4 ]. s
    # ^4 x7 e. N! q; V0 J# d' w/ a
       //线程进了线程调度器, l  j' F  ~: g3 K* e0 G' g
       MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   . C6 V2 S1 w1 G& w9 j6 o
    }
    . W, ]' `  @! ?* q" ~
    ' V/ a, `+ G1 [( y5 T5 Y, P6 _% |, k" v1 [) Y4 }1 s! w
    至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。! y3 C- l) W( F+ F; ?  E
    7 `) Y/ S0 Z5 D- |
    2 q# S$ l. z3 U* Y3 Y' u7 f% T8 W9 P
    2. 理解mysql是如何处理sql请求3 _: c- c3 ^: d6 R/ i. }: p6 ~
    这里我以Insert操作为例稍微解剖下处理流程:
    ! r& U4 {1 t7 P: f, [, O: [* w7 i' F
    / l+ d! s  r; S1 e/ {7 M当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。9 @- r2 }; Z& R% t% z3 U
    / s2 R1 Q1 C# [1 b9 S, D
    $ W) S* O' M8 K! w: p0 @
    static scheduler_functions one_thread_per_connection_scheduler_functions=
    ; a/ G& v) l% a5 f# }8 F- {{
    / \' p2 v$ k0 f  C  0,                                     // max_threads
    % b: g) m( I1 l) p9 v2 l1 G  NULL,                                  // init
    " |! X% A; Y* L( d3 i  init_new_connection_handler_thread,    // init_new_connection_thread  U/ p+ l8 {! t) ?
      create_thread_to_handle_connection,    // add_connection! ^; O/ a. [' a$ X! \
      NULL,                                  // thd_wait_begin" S4 Y! v+ O1 @2 |7 m1 v
      NULL,                                  // thd_wait_end  ]$ U& d4 z. e. q
      NULL,                                  // post_kill_notification
    # I$ l* S4 A* e6 ^0 Q  one_thread_per_connection_end,         // end_thread
    ( B8 I2 P/ \: P& D* U  NULL,                                  // end
    " S+ v# \; Z5 k9 E/ j5 A};
    + J5 S, u3 \! w% ~: Q+ S$ q* D, `- X
    ) g$ v/ M9 G# B( r/ T
    从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
    : K5 {* j0 l0 @, T: l8 {' \  a- u. K1 |( L/ B
    <1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪. }- g9 }. y' l' B/ f) j: U5 J

    5 z& v5 G2 m2 g/ g; g$ Nvoid create_thread_to_handle_connection(THD *thd)
    ) e/ D5 A, S, I+ h: o9 X{' T% y2 R$ @$ _& W8 ^- j6 H
         if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
    ! `. G7 w9 g4 o6 G9 q                                     handle_one_connection,(void*) thd))){}+ `( j% m7 P$ r8 i$ f5 _
    }. q3 M( d' ?& [0 M" P- x  r
    //触发回调函数  handle_one_connection/ W* ^4 n  C/ N' s% O
    pthread_handler_t handle_one_connection(void *arg)7 j( b8 `% ]! r  `
    {, T7 k* \0 k2 [* @
         do_handle_one_connection(thd);
    5 Q+ U9 i, g: g8 x}% ?: ?; g# p& a. f! ^, k
    //继续处理6 ]- q3 x$ H( i' T8 ^
    void do_handle_one_connection(THD *thd_arg){* i; ~4 g3 A$ K( W/ p
        while (thd_is_connection_alive(thd))6 h+ }) U7 C- m2 |: {+ e' @0 |
        {0 R  m# r" m% B* |4 y, A
          mysql_audit_release(thd);7 [) y9 e/ O  U( B0 F, b
          if (do_command(thd))  break;  //这里的 do_command 继续处理
    . i; W0 `  F6 Z    }7 B8 Y7 @' t& C/ v5 F5 ~  ?
    }
    . m' {& X" s; ]//继续分发; T9 ~3 g& M% M! T
    bool do_command(THD *thd)% J8 s8 w7 Z0 a! z6 t
    {2 k+ j8 n" z  b- r
        return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
    " ?8 k6 [3 h) t}
    " A5 E% }' w7 k# Qbool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
    % A2 J' L! M, |+ k  W- W+ ?{: {) [6 g7 S( Q; e
          switch (command) {: c& o) z. p7 j: @+ l  `! k& U
             case COM_INIT_DB: ....  break;
    : `2 i- g* o1 z" J         ...5 P$ z) j# `3 L" \; m# s
             case COM_QUERY:   //查询语句:  insert xxxx' }. q" J& G5 Y# F! Q1 t" A
                 mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析
    " R( T- S5 B; G) t           break;) X& u. m* z3 ^! M1 z
          }
    8 H0 h1 A5 ?) {- e+ y9 u! t; l}+ y3 }  ~3 j$ S. b7 Z
    //sql解析模块
    3 w8 p$ {& S; g# k3 z) b* lvoid mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
    . K# J* o1 P. o$ Y! W" V/ Q7 {2 i+ U{
    % ^* {4 A# P: |# w  X  e      error= mysql_execute_command(thd);
    3 U( g0 P4 ]; p}
    7 u3 L, _5 Z. J" r# l4 _
    & b+ I1 ~" {: ^. X7 Q5 n1 ^" V# h0 ^+ Z1 G* V) x- V" |
    <2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。5 z/ K  B3 t; d  [  M
    3 G8 O$ c) l2 ^2 k5 [
    //继续执行
    / A) K4 d) u3 A  g/ x9 Oint mysql_execute_command(THD *thd)
    . a5 V1 u2 l* ~/ i+ J{
    6 ?6 p& \: |' r  c: U  switch (lex->sql_command)
    : U; Z) X) R8 ^0 e  {
    - M' E, Z& O9 e, ^* j4 l      case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;/ o4 D. r8 ?" L$ v+ f
    % d- ~, X* M  `3 J$ X' ?" b
          //这个 insert 就是我要追的
    + u% D4 @" f3 }4 o      case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
    8 W% V+ _9 z) \3 P                                              lex->update_list, lex->value_list,% C! r  Q! T& d' d
                                                  lex->duplicates, lex->ignore);6 M8 y8 t& s$ E! Z5 z9 Z! ^) Q
      }
      E. r, U$ F5 m8 `4 m/ h}
    5 P7 s# }2 \0 V* S0 M//insert插入操作处理
    1 G% O% z7 ?/ j' a/ T+ \2 Nbool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,2 }0 @9 l7 j9 ~( J! a; h
                      List<Item> &update_fields, List<Item> &update_values,
    + ^2 }% U' J4 F* x. @5 G                  enum_duplicates duplic, bool ignore)
    0 B8 u) R8 f$ C7 |6 J" z: o' P{* ?+ B5 U( a/ J1 T
          while ((values= its++))! Y8 ^  K5 U- _! P9 G! ?+ U  K
          {7 g6 \: V6 q4 R$ z+ k
               error= write_record(thd, table, &info, &update);
    $ f( [( W, ^9 B. X' U      }
    * J! F) Y9 ~4 g) J# h}' ?9 P% i7 R/ @( b1 D5 u9 i- l& G2 |
    //写入记录
    0 [& X" T( S  E/ F. W) ^int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
    % H- K- ]! z5 O3 C" B{( R/ n- M  V6 p8 h1 z6 G
        if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
    6 R3 I4 \; Z3 C    {
    1 H* [: T% l9 U8 r' v# A         // ha_write_row  重点是这个函数
    ; v6 ]2 u3 L/ v' P7 X- }         while ((error=table->file->ha_write_row(table->record[0])))9 S# R# U+ n5 e! i$ h, l" l
             {
    1 G* h$ S8 [$ J9 n8 W1 [" r             ....3 o& L6 h7 I0 d- U/ p5 b' Y7 E  H
             }
    ; T- |) h$ J& {& y    }8 t# a0 x: w( Q6 _  j( Q" |2 k, q
    }7 A* [2 J5 o/ \3 r3 m' v5 I& C
    2 {* [6 D: |: K: B
    / F8 d. O3 M% J3 b+ Q1 e. a$ @2 }

    . M8 i& i. C5 Z, U3 M6 e可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。5 _1 m6 p7 M, ^7 e. \- R! a* c

    1 x; J( l4 p- l7 d& [6 s* N<3> 继续挖 ha_write_row
    1 I, g5 [, n' d
    * i& W' H1 _) w& F  Zint handler::ha_write_row(uchar *buf)" @9 B6 v7 X/ ]3 T- ]; V. `
    {
    . [% N! Z1 p. Y* E0 d    MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
    2 @9 ~3 J. N5 K( ^}
    4 x$ }! Z/ D6 I0 T+ e: K2 M- s6 @  a  W& L
    //这是一个虚方法) Y2 i' o) a. \$ T6 g* T7 ~3 v
    virtual int write_row(uchar *buf __attribute__((unused)))
    " Q+ F2 @# J& W' |9 \$ E{! W, Y; ^6 N% j: W- H
        return HA_ERR_WRONG_COMMAND;- Z2 o' B8 q% N( K- N
    }
    # Y/ {: X# Z  n. T6 L, k6 d) q" R' b# N$ I

    % O* D# M: W& e% C- n' q& _+ z6 Q看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;
    ; G) y* j; t. U# p; Y1 t
    9 ?' r7 `- O. U/ _3. 调用链图. T5 G0 x+ t  E8 c
    这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。) L' K5 i; \, T
    1 D, i3 j8 }) M$ k( q9 F
      ]! {0 c/ \4 N5 ]3 W9 s8 j' f; {+ ?
    : F8 G" d4 m4 |6 _+ W+ w
    三:总结
    * P* C$ @! m  X8 A大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。: X2 F# L$ p6 ~. C5 L# G
    ————————————————
    & x. T- x4 Y3 I2 T3 n版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。% @, c1 m) J) S) _+ s2 o7 [
    原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
    9 `# S6 j( z0 M
    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-4-24 21:14 , Processed in 2.960031 second(s), 51 queries .

    回顶部