QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3536|回复: 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
    一:背景* g/ d+ z! h$ N; _& o8 ?6 p
    1. 讲故事
    6 T! g. u5 t; q最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。* ^: M) `7 @. U8 w
    7 d( r( w9 ]1 h- w+ e; ?1 h2 ]
    二:了解架构图, j7 l4 U) l2 d8 W2 l7 h7 C; @' d
    mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。: ]% E, p6 F% M; J; E

    ! b1 G5 k5 P, q/ o) Q5 _3 A1. 从架构图入手, B% `' q7 u) ~: ]  F1 m3 T
    大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
    8 s! E. R$ r& Y
    + ^) W  X! {! `0 \  c4 U8 u! ?7 L/ `+ l

    9 \1 g% R4 y- L% h- i4 ?5 P其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~, g1 j  M* o9 R% _+ u' r6 ?! l, R- g

    - [+ t3 @4 s) J6 {2. 功能点介绍# n* Z* f: ]: S
    MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
    2 M2 B1 S, o' i
    ( C$ b8 Q% B! k0 s4 x" v<1> Client
    * n  C9 T3 E& Q$ M! A. u  D不同语言的sdk遵守mysql协议就可以与mysqld进行互通。7 }( g- s! [0 M7 z. i+ A$ y, w' J
    8 W* m! `( S2 w' m
    <2> Connection/Thread Pool% G4 p9 o; m5 K) g$ `0 t2 e6 z7 B
    MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。, _$ R: h9 B7 y7 }( e! E/ G
    & k3 @  t6 N' \7 q" Y8 y7 J, ?
    <3> SqlInterface,Parse,Optimizer,Cache
    ' V4 Y! F, j$ [0 C. {! u对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。- g) u8 w% s6 g
    ! B  v# |4 S/ D7 j1 t3 M0 M' j2 D/ c
    <4> Storage Engines- ]* S  ]4 ^( ^, s  g
    负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。2 q# A" D# L1 B6 Z! s! B
    ) }) F: a& r! h+ c3 k8 a8 @
    三: 源码分析
    , Q# q" y( D+ ]7 T7 t关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。  H2 ]& s' o4 m9 z2 V
    , c2 V# \0 y; i$ e, G& q6 R+ H8 H
    1. 了解mysql是如何启动监听的* o5 ~% D8 B4 E/ b2 I$ T
    手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。2 U# a# u  p( c& k' U  l0 V, U6 P
    ! M  I( i( N6 s0 Z+ k8 I
    0 V! n% a! X' J4 k

    6 V6 O  J' F0 f( l+ y) |! f5 `0 V从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。3 q8 l2 E4 s8 O0 A

    : L0 K8 G6 Y- x& P<1> mysqld_main 入口函数 => sql/main.cc) t! y& Y& F, T: O. x! {
    ; b8 g; [" C1 ~% t
    % e7 Q% ^+ L2 |% r3 n, H* T% P
    extern int mysqld_main(int argc, char **argv);
    % R$ j* G, `6 i! h4 v2 J: i# Q: x, e( x5 \( q  f" x) N
    int main(int argc, char **argv)
    0 t  I& j  n' a{
    , k8 g2 \3 G7 V* d: i+ M* `$ K  return mysqld_main(argc, argv);. R4 |. `& g0 U6 O* j
    }9 D# D3 c1 z$ |: }
    / C  k6 X" I" X" b
    6 y6 |$ `9 n3 T: v) `2 p7 q# u
    这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。  I1 q$ ?0 Z! @; M- d

    ' O( Z9 [4 `% _2 l8 c/ p<2> 创建监听
    8 ~4 W* p1 H% F' F' W" ^% y! B+ b' k! X  J5 L4 q2 N9 s6 }
    4 Q, L# f5 R  k" f" m% H
    int mysqld_main(int argc, char **argv)1 e0 x& Y) P% V  T6 ~" e2 g
    {: \( N( x6 ]3 h  O" C$ u
        //创建服务监听线程
    . Q% N( `/ [6 Q( v" i2 X    handle_connections_sockets();
    $ d$ Y5 R, C9 z( j9 F. t+ b( e}8 t% Q- J' B- f2 f* {3 a

    * V. V/ b& J1 y6 K  wvoid handle_connections_sockets()
    0 y5 H6 Y% ^# g. k{, @+ Z+ x- o9 H$ Y) `5 `4 i  {% }
         //监听连接1 j; I- L) m2 G# M5 o1 m  q$ z3 p$ ]
         new_sock= mysql_socket_accept(key_socket_client_connection, sock,
    9 p- S6 b6 A5 z( z1 D                                    (struct sockaddr *)(&cAddr), &length);
    2 Q2 A% q' ~" W' t
    ( X" c+ a9 P) @; n5 w9 }    if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
    ! O. f1 t. Z: m8 m% d      thd->security_ctx->set_host((char*) my_localhost);
    " f1 }* N. g3 l: ~/ P# I. e$ g$ d. o; A1 m. E# D: T0 T4 h
        //创建连接
    - ~$ {& w: V( x# |! l' {5 ]5 }    create_new_thread(thd);+ h& N# b5 z# D- K2 ]
    }+ e" w$ x+ A' ^0 [8 @8 }8 Z) c
    & o4 R! b+ F' b( v# `
    //创建新线程处理处理用户连接, O/ n) k" @8 q
    static void create_new_thread(THD *thd){
    1 \& Y! ?8 _& G+ a3 e+ C' I& S' v% c$ o; f8 D# o
       thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
    3 a0 u& _" Y; n" h5 v7 z- q' v: @  k. [7 F  N) s. r
       //线程进了线程调度器
    ( e; S5 V; s( B7 O$ P/ i   MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   ; N6 q8 b$ d4 Z
    }
    3 y' O: F6 l8 R4 x2 R' P
    $ s, [9 h- f' H; r9 y6 ^& `& s! {# w; S/ j+ b: C& P
    至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。% o' y1 V4 u& m; l/ n* L3 ?
    & J8 s1 V$ P0 f: s% \% h* q
    5 K* e$ X: t4 r" Q
    2. 理解mysql是如何处理sql请求
    0 I2 @5 d6 [) S3 q% b1 C这里我以Insert操作为例稍微解剖下处理流程:9 e- W: z5 U% W8 R" @
    5 Q- S7 n: e3 H" i" A, O* |
    当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
    5 r0 Y6 [( Z2 O7 y5 W/ u; O+ T- T7 I  R
    0 Q' G' R! I# S9 J( x
    static scheduler_functions one_thread_per_connection_scheduler_functions=6 F( {* {7 Z! |5 B" m+ }. I
    {* ?8 z( h6 v' g) v1 j9 Z, |7 G
      0,                                     // max_threads
    9 p) l0 Y7 K; x- {; T- E( c  NULL,                                  // init  S5 [4 u2 d- O( R$ \' m2 N4 a
      init_new_connection_handler_thread,    // init_new_connection_thread+ W1 H: G  N( ~2 o7 G: I
      create_thread_to_handle_connection,    // add_connection+ d5 E7 p3 S/ {: G; E7 K
      NULL,                                  // thd_wait_begin9 V& W9 F2 o$ b9 ^* D& q% b
      NULL,                                  // thd_wait_end! B0 q: G! B6 a  t& D) t8 |* I
      NULL,                                  // post_kill_notification0 v* g- I: z! x
      one_thread_per_connection_end,         // end_thread
    + P/ `3 [4 x+ C; B: ]7 t, P6 \  NULL,                                  // end3 c' u! ?7 ^- A
    };
    & n. _& T! v8 f- n% ^( i+ ~
      D% r! N; L# @. r* a! `6 i/ ?( O( x1 e* ^' }4 _
    从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
      T0 [, ?: W+ D6 X0 l: B6 ^- G0 L% V
    <1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
    # r5 Y$ k7 k# S1 G
    * g; A" B2 W8 e/ a  L  nvoid create_thread_to_handle_connection(THD *thd)/ s, q7 D: K7 q
    {
    5 O0 o7 z5 f; `. L% _9 Q) S     if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,4 }5 O7 i: n& @1 P6 B
                                         handle_one_connection,(void*) thd))){}
    * U" \' q, l2 I$ k}
    * }; `! N1 L1 |% M# {5 P//触发回调函数  handle_one_connection
    + Y, X. ]2 d/ a/ G% I' T7 tpthread_handler_t handle_one_connection(void *arg)" J0 M' w% U1 a4 d6 q
    {
    % U4 `) q$ t6 E0 E+ L8 m     do_handle_one_connection(thd);
    5 `9 S2 H* _5 q- x}2 u2 h9 W! D9 x  H5 w5 e6 p4 p
    //继续处理
    ' Y9 p. G! B, W  _void do_handle_one_connection(THD *thd_arg){. ?- ]" |% Z+ z% g  A4 X' r9 P
        while (thd_is_connection_alive(thd))# O% U/ q: n; d2 G
        {' E0 d9 Y1 m: A& K" _
          mysql_audit_release(thd);' @4 [* Y) q( x, ?7 A5 q5 O" J
          if (do_command(thd))  break;  //这里的 do_command 继续处理
    4 H6 a* Y1 ]# E( ^5 k& U    }# m/ A' \; E! }+ m( Z
    }
    3 r: Y3 F5 V8 g6 _" {* h//继续分发9 m- O1 Y; Q5 ]5 [- \% b
    bool do_command(THD *thd)0 c4 l- f  _( y# b" X; a
    {
    8 \* M6 u7 J: `4 U5 G  m# W    return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
    " F5 u& ]% P6 Q5 X( ?+ O# V}3 e$ b9 j4 h6 s% p* x3 r  D- i; k
    bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
    / U# a$ f$ e2 O% H7 x5 C5 B{  B, u) }2 N( |0 n% \0 o$ U! w
          switch (command) {! o( q1 B+ P5 U) u  C
             case COM_INIT_DB: ....  break;3 M* m) t+ a( k( [2 @
             ...
      v* Z; s) h1 b7 L8 T( A, R( j         case COM_QUERY:   //查询语句:  insert xxxx
    9 z5 c2 N( }8 D2 ~+ c             mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析
    & j. h5 {. _$ w3 q; Y           break;
    ' o$ W2 c5 d( q      }, R/ N/ U* x9 Q
    }
    % L; z- B, ~- c* v: A; F, f' _8 a//sql解析模块
    , k) A4 t3 A% I! @, @void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
    ! p/ }! A4 z; ^{0 ]$ E  x( T" T) K" _
          error= mysql_execute_command(thd);0 F# D) {( P) f/ L( X
    }1 D' u) O4 a% Y  w5 @
    # D* N, @" Z4 \

    4 B5 i8 `; ?% y2 Z<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。6 f' r1 _! O( m9 I& s9 Q) ~+ @# t

      s! {. i5 O; q/ t" Z& Z+ P# }7 W//继续执行
    % w/ w; i/ K8 i) B% e7 qint mysql_execute_command(THD *thd)
    % g5 \$ |4 ]4 Y{
    $ K8 g7 G( O6 B) G2 M  switch (lex->sql_command) 3 g7 b$ X# w' O( X+ s. K, {& Q) O- d
      {0 O  H' @! n4 q8 h/ @; S2 a
          case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;/ I4 S5 a) \4 ~9 h7 P

      J' }" I+ ]8 b# B0 v      //这个 insert 就是我要追的, L) f$ e" f' z% ~; y$ _- Q
          case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,& d  N2 Q# \; s
                                                  lex->update_list, lex->value_list,
    1 o4 ?$ F. E+ @                                              lex->duplicates, lex->ignore);$ P  j, u+ s$ J& X8 z2 w. p
      }
    - ~' x! T& d- Y}: ^+ E+ Y9 E1 n! k- ^; F  f
    //insert插入操作处理% x! F9 S6 r/ F; v4 d- ?* {
    bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
    6 |# C$ @$ K: n9 I3 Z0 Q' I                  List<Item> &update_fields, List<Item> &update_values,
    * M, U2 _9 X- A+ c0 X4 R" i9 P                  enum_duplicates duplic, bool ignore)
    7 c2 A2 p! i$ K% ?4 A- ^{( D6 F# h4 Z2 W+ E8 _
          while ((values= its++))# |/ Q  l: {5 i$ L3 @1 A
          {
    ( i* W4 n+ r% n5 o) B+ j, m9 c, R           error= write_record(thd, table, &info, &update);3 h" V3 N. i+ M
          }5 t1 @3 l+ o0 D$ P  t: r: `) t& S
    }( n3 i2 c+ v3 L! n
    //写入记录* M7 }" K& G# L9 y
    int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)- K7 G# X3 I  J  T7 q: ?& w
    {! r9 ]5 j& [, }4 O4 Q% ^
        if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)6 c' R# G5 h1 U  q3 ?' a4 i
        {
    " y- t' a1 a1 h0 T         // ha_write_row  重点是这个函数
      ]4 [# x. y. `, I3 A         while ((error=table->file->ha_write_row(table->record[0])))& p! l0 {, z2 N/ C' A
             {3 R8 v/ D8 `( y5 p! T9 G- F( ]9 f
                 ....
    ! _: H! j- P* l7 b0 {6 ]/ Z- U         }
    . c$ t6 o& C. |/ l  Y0 R    }* a( U/ N  Z' E5 [
    }
    , `2 f, `; d1 c4 N% g2 p
    ; }' x* x% @, Y6 {1 R
    ' O. _' Z2 O8 F0 J: z4 g
    5 ]/ I+ m7 ^9 S) z可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
    % ~4 M4 ^, ~  E( H7 b) u6 `& y/ F8 Z) s
    <3> 继续挖 ha_write_row5 X; Y/ n8 B  O) K2 U1 I
    7 A& ?" @. {3 z
    int handler::ha_write_row(uchar *buf)" n: l) q$ X" L& |
    {; Z8 v% C2 s  J, O# ^  y7 v
        MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })" E. D) z7 K) T/ ?4 M
    }
    4 ~6 C6 P  H& _/ q7 o1 O6 @6 g, s" I8 \
    //这是一个虚方法; U7 @! Z: u/ ^/ w# Y
    virtual int write_row(uchar *buf __attribute__((unused)))5 F& L7 Q: h7 z
    {
    ) y9 c) @1 W. i3 ]    return HA_ERR_WRONG_COMMAND;
    # H, Z( ]; d. _, h5 q+ E$ o8 K' u7 \}
    # q" K2 d! E% ?6 }# [. H" X' ]) r  d% {" W( R

      b* k$ `7 b3 A7 P# V4 z看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;
    $ {1 E/ j3 j" a: I" b: a5 M1 R" w2 o5 n
    3. 调用链图# J  X$ y( G- q4 p$ A
    这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
    / l5 T  t% n) i4 J; j+ R3 {
    / O: T# A3 X7 v: z  r" N3 k0 p/ B) F8 Y$ ?- W

    % `/ x3 P$ f, Y9 D) D3 I三:总结/ x, h1 h0 T7 m! C9 i# b( e
    大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。1 F( f4 P' d% u* H5 t0 |. M
    ————————————————* O+ A/ g# x, W# s6 o! o7 r9 W
    版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。6 J+ G/ ?4 ]5 ?7 b
    原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
    ' |4 O+ Q2 @# s# T* j) K* Q
    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 14:58 , Processed in 0.410179 second(s), 51 queries .

    回顶部