QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3531|回复: 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
    一:背景
    - N- j& _- ~2 z4 u* p1. 讲故事1 }: d7 r$ A% g* b; j1 H
    最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。* L  D8 {2 E: G" {# v
    8 L3 Q# w3 ]& R
    二:了解架构图; h  I: t. m6 j
    mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。6 d- c: a+ w8 l% Y
    $ d! N. u8 w/ N7 S8 p) G" |5 d2 ~
    1. 从架构图入手8 o$ w% U# _- k
    大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
    - B( U# N! g' {; J' ?
    ) r; p  u/ h( A- u3 G" _% `( c, F- u

    ) r, C/ X8 O* Q- R7 Y) {1 g" h其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~* z; Q" w* t9 o1 L

    $ l4 I6 D1 }& B2. 功能点介绍
    6 p2 r' I; A9 \MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。: W: P7 j. ^; N" r- `/ \0 H  D
      d& c+ C, q$ i: a& X: x. Q
    <1> Client
    / n- O0 }. d6 e" m, w6 M不同语言的sdk遵守mysql协议就可以与mysqld进行互通。+ w: k- X. C6 q* x5 I9 W# g" B
    , c" y. C1 Y2 m: k8 n& Z
    <2> Connection/Thread Pool
    ( M. ]6 l( M. e% Q. j- z! a+ aMySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。7 y4 ?0 s. Y2 b; u* p, K
    ! @0 D$ S& V3 E% l
    <3> SqlInterface,Parse,Optimizer,Cache9 @0 X4 \( j  ]  h- F* Z" G1 {9 [% G
    对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
    5 D+ n+ J9 S! z! M3 t  R& g) k* n3 g6 @3 r
    <4> Storage Engines; w9 b2 c, W+ E1 p3 D! x
    负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。) S5 s5 @, W/ w, L$ Z

    7 |8 [. J5 E7 M+ [' `. t3 X三: 源码分析" i) [- X/ a2 V2 U
    关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
    + o% {/ z; ]1 Q0 o
    3 I* t$ O# Q) |  \% _1. 了解mysql是如何启动监听的
      l: r8 M$ {7 B6 w! ]7 S手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
    & _* N: t! _: K( R  M' w5 c" m  Z1 }" @* Z! r7 ]

    2 A# W, U0 V- L9 B% |7 I9 v3 _* j# v3 ^) y% a) j
    从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。% ]. U/ I/ m% I& H9 E1 @1 p' m

    7 Y6 S' Q; F. Z& J  ~9 g<1> mysqld_main 入口函数 => sql/main.cc
    9 y8 S3 b% s* f% h0 q
    - U$ I% ?& h7 v+ X
    1 t' t$ I8 ^( V0 }7 gextern int mysqld_main(int argc, char **argv);
    6 ^& {$ a- x& v- ]% w5 w4 K0 \3 s0 O1 ]( Z/ t/ W+ p
    int main(int argc, char **argv)
    2 s2 E1 O5 t3 k0 q  H# }. P" i2 I{
    # c% P4 i1 O+ S& n( P  return mysqld_main(argc, argv);
    / \- k. {, L0 C- X9 L! w% `}% Q1 a( Q. D# p: u3 \( r" n4 V

    & N) H4 a( j# f4 H
    8 ?9 N: |, m) f' y7 D# [这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
    / t5 L1 p, q% ?( v. S3 }- B3 b
    ( P$ i# @; {4 @) o* ?<2> 创建监听3 ^% F, O3 ?0 M+ e3 w( T" b( r% X
    / g' w& _% e$ ^- \/ H2 k

    3 Z/ X( i0 i: Aint mysqld_main(int argc, char **argv)
    7 d. k& r: p6 K2 U6 _{
    3 b. _8 |6 T' C# `    //创建服务监听线程. [: [  K- b; U
        handle_connections_sockets();
    % T1 P" Y; L% {}; h' p; h& d, \- g
    ' o- Q7 u' q2 D7 U" m
    void handle_connections_sockets()" f, V) k) l( ]  Y$ x
    {5 }8 `2 C$ k% n5 \- g9 n9 z. q
         //监听连接
    ; U% N$ t* u  F     new_sock= mysql_socket_accept(key_socket_client_connection, sock,+ |9 X$ f) H$ l' E  w
                                        (struct sockaddr *)(&cAddr), &length);
    + N7 n: \5 Q/ L" n; [% [. X3 o/ D6 u9 g% S5 u- [7 B! ~( w
        if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))% s6 [) r! {3 b7 ^( h+ w
          thd->security_ctx->set_host((char*) my_localhost);
    ; h4 P$ w8 ]" E+ X' W0 V# G$ {
    3 F  |' B! P; c. }/ ]2 E. T    //创建连接
    - B7 J8 ?- M$ z1 v3 ]6 Y* u8 D' }3 f    create_new_thread(thd);( v6 f! t5 H% c# [/ c: J% }4 D
    }5 A9 \8 H9 ^- ~" \  W; Z4 }) Y) i
    & S$ W$ h+ H/ x/ F# e
    //创建新线程处理处理用户连接
    % D' s! N* D% K% n" h3 ystatic void create_new_thread(THD *thd){
    . f( P, m, B% C# `4 X: H4 b8 l2 R  p  r" U. |* l( ^
       thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
    7 u/ h, c/ c3 ~1 K! @8 i8 i0 c9 D: j& Q2 h- x: S( N1 ]! C! Y# d# [
       //线程进了线程调度器
    + S4 c- h5 [7 n* \: C) s   MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   
    7 M! Y; Y- X  b; o  w}2 \5 @2 y  E" ?+ u( ~5 g
    3 H5 p3 i8 Z5 s  K; |, S- M
    7 W+ F( e8 @# |, K
    至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
    $ N  q$ O+ z% b& \
    5 z" j/ A- P& z/ O' i5 ?( U7 }
    - C- H  j9 m! Z( N' `0 m% `" i. B2. 理解mysql是如何处理sql请求
    2 X$ F% R* v* A* ?3 ]$ w" F这里我以Insert操作为例稍微解剖下处理流程:
    # y" B1 w% M# L  m5 z  g: U, X2 K+ X# p2 P, m+ a
    当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。0 m- n- `0 Y$ E: x# ~% `$ @

    . v- V+ u3 u$ a& H) l) A! {; R2 ~
    ' e- U% a5 u, _% r) gstatic scheduler_functions one_thread_per_connection_scheduler_functions=$ l3 r, s) s; `+ X
    {4 H9 `6 e. ^- E: m" |. b6 U
      0,                                     // max_threads
    : @& C, B* Z1 E+ r5 y& g; ~7 i/ r  NULL,                                  // init
    $ r- Y7 b) x$ h' x! S* y  init_new_connection_handler_thread,    // init_new_connection_thread
    , a  p, w' P' ]8 |5 W8 Q- Y  create_thread_to_handle_connection,    // add_connection- ]  {' H6 e/ c. @% h6 S
      NULL,                                  // thd_wait_begin
    & _5 I! K- ~8 d6 @( Z. Z1 I8 N  NULL,                                  // thd_wait_end- d# ~5 ?+ Y5 Y8 L6 w7 _0 n+ {
      NULL,                                  // post_kill_notification
    % u: \$ L; ?1 |0 h! {! _6 c  one_thread_per_connection_end,         // end_thread
    8 E+ [9 D& V' P+ Q5 A5 u3 _  NULL,                                  // end7 X  G$ ~: T. y. s9 T
    };! ], F. p8 R8 ^$ T  _

    * k  H. m, k/ r2 n7 p
    0 a; \. {+ e- M从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
    . @. |8 _) l. ~' s+ E- a  \" ]7 I% g8 y; A
    <1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪, d% N7 s+ m7 O' ]+ v/ M
    3 T) d$ |4 p* K( ~- Y" g  V4 Y# J
    void create_thread_to_handle_connection(THD *thd)
    ( X4 B. @% Q& x( n6 x" e9 @{! y7 @+ M" w% g4 g9 I1 t7 }
         if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib," _( F% G+ M; u4 d9 \0 z0 R% v6 r
                                         handle_one_connection,(void*) thd))){}. r! N1 n4 ?4 x0 q! R5 q" ?
    }4 R% M- a2 a! G
    //触发回调函数  handle_one_connection, W2 @: R: x: l4 _$ E
    pthread_handler_t handle_one_connection(void *arg)+ |+ I& n# s" }$ C# Z& `* K. j; f
    {
    4 u+ G3 R6 |* X$ }5 E# i9 i7 a/ E     do_handle_one_connection(thd);
    * l  \/ u6 k# H: `}$ O% e7 i+ M% A; r3 W1 |" Z
    //继续处理2 ^) b4 ]5 i" |
    void do_handle_one_connection(THD *thd_arg){
    0 @0 {7 q! ]. V( q    while (thd_is_connection_alive(thd))
    ! y2 q- G, F1 o  g9 z3 [% H    {! K; t# A" v7 N
          mysql_audit_release(thd);1 Q" {  Q4 w3 o% h8 `
          if (do_command(thd))  break;  //这里的 do_command 继续处理" K$ r9 @, P. O2 V
        }
    6 ?6 M7 e. ~' q7 V# j# s( }}% F# s5 \! |9 U, H
    //继续分发  O  Y7 s- {# c$ b6 s
    bool do_command(THD *thd)
    $ x5 E9 Y( y9 b{
    . @* a9 t) o( V, o. Y) f  P    return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
    5 p: \& D0 c6 [: y}
    & `* M, t/ q0 Jbool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
    * i  {' @, u' y& r. S; M1 a{
    $ h# Q) D" |  F$ u% v+ T3 l      switch (command) {8 y. r" e/ D! K$ Y* W, u
             case COM_INIT_DB: ....  break;/ T2 F1 C& V* [, T" |# R( {4 r$ D3 X
             ...
    / c' ~7 Y- x$ A4 j& O         case COM_QUERY:   //查询语句:  insert xxxx. x+ b3 L: {3 @( q. K. \! _& O
                 mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析, b+ x3 l+ `1 P: Q. \
               break;6 x. T$ i+ K& _
          }. ?& k% D5 c- \4 ~* a
    }
    * p3 I( M  j' P8 b//sql解析模块$ x0 i) x8 i* c
    void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
    , E/ L' }5 h7 t) v# K{
    * ]7 |! g, z8 P" O6 v- x' T( d) c      error= mysql_execute_command(thd);
    ! R9 J8 W) K$ a+ {9 v* F, H}
    4 x' O1 d5 B/ Y$ k# M. C! k
    / @- B5 c4 [* j9 f3 t- L2 ?  v. R9 y" b
    <2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
    5 R! |( q1 r$ S* w5 q+ d) S) Q* m+ ]
    //继续执行% T0 }: u3 ?/ K# z! T
    int mysql_execute_command(THD *thd)
    7 O7 a# d" {" M+ F& `' K" H{
    / W$ g! t& w, {! G+ a  switch (lex->sql_command)
    ' v7 w1 Z  u% g# }# X  {
    8 s, D0 s# `) k2 R( Y      case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;) ~# E" ?* Z2 f  |% ?0 S/ M
    + L" D! v$ F, o+ [; @) E! O7 V2 z
          //这个 insert 就是我要追的2 P+ X; z, R: k' n! b
          case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
    0 b! |7 b# u+ ?: f( }                                              lex->update_list, lex->value_list,2 H# u6 G! ?+ [  I3 x2 U
                                                  lex->duplicates, lex->ignore);
    + Z( M8 u1 i3 O4 K+ B7 O2 j# a  L( E  }; ?, I. v% U8 R7 I5 P' B
    }
    3 h& c9 a% H& e% t) Q//insert插入操作处理7 }& F' t; [6 m5 h& }
    bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list," d  a# e0 y0 t# p( k! P: D: |# {
                      List<Item> &update_fields, List<Item> &update_values, / _' }" W, k8 w+ b
                      enum_duplicates duplic, bool ignore)
    . `0 }* v0 f4 d$ N1 b1 k{
    3 d: L4 o* v5 T% j" _) p      while ((values= its++))9 P$ W, f& E5 U7 _/ Z, x
          {
    # i8 ^4 d7 P1 g* m5 ?3 h9 p           error= write_record(thd, table, &info, &update);& ]; @7 y. |+ \) @9 S# {
          }
    7 E7 j3 \4 j, V, v& V}3 d  i( W( N' V
    //写入记录
    ' |* `# @% S+ V0 l/ W, `int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
    4 W" g$ ?( |# K) Y' Z{  @9 l/ x. j% s! D% C* h
        if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
    ( K2 O: i2 ]" _; J7 Q1 o    {
    $ i3 F, S2 j  K. r4 T         // ha_write_row  重点是这个函数
    7 Y' A% f0 l! g. h7 d4 w6 k1 r         while ((error=table->file->ha_write_row(table->record[0])))/ s, o) ~8 ^8 |' M, V
             {$ ]/ f, c7 Z  X& n
                 ....
    # w6 ^; t- q' m0 h7 i! ?9 {0 N         }- |! Z( z& ?2 D+ F6 Z  o. h
        }
    " ?) ]0 x8 M. f1 Z$ K2 [1 X}
    6 Z- g0 p0 E, |6 y' M. @5 B& o% b# Y! {9 H! I
    0 y5 I- O- j5 e; Y1 x
    : k6 h& l. z% P4 _) ?' ]
    可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
    ; L) t, M& K1 ]/ S$ q" ]8 E& {
    % `. u" M2 _! L<3> 继续挖 ha_write_row
    ; C; C/ r; r5 k6 X9 K3 ]" K3 Z" s8 F! u, E6 ]& v0 M
    int handler::ha_write_row(uchar *buf)5 V% N' i' ?" |  `% h
    {
    ( s$ y/ k4 K  o0 k; Y    MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
    , `6 T0 _' `* r  q9 j}4 _- C) i  h& H* B* j
    ! e6 s4 p' _8 s, n; @
    //这是一个虚方法
    $ P( v: h/ W5 Y# W: m# ovirtual int write_row(uchar *buf __attribute__((unused)))
    6 C1 B( ]0 o6 q4 n2 }2 }{) k9 |6 \. W0 ^: i
        return HA_ERR_WRONG_COMMAND;4 Z% G" ~5 d/ V1 A
    }$ r4 u/ A. y9 Y1 _) h  d" X1 X

    ! y9 @, M( `, J! Z
    ( w0 t) B/ L0 u: b看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;) U; q: v$ t3 \6 J1 h
    8 Q3 s% Y2 Z$ r8 V$ s
    3. 调用链图
    6 x$ p3 V7 f4 I0 F5 i+ c5 ?; _这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。; Y, W; @: \3 w' @2 j2 u/ B4 L* n

    4 m$ z6 e1 b: v$ L7 v' B, b0 H) [4 n) _, V
    , ~, o' b' H6 a! }3 E
    三:总结0 o; E$ }+ ?) ^3 ?3 X7 i+ p7 p
    大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。+ n8 t( [& _2 ?" a9 @
    ————————————————
    ) m% T# \. v- I, h7 g+ y+ c0 \版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    ! [' X, \) r& c) c原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415( }* p, @8 ^8 {: K% A5 h
    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-14 19:53 , Processed in 0.441007 second(s), 51 queries .

    回顶部