QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3170|回复: 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
    一:背景, q* k% K- K6 L9 Y. ^/ p! C! E9 \' ?9 s! L
    1. 讲故事
    7 G. |- {0 x( A9 |最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
    # P# A; r8 y, K$ I1 k4 m+ f* b/ h8 w0 @! J# {
    二:了解架构图
    3 x" {7 |( f7 m2 W; @9 \8 ^mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。  E" l+ D5 k+ Z' y7 t$ u

    * j9 I& G9 @' _# k1 S: a. p1. 从架构图入手
    3 _0 ?& @. w& b/ K2 s' |大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
    7 o5 w$ J) N: I: x8 M; X1 p3 P, X. ]$ x6 ^4 J

    * @" B. a4 q# _, ~& V# S) x. m
      l% T6 {1 ^, t其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
    9 }# c) p: @) u3 e9 Q. i# x# ^0 ]) X- m/ |  I/ i7 t, k, h3 }8 }" M
    2. 功能点介绍
    7 r# q% C" D6 L( H: N2 {# |MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
    3 ]$ @. [9 q0 |7 ?- _' B+ o; Q4 H/ ^: o0 d9 j5 c
    <1> Client
    0 o; N) L5 e% p: g" B, W, }6 H不同语言的sdk遵守mysql协议就可以与mysqld进行互通。' Q. X9 v3 L# K/ l
    2 A- E* G) P4 j7 w
    <2> Connection/Thread Pool: J+ o; q- H. t
    MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。& c8 D6 W6 i+ S/ k  w
    % D, |6 z3 e6 }0 G
    <3> SqlInterface,Parse,Optimizer,Cache
      j' N5 c4 D& M) E对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。6 ^0 I7 I; F, j2 c, O' e  Z
    2 N% G  O5 \  O" A& O) p" x
    <4> Storage Engines
    * h) e+ C. g+ e) k& V' `* v负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。
    ! d. z7 V! E9 u+ N6 N' d2 {2 X/ q) ?6 R0 ?1 P: h& R
    三: 源码分析( a4 V& Q; `% D5 ^5 W
    关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
      u9 _& f/ W% B4 I' i/ y
    . _/ J5 K$ a* L1. 了解mysql是如何启动监听的
    - l7 E) z- e* H5 [. E手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
    % l$ h. C3 E# {5 ~2 T5 c1 P$ @0 m
    0 O! h6 j* ]1 z3 w/ T% z3 {: g8 J7 T
    ' [- [1 u; ^3 D% b5 s* P
    . ]: ^! W+ Z, K2 D4 t, ]  E& s& }从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
    . j/ u4 H2 I) b- t7 q4 F, h' Z: d0 `3 i. U. U) R2 o6 z9 _; W/ Y
    <1> mysqld_main 入口函数 => sql/main.cc* {( B( W; L/ ]1 Z# r& p) n

    " H$ \& F' X% Y9 z7 z9 c. B7 p. K' X" c5 I
    extern int mysqld_main(int argc, char **argv);+ ~/ K9 y4 V5 J% ?  p1 n& z+ ?1 O
    1 [, Z9 {0 F5 n* U5 b) i! a% r
    int main(int argc, char **argv)4 C2 U/ v9 w' }2 I% y* N- H
    {
    * d( h# ?) d9 G! I; ^5 n4 `4 D  return mysqld_main(argc, argv);
    & k: ?& H2 m! R; O; d* T7 V}
    3 Q3 P. T; J0 \: g& V: D3 T* i( ?, E1 E; s9 k. Q! c; A  b! R2 F

    5 X- t% ~) I% b7 W这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
    ( M. f& V" A  R* l. a7 E0 Q+ h+ i' E6 R- Z( W3 Y
    <2> 创建监听2 k, k  M- ~2 R& ~7 V

    5 N* T" h. k7 V/ X" Z
    7 `. c/ {: n' m! s4 Uint mysqld_main(int argc, char **argv); b9 L0 b  s/ Y, G2 @! v+ T
    {
    / c5 u3 m% q( @, s' m2 h# {2 g( n) u    //创建服务监听线程$ E+ V. ?& B, i
        handle_connections_sockets();
    # b7 t" V% s3 |: x0 W3 p}
    4 }+ O" Q6 {, o4 x
    0 ^7 D3 j7 K# ~$ ]void handle_connections_sockets()
    - W4 i5 o/ B7 J! a% {! `{
    1 s, a' e: y) m! g     //监听连接0 l4 Q  I$ q6 r8 }* N  n9 S
         new_sock= mysql_socket_accept(key_socket_client_connection, sock,
    + I0 H  O9 p, |                                    (struct sockaddr *)(&cAddr), &length);
    ) y. l7 ^& o* X3 ?: j8 y+ u0 o' t. W
        if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
    ) ~1 }" f: E) P& ?, a      thd->security_ctx->set_host((char*) my_localhost);
    3 d3 x2 W0 n7 i4 P6 G2 Y
      c1 L2 C! F2 r8 |    //创建连接
    - m2 O1 L* c% @2 f6 Z    create_new_thread(thd);
    $ u  n% I8 ], h( z}3 w. |9 P4 R( _% o
    7 k# ~/ j; K6 k* @) P1 }
    //创建新线程处理处理用户连接* E* G: r& s6 z: x; N+ e. K
    static void create_new_thread(THD *thd){
    ; X: f& |& a% \% Y
    8 ^$ M2 j* }4 ]% R   thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;% f% y* ?6 a- y5 j, z+ [7 n. O4 N

    - M5 r3 I; w' k. s4 W   //线程进了线程调度器+ _( I+ n: |" X+ z
       MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   3 t4 U9 }+ p! K1 e: t
    }
    4 U4 I0 U# C1 O2 Z5 y2 B- J, f
    % f( X8 N; Y9 B, f4 o
    7 W' y- X9 z$ K' e8 r至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
    / O& ~# a5 z4 s' W, M0 Q/ A2 s- O% C6 s( _3 P

    ; f% E. m2 }( Y1 p$ S( R; T; Y2. 理解mysql是如何处理sql请求
    # z$ i0 p6 [  V; \$ J, O这里我以Insert操作为例稍微解剖下处理流程:
    $ J( U5 @0 D% k9 {. D; p
    1 E  Q/ S1 i# l3 V! d当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
    0 J3 }$ o8 e' `6 L/ ]8 I0 T2 [8 d" m& T* T# I$ h# N" G2 J
    8 v+ p# K7 a, ^; b. U3 P# f
    static scheduler_functions one_thread_per_connection_scheduler_functions=
    ! ?' B) q, C0 [4 g% F$ ^3 p{% L: e- S6 [% J0 ^% ~- D' h$ j
      0,                                     // max_threads# ]& J1 a$ I# C3 F* l1 h! G: l
      NULL,                                  // init) r1 n; Q5 z& V% @
      init_new_connection_handler_thread,    // init_new_connection_thread
    5 ~0 a1 s6 x( y# X+ W- g  create_thread_to_handle_connection,    // add_connection
    . a$ b- e0 a  |% \) [8 v  NULL,                                  // thd_wait_begin; L& A" B6 P, W* t
      NULL,                                  // thd_wait_end
    : Y* Y1 K/ W: J7 e  T9 Q2 p% x* D! z  NULL,                                  // post_kill_notification
    9 U5 Y2 I! A; A2 H! x  one_thread_per_connection_end,         // end_thread
    ' p) f* @" ~+ }% u, \2 C  NULL,                                  // end9 }; W0 y+ Y7 Z$ g2 M4 L2 G5 d
    };4 Z. l9 c4 ]$ b$ v' W% J
    & c7 @4 ?$ H' p3 D

    + P9 w: D$ k. o从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。9 [7 s# m1 }8 k

    : w' w" T; o+ i$ @/ ]8 j6 m3 p7 Z; C<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪, i; [0 X9 e* @% w& q% F$ p

    # X$ d3 `9 g# E3 v8 Zvoid create_thread_to_handle_connection(THD *thd). @7 E/ z, w# \4 z6 V" K
    {
    . Y" v* o* b9 g" _$ ?! a( J3 n2 d( l     if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
      W; ~6 `6 p/ w! ]+ ]: w; L. J                                     handle_one_connection,(void*) thd))){}! H: H/ ?- l& F( G+ g) F
    }$ v7 z( ^. h8 T& o# F+ i7 y  C
    //触发回调函数  handle_one_connection
    # U5 t: Q+ d+ g3 h2 B1 O7 L8 a8 Opthread_handler_t handle_one_connection(void *arg)+ }# ~4 j, E$ G% N* _& Z
    {
    . }+ _+ T& ^' X& m3 u# `     do_handle_one_connection(thd);
    / W  Z; L$ ~6 v) A( l4 h% k0 ~}
    ; N7 Q7 E4 J2 q/ r3 X//继续处理
    5 a  c- g6 x2 M4 T* d8 b) G. X+ ?( h+ ^void do_handle_one_connection(THD *thd_arg){
    & O5 G% i( U8 C+ Q    while (thd_is_connection_alive(thd))
    / {4 S+ z  B: h# \( u+ U" t    {% A. B0 n9 H8 O/ q$ c
          mysql_audit_release(thd);
    ; J; Z# b3 X: `* P6 t      if (do_command(thd))  break;  //这里的 do_command 继续处理
    , `( |: x% g  ?9 E/ Y    }) q8 E: w3 l4 F) S6 V5 n9 D# m
    }+ M& p4 y2 k6 B; M' G
    //继续分发' E6 P' ?- V* ?2 _2 ~: g; U" S1 m6 t6 F
    bool do_command(THD *thd)
    " F# \; D7 Q) ?$ s, D: R{
    3 W9 ^6 p4 A2 T% D6 P! x) }    return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
    $ F6 r; r1 u* d+ o7 O9 y}
    ( {) Q- H1 ~, ^* W: cbool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)0 F' {& ^6 H& U" ~* f
    {5 y+ i1 M7 M( v) D% U5 s6 L
          switch (command) {7 J2 G# ?& I9 }" r4 z0 v; t
             case COM_INIT_DB: ....  break;: _5 a0 C& q3 `( K0 M# `' r2 E9 E+ i
             ...8 s, C& U& V4 v+ W* u) i! B
             case COM_QUERY:   //查询语句:  insert xxxx, @' l. \8 m. \5 _6 o% n
                 mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析
    . T9 j* f  M4 e+ x0 D* F           break;
    * }* r: i4 L4 G4 u4 w* _$ h; ~7 ^9 j      }
    9 q  ^; ]. w- H  G" [}
    $ [- h0 O, D/ D8 M+ g) `1 U8 C//sql解析模块
    ) r) J. M$ g5 v+ Qvoid mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
    - H( A+ ^  j; l0 y# u* U7 u( R% \% [8 F{
    9 y. N& o& g: D: B      error= mysql_execute_command(thd);
    ! V( j1 ?+ V' P0 e  P: l" B0 {8 q0 c}. ^- W1 L4 i- n# u6 A  q

    % p5 G9 w) ]$ \/ v* k  Q/ J) o: B
    7 n* Y: S$ ]1 s2 t' b4 q- i) N<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
    8 p- Q9 C4 a* a8 b% L5 L, G& n) H# V$ c: X+ D7 F
    //继续执行
    / m5 B+ n  C5 Jint mysql_execute_command(THD *thd)
    - ~4 [- M% q9 O3 `( X3 J{
    / z2 P1 @! m* K! F! h  switch (lex->sql_command) - L' R6 x# U' {$ @+ R+ p. s( W
      {5 W5 a& t7 k0 F7 Q+ i
          case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;
    + b4 g. \- K, w& Y2 h1 n, F$ M# Q
    ' I& ]* {# u9 `9 L1 O" ]      //这个 insert 就是我要追的: G$ P3 J" \" ^
          case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
    4 W! |; g% _$ ]" l2 J! v1 A  J- V                                              lex->update_list, lex->value_list,! O6 t: c# a0 y, ~9 R& T
                                                  lex->duplicates, lex->ignore);
    0 B/ n( e; B0 Y& l& h% h  }' v9 x4 v+ e" J8 x# J) {4 s- @. S
    }1 ?2 a$ F$ x1 k0 Q5 J0 ?
    //insert插入操作处理5 o* ^7 e; h9 ~2 j* h' b! r  _
    bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
    / S% G2 K% t0 B8 r4 U, {7 M                  List<Item> &update_fields, List<Item> &update_values, # p& {/ e, K/ C/ q* h- @- X& k
                      enum_duplicates duplic, bool ignore)
    + P* J$ A* c1 W; Z{; ?* N) l) Y$ o9 A8 @4 n
          while ((values= its++))
    9 x" M4 j+ J/ l$ m$ x7 R6 \      {  R+ d6 \7 d/ S
               error= write_record(thd, table, &info, &update);3 q2 A( p' @# T, J& z
          }! A2 w/ c8 @" X: g
    }/ o: L6 r5 t; O' ^# x3 x
    //写入记录- g! w0 @, h$ @' s0 d3 i2 t
    int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
    ! U3 C$ m2 r! W( ~2 l- t- c% t{
    9 T4 }- j4 y: L9 V- D) R    if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)( F+ ]% l5 }. {: U+ N6 @$ T$ ]
        {1 J8 \* [- U5 M6 \
             // ha_write_row  重点是这个函数
    7 W3 p& g+ ^# L' l* [0 X9 r         while ((error=table->file->ha_write_row(table->record[0])))) B9 b) w4 P  E; }/ F" G0 @" i$ V
             {8 `% l: y# R0 c, K, r9 Y
                 ....5 ?0 v% P6 k' S  C: Z4 K, @
             }
    # A* ]# u$ Y1 M" d$ j    }! F9 {$ e8 N5 m/ {
    }
      l  _% A% f/ C6 @  C$ A: P) \7 i6 J& w/ p  X( |: h& I; H
    2 m0 M$ @' |9 ~; J7 Z% c
    : ]3 ~$ j3 G. j$ o4 m! ?, k
    可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
    ' G6 E9 H9 J/ c: G, |! `5 p8 r6 r) n" @+ }2 t3 w4 d1 O& P
    <3> 继续挖 ha_write_row' P4 Y1 {5 {' \: z

    4 r* H8 n- I" ~int handler::ha_write_row(uchar *buf)
    * Z0 }8 _# Y* I9 b; O0 N( ]{
    ; V: \) l5 T- x: d7 t    MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
    7 `6 Y: R8 u: E9 }9 M- Y}( W( Y+ Z  ]6 z. S! W8 |
    ' h9 w6 Z3 k. W, @( ?2 J) H: H- O
    //这是一个虚方法
    0 S  [' |/ N; A: H  @( ^virtual int write_row(uchar *buf __attribute__((unused))): \. r/ c& `/ N
    {
    + _' q- g' `) a! T  ?% F    return HA_ERR_WRONG_COMMAND;
    % i7 h+ ~' D5 K+ z# B; P}
    2 H% u! q5 s8 ?4 x) z# ^+ K; S& t' ~9 c4 y, E# ?, Y) l/ F* S

    ) y7 p  G3 f# G0 [: L. B看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;+ W: ]$ W1 J" \# c/ S$ I! G& |, i! w

    6 L/ S0 g: I$ k0 I' x3. 调用链图. i$ b& |7 i1 I8 C
    这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。% M, P: g) \: {! l
    % s) `, f# l& L* j
    + t- ]- @# W/ l4 n* s

    $ e% P( V* Q0 m8 S/ F三:总结
    ' @6 }: n3 @. B& e  D+ C' m% J大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。
    , P5 Y+ d8 O9 h6 @+ t* i+ K3 ?————————————————
    / g/ U* w3 z1 L9 R! M, |版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    . ~, v: ^6 L: W* y) D+ h. z! s原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
    1 c+ ?2 ]$ F0 q3 S, S2 d; i
    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-11-17 07:22 , Processed in 1.291030 second(s), 51 queries .

    回顶部