QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3521|回复: 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
    一:背景- o9 W9 u% l$ ~' v# q( g
    1. 讲故事
    6 S3 K7 F* j4 O* ]# H5 o8 P9 S: F最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。- L; h- K" e$ f! ]/ u; E8 M0 z
    & b) s3 P: m  f* B$ I
    二:了解架构图1 w0 `) x* w( k: `& C7 F
    mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
    5 d7 J2 u! O8 |0 t& |- U! y( U- O) i+ q( N# l# N" o$ u
    1. 从架构图入手
    9 Q4 u# M9 c! }+ ?6 V5 u9 W大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
    2 [% s$ W+ L5 u9 }: n% n7 Z. p+ {
    : d' y6 n" I/ ~. E
    : Y; z. m1 C7 f1 _4 l0 g6 P
    4 _  L6 Z, A+ f( D: T其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
    : w7 b& e' A; }# R, c3 e
    . s# Y; e7 v, u# q9 O  {' A2. 功能点介绍
    2 ~5 t8 K) ^( K8 u  tMySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
    % L; K2 o7 S+ w$ E" t+ H( ~( ?
    <1> Client
    ) A* `* J! T1 d( K不同语言的sdk遵守mysql协议就可以与mysqld进行互通。/ A8 @# Y& }( p8 E" M  Q# U
    ) |; d3 i+ Q1 Z. x5 }/ {! M& u
    <2> Connection/Thread Pool
    3 X: R* ]; r; _5 A7 q  \4 aMySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。* c- z2 o# p, Q$ |5 q8 r3 k) p- A
    3 v0 S3 u0 Y- P1 R6 |
    <3> SqlInterface,Parse,Optimizer,Cache
    2 e$ c2 l9 n$ K0 Y/ x- r对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。. H" D6 c7 m9 r4 T5 R

    + p' l3 X5 s+ I2 b' \  d<4> Storage Engines
    3 K8 `; N2 ]* n) @负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。! C! K0 A' K  S8 K2 _( u

    % Q4 o8 j' ]& Y, L. X& i3 ?; D* A三: 源码分析
    / V; @0 `! \- Z: b- b: S关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
    , N: K: N& q7 `# d
    + E* Q6 F1 }6 @+ F5 @1. 了解mysql是如何启动监听的( O, R; W7 ?1 s4 o7 @
    手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。1 J  T; B3 l  Q% V5 l
    0 h9 c& U' W! d2 O: I; K, ~
    : `  f9 W) `9 |8 S5 E2 F
    , [! v; M, R6 z* n+ u
    从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
    : n& v+ K) G2 Q# w+ h
    ' i. N/ P9 _, J' \/ A% T6 Z<1> mysqld_main 入口函数 => sql/main.cc3 H1 n' i1 t4 ]
    ( L- x: I( R% w: e6 N
    , n! A: d. f9 K3 B3 _4 A
    extern int mysqld_main(int argc, char **argv);9 P* s+ W+ M7 u( y

    # F- A& U4 B4 d: B6 ]; c/ n& nint main(int argc, char **argv)
    0 ~5 Y* G3 ^0 g" ~" N{
    8 _% l* y/ ~  Z' _5 r" }' h3 y* o/ M  return mysqld_main(argc, argv);
    8 w4 F! S8 T0 C1 l}
    - a  x( e5 h4 {; |
    6 F* a  A, M$ H5 ]5 O8 l. R) W
    ; f. y8 L7 G0 y5 }4 a( O" g这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。) u( K: ]* _7 s9 N* m/ [4 N
    ' P" `" z( i2 K& C
    <2> 创建监听+ Y2 h% S$ x: [" C: [

    $ @3 @- r: L' c
    $ B) F& D0 {+ T# @2 _* p3 |6 M- eint mysqld_main(int argc, char **argv)
    % C: a( q8 M& t% E. d( @6 t: |{
    : K# z( v9 r; ], i2 M    //创建服务监听线程
    % ?- f" M  y* S    handle_connections_sockets();
    " u* z2 X4 v3 V9 s# R6 E) D}5 h+ n( ?1 g( m

    4 _$ q: z" M& d' dvoid handle_connections_sockets()
    - M& T0 y7 T; B{0 _0 b2 [8 ~! m0 j" v( B2 P: b
         //监听连接
    9 X' ?1 h' G# }     new_sock= mysql_socket_accept(key_socket_client_connection, sock,
    5 H8 R# V4 @5 z/ k+ H                                    (struct sockaddr *)(&cAddr), &length);# A5 b% O5 h- [) O

    ; Z! K: B& I( J    if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))2 ~$ @1 P4 n8 j
          thd->security_ctx->set_host((char*) my_localhost);  G* W- r; Z& E" P
    . w4 S6 `/ A7 [3 p$ ?
        //创建连接- }4 b: {' B3 s$ D
        create_new_thread(thd);; y) c0 p$ J: d' d
    }
    ( \# i) H$ r9 e8 E8 q
      h" H; O' P8 B0 g) c//创建新线程处理处理用户连接) h$ e( F8 J/ R" w; Y( W8 `+ ]$ U
    static void create_new_thread(THD *thd){  r8 m+ k! Z- w1 ?3 A% d4 u

    ( p: e7 L$ j7 O( S$ j5 P% S& s1 a# F   thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;0 \1 T3 Y; R) ^; i' u% k: o
    1 ~- _6 Y% `9 j# I
       //线程进了线程调度器
    4 F9 K1 ]. n2 m$ a- C( O( ^- X   MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   ) B% G: f$ a* K3 H1 d
    }
    " U/ }; H  n: T+ i% W7 F2 V
    , U( |6 k* |4 Z( j, _8 }( l
    ) J/ ^. P& Y3 D2 O至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
    3 a0 a8 h3 V3 j5 ?2 c  f
    1 Z# S" N: E/ Q* g. d8 F1 v# ~, ^; h
    2. 理解mysql是如何处理sql请求
    % n8 r4 Z& M) o& H" b7 v. {这里我以Insert操作为例稍微解剖下处理流程:/ C6 }: S1 k* n0 h
    2 H; r, V% l/ D1 S8 l
    当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。( W4 n1 g$ O* M5 x# y4 d3 }& ]& |

    ' V- b7 a8 @, E, J
    6 \; c6 s% a: }/ lstatic scheduler_functions one_thread_per_connection_scheduler_functions=
    3 o5 x0 n: @0 Y1 h) ^, L{' O* j9 h& W6 A' r; d. P/ Y
      0,                                     // max_threads
    / @  {( P6 J, P& s6 Z. c  NULL,                                  // init
    . |4 x" v. U$ w0 K9 D  init_new_connection_handler_thread,    // init_new_connection_thread+ i  }/ S! g" g, `7 \. `! _) V
      create_thread_to_handle_connection,    // add_connection2 W& q: s. G: l' T8 f
      NULL,                                  // thd_wait_begin0 v7 |; K+ q8 ?
      NULL,                                  // thd_wait_end
    9 Q7 U" U( o+ F) |" G% K  NULL,                                  // post_kill_notification
    1 K8 x( I6 j0 c  one_thread_per_connection_end,         // end_thread
    # B$ I6 v# I; C  h! A; d8 X  NULL,                                  // end# _$ A. \2 M6 ]/ i0 ]. s# N* ]
    };
    ( T8 z( g5 _) z" H# X  D% b& r' x- I% x/ S

    0 R( R$ M7 N% a" {- r2 M3 P# L从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。0 S' c; K% A2 W
    0 Q8 {' Y4 q- z$ D9 L6 u* P& j1 ^
    <1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
    + l" Y) t' v* Q5 g3 c0 R/ H5 r  I+ X  l  n3 h! k. l8 K% b! Z
    void create_thread_to_handle_connection(THD *thd); G' z, ?: E8 E6 V8 N! h
    {# |' _/ H" T5 o2 U% S) u
         if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
    + c* O- R# C' t/ q3 d  q9 i                                     handle_one_connection,(void*) thd))){}
    % C: l$ G4 ?8 R8 s9 Q2 V' O5 T: z* z6 @! b}+ q7 n' ^$ o+ j1 Q7 K' [
    //触发回调函数  handle_one_connection
    9 F& o1 c- A2 n8 i1 `# wpthread_handler_t handle_one_connection(void *arg)
    9 i/ f5 p  C. u{# [/ }2 R0 `/ P+ Z  w
         do_handle_one_connection(thd);
    3 e6 a: [# W) k( U}+ P0 d7 K* a% v# i& t
    //继续处理
    - P; H! \/ T7 b: s& Z) x' ]void do_handle_one_connection(THD *thd_arg){/ M: m0 x7 W: T
        while (thd_is_connection_alive(thd)), @9 L2 ]% ~' ^$ w) Z  Q
        {
    7 U4 Z! ^4 J$ B1 M      mysql_audit_release(thd);
    % D- R& M$ e2 r$ c      if (do_command(thd))  break;  //这里的 do_command 继续处理
    % \% O' d" A; }1 `    }# ?( @* p5 x+ q, ^1 u2 ^
    }
    % I/ I" T% e) a0 j6 G3 k. R//继续分发
    8 d0 F4 V& Z- i5 W% @bool do_command(THD *thd)9 Z7 h5 a$ u% N% |  W+ A3 Q. `
    {' z1 e' o! y( N8 k) f2 L  O
        return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
    , Y! e. B! z4 ]# x' A3 k}
    : ~' Y/ U, l1 Y) sbool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)2 |) x% J$ {8 Z4 m( d
    {
    $ L2 j, e4 R' d( b4 E3 V- I      switch (command) {
    / z/ [; N$ j; W; ^         case COM_INIT_DB: ....  break;( \  `" a5 I* @% ^4 G! [' Q" P! c
             ...( J/ D3 J& C, L$ o
             case COM_QUERY:   //查询语句:  insert xxxx
    ; j* }! Q& P# y2 Y- ]. z             mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析9 X+ b4 z: w) E4 o( ^8 A
               break;
    ' t1 Y* L( G6 A/ F      }% g+ e) Q/ ?" I$ P$ t5 M# ?7 ^
    }
    ' q1 c( W8 j3 N# a//sql解析模块
    # h: {& h, J" A2 P! w: E7 [6 @void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state). @. O: X' \/ ~9 v8 F
    {1 w4 }4 B3 G9 g1 t3 q3 s. \
          error= mysql_execute_command(thd);
    * h) O" h7 P: F# f}3 T7 z2 u* C, M& k

    " c5 Z( @( k+ W# J# d# ?$ b3 T/ U; S2 ^* `4 s  {3 o# G& S
    <2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。+ H8 T  P0 \& `7 B
    1 O4 d2 ~  x4 k9 Q1 @' U
    //继续执行
    0 q! ~* @2 |# W. Q2 {7 q# U8 Cint mysql_execute_command(THD *thd)
      m" }1 n3 A6 h8 }, P2 ^! E{  {; a4 Y" m+ L2 v  h7 c5 D
      switch (lex->sql_command)
    % J. ^8 I* L* z! A1 B. V# y  {5 y  Z* z- ?2 N5 t
          case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;
    & g4 r- C: M9 k+ i: [% ~# g. B7 c0 F
          //这个 insert 就是我要追的$ Y1 J  l( e+ C' e( P/ U
          case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
    ) ^& {! Y8 C! }2 |1 f1 E7 A                                              lex->update_list, lex->value_list,. y3 a5 g% t% R8 d! D
                                                  lex->duplicates, lex->ignore);
    / t( ^) V: A; K- c& P  }  T) S- Q( ?" q  h
    }
    , e0 Y" V" I6 z//insert插入操作处理
    6 S4 h9 y% m* j0 ^7 {: t) ibool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
    1 b/ B' I" B) U* j0 R: t0 `                  List<Item> &update_fields, List<Item> &update_values,
    9 c, E$ ~) F5 ~                  enum_duplicates duplic, bool ignore)
    $ O: C0 s& X6 e, Z8 k+ O{, X4 v+ f" Y4 u/ |$ l2 v; s
          while ((values= its++))
    ( u  N- ^* D9 x3 x* _9 ^& D! M      {
    6 y3 X8 F/ a* X6 P/ j0 P& ?           error= write_record(thd, table, &info, &update);
    2 O; J' v" N+ F) Z6 x      }5 C- \9 @0 t# L/ P3 `$ |: t8 b9 |
    }
    7 e; g# x$ X  X- A% k2 B//写入记录" r$ F' t1 c% H6 L$ o/ M# e
    int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
    % Z& x4 o5 t  L& V4 s{
    ( u( x3 H# |7 V6 i6 s8 L    if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
    3 G4 e: f/ ^4 x    {
    9 T, l( A& k% O         // ha_write_row  重点是这个函数
    5 N/ ~8 V# ?5 D8 c  S7 c4 C         while ((error=table->file->ha_write_row(table->record[0])))
    4 k( O8 u" _  Y+ c3 R- l4 s2 I         {
    9 s3 S, v; U( f6 t+ F- e             ....$ v# L* O- E1 s# m/ B- }4 C: j
             }3 S( T  i7 r' E% ^/ g% {( ~3 W
        }
    + `; F- c% u8 R( ]* e, Q* P( g9 s}0 ^* D% [  g6 e# @/ u0 @

    $ w' x& B9 f4 G3 j
    # E7 Y! b. \: c
    . s- }. v3 c' X" @+ O可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
    : Y+ W9 E) v) ^6 J% [
    1 ^) S) T/ [% G3 I9 ?0 t<3> 继续挖 ha_write_row5 X8 V$ w8 W" d

    # ]& T4 ^2 _. kint handler::ha_write_row(uchar *buf)
    ( ]/ @4 A4 ?" k1 `7 M{! H8 P/ B8 |# f" f2 C
        MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })+ C5 M4 q0 a9 Y; Z5 O* @
    }
    : g+ ?2 A, C) Y1 A9 y% Z! W7 y4 h) F( F. Q8 H0 y
    //这是一个虚方法
    $ H: ?) o" \; O& qvirtual int write_row(uchar *buf __attribute__((unused)))
    ( J8 H" T; G/ m, T8 s3 V{" ?3 ~, F, [1 ~* \/ e
        return HA_ERR_WRONG_COMMAND;
    9 T) ?) B$ y' l4 N1 `. p9 k}  b; a; i. X- g

    % `5 V8 C; n. ~8 m: d, P( m9 {% \) X! A% n: X7 C# S1 W, H7 p
    看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;2 @2 |( k% B- E* `

    3 Q9 W. j& k* [0 p: j9 r, l- |3. 调用链图9 g, R% }! A& b0 h5 O0 A) `! W
    这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
    2 p9 I  t$ p; _$ H' N0 m& _! ?* x6 K7 t' G
    # _, x# J. h( P

    " B+ {3 b# g* H5 B6 [6 g三:总结
    , C5 O3 o4 W" S* o7 b大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。0 O( p( U3 U$ h
    ————————————————% y; ?3 A/ m9 k' l- ^
    版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    + m$ h+ d+ A6 e7 i原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
    & g1 @- ]; l! I+ o3 x% o
    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-5-8 06:46 , Processed in 0.434794 second(s), 51 queries .

    回顶部