QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3540|回复: 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
    一:背景. U7 R" P1 I5 l+ k& A* m
    1. 讲故事- U1 O+ l1 m; Y
    最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。. r! i6 T; [6 U% B" M) p" `

    ( l, J+ e5 Y$ ]; z" T7 ]二:了解架构图
    ( G3 p' f- g5 [# h1 V, Q: Dmysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
    , E1 a  d) ]2 Q, o# S7 ~( P$ ?, ]" }! N# p$ ^; O  X
    1. 从架构图入手" w5 c( u$ W1 h4 I4 x3 P! P
    大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。7 S, w' l, {+ Z$ Z0 ]

    ! n" b' _: v* x3 t5 f! v
    ! t6 m' w# F. P. c: g2 `0 q/ S5 |
    / z  W: m! m, \1 d其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
      [5 I+ c% a5 U5 b. g1 X* h, i8 O0 i* O6 |5 Q) I
    2. 功能点介绍
    ; N" T# L1 h# u; GMySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。8 w" H( K: G9 ~) x4 j2 e/ t
    4 s6 I5 Y- C; m, ]+ _% s
    <1> Client2 E* U/ j+ G1 O7 }$ Q
    不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
    ( ~) X( c: o5 ]3 |  O% \' I9 Z( G) Z  T$ l: A) `% Q3 x
    <2> Connection/Thread Pool* b( n$ r; s- K8 L" w
    MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
    ) ~( k" W( W) v' f: ], Z' R, ^' [: F8 F; {0 e
    <3> SqlInterface,Parse,Optimizer,Cache% P, P# _( `* q0 g" z7 Z
    对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。: T1 D* z' s; o9 e

    / L' W' [* q" F! q$ `  A<4> Storage Engines2 c) F. R5 c' r. u: I8 i
    负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。
    2 z4 Q& _% P# ~( |; j1 G* o- z8 Q9 o. g; H
    三: 源码分析" J  p6 d# [& ]) V5 M& Z) a. J
    关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。. F7 N$ J+ M' R, |8 k

      N4 a: `0 t! l1. 了解mysql是如何启动监听的' ?% a, s" a* Q6 J8 h5 v1 @% W
    手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。( ~2 E* a8 [( g5 P% q+ m' ^
    * \+ |3 p+ e4 p3 O
    7 X4 b: L% E9 G. O
    # m& s# d3 @: i* O: p* m2 ?9 r7 `
    从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
    6 W2 I8 H/ N8 O3 V& N% d  Y$ O
    ) w$ z% Q1 t9 X, ?<1> mysqld_main 入口函数 => sql/main.cc
    $ B. G$ D1 T% d! |  T4 S0 |* T
    " \" I, ^( Q7 ?& X3 c9 `0 v9 O7 S3 ?6 K; m6 l. ?
    extern int mysqld_main(int argc, char **argv);
    % u1 e. U1 d6 a
    0 n# A7 @; R% U  Tint main(int argc, char **argv)
      j; \& Q! B4 A8 G. s{) W& s( M* o9 O. s3 d8 W
      return mysqld_main(argc, argv);$ x: H" Y6 F7 W9 s4 C
    }
    ( L8 j. x- D, K: Z  \$ q8 ]6 w; x+ F/ C4 m; m

    ! k  K% \! [- A) m+ V- P$ w1 g这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
    $ _5 I  r) r; \# L2 q$ m/ t* Z* A' z, w- ]
    <2> 创建监听% F& Z0 V9 {6 D

    6 F0 E7 ^2 b0 Q7 ]
    + l# Z9 S8 d7 A5 s, H! ]1 I/ zint mysqld_main(int argc, char **argv)9 P- j  [* _! j1 o/ R8 B
    {( U! x9 ^0 l# ]# t* N4 R6 \
        //创建服务监听线程) O' _  J% `: g- V! H5 H
        handle_connections_sockets();
    + R! z$ ^5 c" p, U9 D1 l}9 v; I9 n: M: c/ T" u- z/ Q: v

    # ?& }& r6 j( w5 w6 ?void handle_connections_sockets()
    3 Z" P9 J+ L1 V) k{
    + b; y; _' _2 M. v4 V" E6 g     //监听连接
    / p4 g# R4 X* P& `+ d( H6 E     new_sock= mysql_socket_accept(key_socket_client_connection, sock,1 d4 S8 r7 a# g$ e8 j
                                        (struct sockaddr *)(&cAddr), &length);
    , S) I% o: ^! \# R7 ^  R: {! a
    3 L7 [0 T' f5 w4 E, h5 o8 H    if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
    * S4 o! R4 _' f      thd->security_ctx->set_host((char*) my_localhost);
    ' q  ~, J0 }" j4 r1 ^! C- K( J! X" K
        //创建连接
    ; m2 A, S. A) C' L5 ?; E    create_new_thread(thd);. C4 o; R( Y4 t) p( J/ a6 _# W" P
    }
    : H/ w% u; |6 t' M3 \
    / O& R: }; d% @, m- K; ~7 ~//创建新线程处理处理用户连接
    2 X) c' V/ P' E, a" f( J7 \static void create_new_thread(THD *thd){
    / w# Y7 }9 f7 d& \& J  y: h
    # U: Q) w) F0 N" ?7 p" O4 J   thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;) D# {0 D1 C1 h+ @& j: y# v! V3 B8 L0 P
    - X4 E4 A& ]% J# X  r( U, [, Z: q
       //线程进了线程调度器
    5 x7 Z) d) ]/ K" `, I$ G   MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   . p' O+ Q3 R- ^; {5 V5 D5 s  Q) H; f
    }0 n! @  G% }7 F, A: {% Z( Y

    ( b8 A- y6 J) ?% W! L5 y6 c/ p: Q" f& d( D
    至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。5 @3 v: V" j' h
    ; _/ _  n$ S7 F
    7 E; u2 L: w6 ]2 }) o, R3 t* X
    2. 理解mysql是如何处理sql请求
    & r, q, x- J2 O+ B5 r这里我以Insert操作为例稍微解剖下处理流程:/ E: i6 g3 @+ [  ~" Q
    ( l0 e% x8 a* H# h3 @. n
    当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
    ! q1 r4 g  s5 q6 ~
    , L) c0 W3 [  E( U7 z5 x4 E
    * l( [; o# h# p2 F" Mstatic scheduler_functions one_thread_per_connection_scheduler_functions=4 F, q: p. T* x. j0 j
    {2 I; `& i9 X* q3 c" y1 S( \4 n
      0,                                     // max_threads- a  ]8 h1 n5 J) g5 g2 B$ j5 `$ q
      NULL,                                  // init( }8 E# |- ]" `7 G' ^
      init_new_connection_handler_thread,    // init_new_connection_thread
    % Q5 p% ?& m% q9 w" H8 K  create_thread_to_handle_connection,    // add_connection( H, o# t$ u+ K3 W$ M! E
      NULL,                                  // thd_wait_begin% m* U* J: {  Z. Y: {# N1 x
      NULL,                                  // thd_wait_end
    3 }5 m4 a! K' w  NULL,                                  // post_kill_notification: B7 `# @$ }! m: b# C% q7 G5 R
      one_thread_per_connection_end,         // end_thread
    & S; w6 R8 @+ t3 j  E$ r  NULL,                                  // end
    3 A8 S" ~6 R$ s% R1 C};
    8 U. A6 n+ F( X* c! ^) y1 i2 @) F/ J( N5 q3 U7 X; s

    . H/ [: f3 c8 V7 o+ Z6 O3 v3 I从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。' L7 N# A( z, S! l2 n

    % F# ^! j) L, n. B, G7 p<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
    5 E! e) N4 O2 ^3 M7 B/ j+ m/ m8 e- H; E  Y1 y; ]
    void create_thread_to_handle_connection(THD *thd)% l. F* K  u$ }# u, [
    {
    ; ~0 u1 \" O$ \: x( Y' S* C     if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,* l  m/ Z! X# Y% X
                                         handle_one_connection,(void*) thd))){}
    ( ?3 C& @. k( d! q2 a6 I}* D3 {3 R$ \! T& a6 J3 D% T
    //触发回调函数  handle_one_connection
    $ R* g' w5 C) p9 f: v( P6 M) cpthread_handler_t handle_one_connection(void *arg); d+ ?9 ~% L- F8 [1 b. N8 K7 A
    {
      ?+ D9 r: R  g: W8 ~& G  t     do_handle_one_connection(thd);$ E+ [3 p" m8 W5 _! ~1 l1 @
    }
    / z7 m* u2 F! _//继续处理
    % C. n! w1 c! `void do_handle_one_connection(THD *thd_arg){/ E  @0 e3 [6 G4 s# `
        while (thd_is_connection_alive(thd))
    7 i+ X6 b& A1 P; G    {
    7 M6 H! M$ K0 Q2 Q6 s      mysql_audit_release(thd);. U& G* {& T8 E+ H# S- K
          if (do_command(thd))  break;  //这里的 do_command 继续处理5 _( Z" F, f- Q9 [5 \2 J2 s2 k
        }! [0 _* J& u% U7 |) {0 v! d
    }- `" E) {) s/ k; b( Y: }
    //继续分发
    / T2 K9 x* s9 [2 L$ L/ G) @* @bool do_command(THD *thd)
    ) }9 {! V2 P: \$ w) k{0 C' J* G! A0 p8 t% b/ [5 M* E
        return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
    9 t; G& @( w  }+ Z" D' Y; ^}; a, c7 |5 D; c1 W
    bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
    0 y7 K9 Y" s0 u# ?{
    & i0 B6 b5 b% s1 W5 _; A      switch (command) {
    * m  L5 k3 T& g& J( _+ y& f         case COM_INIT_DB: ....  break;
    1 ?; g& ~% z7 H$ |- s- e! p         ...
    % N% i1 [) k0 m0 z/ a) Q; O9 B         case COM_QUERY:   //查询语句:  insert xxxx
    - |( i1 }8 c$ W3 R, Y             mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析
    2 U5 L9 e& `+ Q. |; Z           break;
    1 _7 }" f9 K1 `% ?) M; i      }
    $ D6 B% T! S: b2 d8 d% }* J  `}
    . E# ]- s) C3 `- {- B# J5 i//sql解析模块
    * R% ~0 r5 R4 w) k9 Tvoid mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
    * P) M" `6 e$ I9 q! V{
    * W' d; u$ y  ^  s1 i- s      error= mysql_execute_command(thd);5 O, o  s1 ^3 R3 F- z
    }0 `0 v0 y$ L2 r
    $ ^6 L/ H3 g$ Z/ j5 u
    ( R7 c, s0 I0 N( M) z
    <2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。; j7 E5 u$ `8 ~* m# M& k
    * b* W- I& @) D8 G6 ]( T: L. m
    //继续执行
    ( u  d" c" g: _int mysql_execute_command(THD *thd)( h) B$ f3 A- j  I
    {: b) A+ p6 C" f0 ~
      switch (lex->sql_command)   A7 {+ J0 i, e8 q! s1 R( e& k3 w
      {+ X/ ?9 h2 L- d- ]% s; e! k* w
          case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;
    ) H- P/ W2 F- `" ]! C- n5 Y
    . e- K( R3 r+ g9 R      //这个 insert 就是我要追的
    7 X  `- a4 E1 d      case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
    4 d9 F6 s& w6 T: A! X7 g& q8 Q                                              lex->update_list, lex->value_list,
    ) n2 A+ I; [: I! x6 ~& R. m                                              lex->duplicates, lex->ignore);
    0 Y, p" T4 W( R2 ]# v5 E1 ~9 z+ u( N  }
    8 I  G; f. m7 k2 Q( E* ]9 r}
    7 H! X. y- g, U6 n//insert插入操作处理" d, K& l# ?/ G! j6 K$ p
    bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,) o( w- E4 Y- x  N/ b& g" y7 I
                      List<Item> &update_fields, List<Item> &update_values, 5 A$ R0 i  [$ |4 R- U
                      enum_duplicates duplic, bool ignore)! L( z7 z& B( t# ^
    {9 [/ N, t0 w  v; M' @$ X7 S
          while ((values= its++))
    6 ^9 j! U5 r' w- J% l7 X, z      {
    & Z' l' m5 ], [* W$ v" t           error= write_record(thd, table, &info, &update);) g! R* ]# k8 v
          }
    / H2 L1 v% s- e; w* @}8 T/ I/ r4 m$ {
    //写入记录
    " v. c' Z( a& t' U$ M) qint write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
    ! e/ _4 U5 n) Y9 s2 I{
    * h9 g- r1 P) R; B: |    if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE). Z* r. z2 x& ?4 T( e; K6 f
        {
    - x* s/ j8 V1 o' ]# _4 [0 R9 U+ e         // ha_write_row  重点是这个函数
    " n' D# E. k& R# K" ?; ?  ~9 f         while ((error=table->file->ha_write_row(table->record[0])))3 P- r8 ^$ Y4 I% i6 @
             {# C5 G0 T+ m+ q. c  x; W1 d
                 ....
    3 P( ]4 y+ U9 W9 U+ W4 a         }9 E' ^$ e# C0 ^) P! f
        }' p: V  u: O  C! Q9 P
    }
    + u0 F2 T- X: }! Q# J. p
    & `3 ~! L. G. t9 P7 U- ~9 X- r/ R  i; r# H2 n' r- h" k4 O
    5 F8 d. v( V+ a2 v8 u
    可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
    + W+ Q. T$ `  a" T6 z' V7 h0 ~. W1 |9 W3 B& d8 Z  E
    <3> 继续挖 ha_write_row3 Q& }( r; C+ F1 U! [9 K

    4 [% U( D4 }4 w+ o+ _int handler::ha_write_row(uchar *buf)
    4 ~: P$ o) K+ m2 @{7 Z9 X; {8 X9 \/ h
        MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
    $ L( L/ S2 e: z2 j3 l/ n}
    2 D; S( l) y# \! H, q! d6 K8 a
    + A! J( R+ q1 D5 l//这是一个虚方法
    " ?6 u6 H* J1 ^+ }6 }2 ivirtual int write_row(uchar *buf __attribute__((unused)))$ k5 E8 ]  X& \" Z4 I) K
    {
    ' d1 V; @7 c/ z6 S/ x    return HA_ERR_WRONG_COMMAND;
    $ C! S; z+ J: n9 e4 e. e& M}
    : J4 R# ~& Q; K! ^) _
      T+ H9 _3 D% z  b  f
    - e5 ^- T4 m3 {! O2 a4 x) y看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;
    1 \# {8 t! K* g' `- ^" G5 N
    + r0 o4 v0 {" N! q3. 调用链图* h7 x3 q# v' j. k0 p% p% R$ W
    这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。1 m: v2 Y6 K# a) l9 r! Q" j
    5 l0 O8 ?' w! o* z

    # j2 x' W9 }& N9 ]
      R* H8 \, {" A+ f三:总结7 c  y2 f3 o7 N
    大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。
    6 c, D4 L7 h/ S) y. w( l8 I————————————————8 j3 Z9 \5 U5 j. N, x4 a; O- n
    版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    / N; w3 p: u" N9 h7 ^原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
      }( I2 w4 f' I1 _
    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 19:59 , Processed in 0.394116 second(s), 51 queries .

    回顶部