QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3512|回复: 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
    一:背景  L& p( n. z% v6 _
    1. 讲故事( Q' V; d  I  d) n/ _9 B' E! u
    最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。. Z5 d; {% E4 h7 G5 f
    : A' d- j( N. q2 L) U/ C0 D
    二:了解架构图
    / H: y2 \: r2 E. }& V* P4 K3 j+ smysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。2 G9 A+ x& S' j! E+ g

    5 ^# A" I/ q! g7 I6 S: |1. 从架构图入手1 d8 Z! }0 L+ ~( {+ K5 A
    大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。% E3 d2 V  Q8 s0 S& k2 j+ f, X* y+ y

    3 x4 @; }1 y; h- c: Z2 |7 q6 c  W) ^" j  Z  p
    / h& @( x- r: R2 a+ V$ C
    其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~  ]$ }  q; V1 H

    1 a4 _7 O. ]5 \" j. @2. 功能点介绍
    , w  T+ }3 c2 W: gMySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。/ d9 y+ Q5 D- {4 E+ E- l% Q
      j: R# K$ v) W* i
    <1> Client  P2 [' N. m* j7 f. R
    不同语言的sdk遵守mysql协议就可以与mysqld进行互通。# y, q' j; m" d7 V! k* Y
    % h; M8 e- i0 R! G2 Q
    <2> Connection/Thread Pool' z$ I- u8 ?/ `: t
    MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
    3 ^1 z: M- u7 v) V/ J$ I8 J# A8 ^0 a+ _8 H
    <3> SqlInterface,Parse,Optimizer,Cache/ @# `' c# o+ b) e" v
    对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。# r7 v8 Y7 x6 U% `* Q
    , z: s) B" v5 `& n$ `7 Z
    <4> Storage Engines
    $ L, Q' ]1 h" E; C- F! J负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。; l) |( {9 B2 H4 i: [% ?) |

    ' ~: f- Q  e  g' D; w4 G3 d三: 源码分析
    9 u  k5 \0 c( e7 C* {关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
    " `# ]/ C" T; r! Y" }# O6 T) J/ l3 N% s  k- e3 W
    1. 了解mysql是如何启动监听的
    2 S4 W% J9 y# a5 z% o' S5 v2 c/ O手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
    * X8 }3 i% u& t* P9 N9 }
    - _8 X7 E* q* b3 _/ e& |2 i+ `  e, {2 t1 M
    6 e! f( n$ O$ o! C& c
    从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
    7 l! Q) F1 a1 d* `! f) W
    ' M# ~3 g& B. R: ^<1> mysqld_main 入口函数 => sql/main.cc
      v  B: z5 u; N5 _3 f- ~& x6 v5 w$ X0 Z; m5 h  a, [' F- l0 I

    2 q: v: t0 H3 m; K/ m5 t6 ~1 A" Iextern int mysqld_main(int argc, char **argv);! ?5 Y& }$ |' Z/ V( u7 t, e

    - [$ q* @9 o. e$ M9 M8 m4 v) dint main(int argc, char **argv)% i* f; Z& {- _" Y1 {
    {' L6 L# A' d' D" J  u* ~
      return mysqld_main(argc, argv);) h1 ?( z& Y& d5 g" i" a6 \7 f2 b
    }
    $ j1 Q- }' w7 N% M: f; h4 l' V" X  q5 l; K8 y! y, H0 m6 B7 E

    4 S* l0 u# \6 D9 A- p6 ^' e) T这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。& R$ V! g- H. ^4 C) @( F

    2 P* S  t7 Q/ q/ G<2> 创建监听
    + N( \/ K- C9 W4 T7 V) ?2 \$ _, {2 d1 k6 a5 B
    6 T4 ?5 C' m3 A% O
    int mysqld_main(int argc, char **argv)" v/ r+ E8 U# \+ u5 M+ b% e: {
    {% K! G, D& `5 o+ x) X
        //创建服务监听线程" J! K- m) w0 K5 m- F
        handle_connections_sockets();
      p. E( W& u, v% Y6 a* v}7 K7 [0 z" P4 b" v
    0 I7 x. ?, B) h  q6 p
    void handle_connections_sockets()" v0 e/ T2 w( ^, i/ N& ^
    {# l# R9 \3 l: C. G% F+ `9 s
         //监听连接
    " _. ]0 L0 I. W; Q$ a& a; K     new_sock= mysql_socket_accept(key_socket_client_connection, sock,
    , Z3 t0 L  j% R( P6 O) y+ z                                    (struct sockaddr *)(&cAddr), &length);
    . a' R+ G% d* S0 z1 M
    : q8 a- ]: f1 B) I  e    if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
    ; e  H! t  r8 U( A      thd->security_ctx->set_host((char*) my_localhost);
    1 }* Y( e8 q7 |$ J
    , Y$ @6 Y! M. k( A6 P1 z    //创建连接
    ' l/ u, Q8 V* m  V) e& P    create_new_thread(thd);& m" {5 |: t* f* t, r
    }: M9 N& K% f; s! y' {- c
    ) X4 Q4 r  p$ i. M5 f
    //创建新线程处理处理用户连接
    ; M( z6 r6 O. b( Ustatic void create_new_thread(THD *thd){
    5 ]1 d0 Z5 ~8 i& u9 Q0 |- O2 h; y  u2 }0 R+ T% l# ?$ `3 b
       thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;3 X9 A; C( G. L4 n7 o) m! x9 Q6 b$ A
    % Y4 ]6 O; W' @/ a) [$ N( |7 I7 r
       //线程进了线程调度器+ e3 H5 y4 [& F: g/ l: d* @& ^' x
       MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   
    4 |* e6 S- z+ b+ ]9 S" ~}" ^' K" A( W7 Y
    $ s5 B" x- q; K+ K- C- S

    ( M. G7 ]4 N% u  ^) E' w至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。& q3 d8 E" f8 `1 D
    6 ^$ g% `, A3 p- f3 i

    0 a1 O3 W5 W( C0 R2. 理解mysql是如何处理sql请求& @. B( S0 J/ M  ~0 ~5 ^
    这里我以Insert操作为例稍微解剖下处理流程:
    : T5 E, O2 x% |) s: Q1 R! R' n
    7 ~9 v) c8 b' a0 R( s2 n当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
    3 q7 P9 v1 V/ I9 o, l) n6 g
    , }5 k% o+ j+ E, H9 R. B( _4 M2 z# T* B
    static scheduler_functions one_thread_per_connection_scheduler_functions=" z( o2 a5 T5 _' }
    {" W# M% u1 V2 d5 \
      0,                                     // max_threads
    6 ]. P% O- }: F0 ~. d- Z  NULL,                                  // init
    - W- T  S$ k1 s: O4 _% s9 e  init_new_connection_handler_thread,    // init_new_connection_thread& E$ ?' u' ^$ h
      create_thread_to_handle_connection,    // add_connection& Q/ Q% U- z/ q$ g8 T- t
      NULL,                                  // thd_wait_begin1 u3 P$ X' E6 x6 p: v) c! I+ F
      NULL,                                  // thd_wait_end
    / P2 h3 @0 @% a0 ~& I( a6 N  NULL,                                  // post_kill_notification% C' |( ~  I, @. r
      one_thread_per_connection_end,         // end_thread5 N' k7 `$ L" R" C. J4 {
      NULL,                                  // end
    " C  I4 r# k6 g4 J};
    ! d9 S" a" K) w2 s! n
    5 r' ?! K8 m/ D' p) g
      o( J- ]6 y1 L' H6 C. S. F/ Y从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。9 B1 Z$ |( v3 D# N; r3 [

    $ d" |+ y. m" l* o  {9 R<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪. o, A/ m# C- G: v% n0 ~) ?6 @+ Y
    . Q6 L* m. x2 S2 y9 @2 {  g
    void create_thread_to_handle_connection(THD *thd)! O  n. h- G, T' o$ Z5 z
    {
    $ E& U$ P% q( Y5 a1 O; A* n* n     if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,, S  W7 E7 v% y3 i6 I8 e/ x1 R/ X
                                         handle_one_connection,(void*) thd))){}4 h$ M4 z3 j$ w8 S' X
    }
    $ L- G, @8 I9 y# e" q( @* Z( {//触发回调函数  handle_one_connection( E! j0 a- c7 A, B
    pthread_handler_t handle_one_connection(void *arg)
      Z4 Q; ~3 |, j{! O' h4 D% j- F% n8 r0 M
         do_handle_one_connection(thd);/ G% ?3 F3 {; ~9 I
    }
    ' V$ H2 T) {  K" ?4 s% H7 t//继续处理
    + J' x* X; k& ^( J: [. C; vvoid do_handle_one_connection(THD *thd_arg){1 M! V# m( @& w3 h
        while (thd_is_connection_alive(thd))
    & f: B) j) ~5 R' J$ y    {- q+ c8 @% r( o& x6 d4 t# R0 D
          mysql_audit_release(thd);
    + a$ C+ F* Y) k& C" q: ^# f, H5 i      if (do_command(thd))  break;  //这里的 do_command 继续处理- a. x; Z% B3 x# p& ?5 `! M
        }
      v. u3 [; C' A6 e* {}
    ' H- X! {+ N8 t( H0 i; r2 s//继续分发, P& k* r% M5 L) J7 V/ Y4 T( _
    bool do_command(THD *thd)2 {. r9 ?/ s9 L' p3 z* D3 g
    {
    ( n* G# W6 h! o    return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));: R2 `+ m& L2 m4 i. F
    }
    9 S) [$ b0 B+ Q0 I; Ybool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
    3 X& B+ U$ g# Z- F' Y{
    ' o& V0 O. g& O8 [: _; I      switch (command) {
    ' M: ?6 ]. V$ n$ y- D7 D2 y! g         case COM_INIT_DB: ....  break;
    5 c( g* X, a" v0 W; I' K         ...
    - o- l3 W5 H- E! o. J/ t         case COM_QUERY:   //查询语句:  insert xxxx  ~9 f" m' I5 b
                 mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析
    9 \9 O6 E% k6 v8 I! g           break;
    2 p/ ]! U% Q' q) a7 j      }4 z% s  m+ G; |! L+ f- @+ ^% Z
    }$ a% X' E8 p/ H
    //sql解析模块* G* s/ Z5 Q0 F8 N3 b
    void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)% t6 J$ E% K% d7 A5 a. [( R1 g
    {
    . `2 \5 ~4 w, ^0 D) F) i( j      error= mysql_execute_command(thd);
    ' [3 e0 s: E7 }! N; V% J}
    3 A, k5 r( h$ a+ }! ^' j3 h/ V# M: M9 o9 ~* d

      P, G1 q6 z! J0 a. a<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。7 w$ {% u  B! M9 A/ i( Q7 w
    + l" y4 t( M0 D  A
    //继续执行$ |& _- |  Q2 z# K
    int mysql_execute_command(THD *thd)/ f  J3 g; w% B- O$ w% @
    {2 ^3 w# z6 b% x3 b( ^* [
      switch (lex->sql_command) ( H1 ^9 e6 f8 E( i* _; N
      {
    . \: ^% k; x! r8 v4 i3 W      case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;6 c% L. f9 ]: p) I: {0 T/ s

    ( J$ @2 v0 d0 i  n. t' t/ [* g      //这个 insert 就是我要追的
    # n4 h, P, p* i3 t- p      case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
    , b! }; `4 m% C5 C1 m, f                                              lex->update_list, lex->value_list,( P& E9 K$ [. `& @& r; }' R
                                                  lex->duplicates, lex->ignore);- X8 d" I: ^  h) Y" X: J) M/ Y
      }2 y7 y' Q9 j+ s# d, i+ }% R
    }2 `- a' ?# s4 q6 z  c) M
    //insert插入操作处理
    ' z5 e2 d- k+ h. ]$ d- ~bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
    ) M! x! ?8 ~( p( I. e3 T! x: ?                  List<Item> &update_fields, List<Item> &update_values, & W9 G. `5 o# [( R! V+ H
                      enum_duplicates duplic, bool ignore)
    ( {, G2 d! M; V0 ?{
    ) K5 w4 [' ~( l  j, W6 u      while ((values= its++))! ^$ U0 _! `, f4 S3 D
          {
    ' G% u1 n# j9 C9 G! D0 q2 a           error= write_record(thd, table, &info, &update);! z9 @8 K+ ^9 e( ]; u
          }
    3 T8 C0 ?- p3 E+ S6 W& q: i) J! z}' O$ b( T9 t8 X( \# P. Y; G0 R% \
    //写入记录: X1 a1 }. _" C! d) {: w
    int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
    & s! P. z! q) L3 \{
    5 k# \3 h3 m2 X2 k. `    if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
    / t+ C: o# c* p    {0 o3 k2 {. ?2 n- j
             // ha_write_row  重点是这个函数
    ' g4 v+ A% P- d2 a         while ((error=table->file->ha_write_row(table->record[0])))
    " J  h& `5 D# z* |$ C7 m+ h: v  G/ O         {- l( f# T$ o* p' q' Q' x% b8 R3 s
                 ....
    2 F+ F! L/ @% P4 L         }  R2 K" S3 e6 @9 z2 j4 D" ~
        }0 @3 I* t% g$ N1 G
    }
    4 P4 Z9 u2 `' _8 d9 Z% a1 J$ V- e& F9 E5 E. K

      U4 I; L3 J7 p. X1 j) B: G6 ^/ Z+ q$ X' W8 I( R0 C1 o/ c! @
    可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
    / q2 }% X% e! b6 G# x/ |3 S7 t: [0 Q) B* S" G
    <3> 继续挖 ha_write_row
    ! h" L) j" G7 Y/ \9 k
    - y0 G$ d6 A) N5 y3 Y9 ?int handler::ha_write_row(uchar *buf)7 v7 T% O/ Y$ b9 N, r& o
    {
    1 Q. M( U9 w  f+ E( @# H2 @1 V+ r    MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
    ! J) j% y0 j' q2 F8 z3 `}
    ; y, v; g# ]' [- }4 u
    : c1 j8 c- s/ ?3 ^3 g! G" |% ^//这是一个虚方法  x8 F! ^* N! I
    virtual int write_row(uchar *buf __attribute__((unused)))/ B7 K0 g6 C& e9 q
    {# j; O+ N6 O! B1 J* I" y/ p
        return HA_ERR_WRONG_COMMAND;
    " ?# O7 u2 o' D/ X: B$ O}
    9 |4 @' b1 I) e5 r  \  H. y  P: I5 D) h- k. G; y( D4 h2 g

    - [' k4 L' `8 Q) z看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;. p% [& x) f: q  t

    5 ?5 e8 Z8 C+ t1 ~! B) K1 k3. 调用链图
    / E. i) H8 O3 {3 {; J这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
    4 [" D3 q' J# g; l7 v+ G: y
    - s, m7 G9 q5 B  g3 q+ S2 L
    * x# N! y- K6 v: d
    % n9 f0 ?  ?" ]: _% g' B三:总结" i6 p2 r, C" p+ k  q  _
    大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。
    . }/ w. @7 P3 c, q' ~* N————————————————
    2 W8 \+ y& @  e' E8 q7 I, `" I版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    " ~& Y" M2 ~) F: v; I+ Y原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
    ; L1 N; R& y4 r, _. K0 I2 l
    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-25 07:18 , Processed in 0.424382 second(s), 51 queries .

    回顶部