QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3513|回复: 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
    一:背景6 b! p1 t. }8 Q/ z7 e: u8 S
    1. 讲故事& m1 U  D2 T2 }' h% Z, o! i2 Q6 s
    最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
    . ]" o( v! n# m; U+ Y7 J) g  W* ^8 I. X# d
    二:了解架构图5 J: t9 J/ s7 x; I$ G
    mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。9 E: _0 G7 T: n2 i1 ~  @  ^2 T8 ^+ d

    # W" |  l4 C2 ]# g1. 从架构图入手! P$ C7 V" X; w7 }5 N, W" R* {9 `
    大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
    9 B, D5 r; S9 u* o, S1 d
    , @7 t8 i* Z3 P$ t4 D; L" ?: A" |

    6 \6 V; [$ c; U9 G0 N2 n其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
    " Y2 Q4 l+ W# w  c9 E( P: U& Y
    1 e" J& M% a! N: m2. 功能点介绍
    $ ~  d( I9 C8 n1 A8 p9 i" ]MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。# D& h1 N6 ]6 r
    1 }% k1 n& P" N' ^5 H# k
    <1> Client
    " Z! X6 y. a0 Y0 A! _) e1 R( l不同语言的sdk遵守mysql协议就可以与mysqld进行互通。0 L% J- h4 ]& i& E; T
    " `% @9 C# N% D) m/ z- [& k, T
    <2> Connection/Thread Pool" ~7 p6 Y' G2 c; o4 _' f8 {
    MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。) T% L$ b5 ]/ I2 x5 j! w; g7 F
    % k* P& B# e( w" H. c; p) u- G
    <3> SqlInterface,Parse,Optimizer,Cache
      s& S# F8 v' t; A, U3 D. e4 w对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
    / U# t' p, t( i4 t$ W
    : C# P, a1 C' i; ^, J<4> Storage Engines
    & p8 c3 O4 e2 F1 G- q负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。# P: P/ B  d+ p% ^

    : m0 F% a- p: f0 q- o9 t% ]" s三: 源码分析
    0 ]2 S2 A3 w' m+ }8 e2 Y9 p+ S关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
      P5 d3 [# w1 q. n9 x- M) W9 D
    7 d* j- a+ e' G, A1. 了解mysql是如何启动监听的
    # y! s9 k$ L! r# v手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
    ( w7 P7 A5 v7 D2 u0 {. [( |, b
    2 l2 ]: ~1 V# e* V( n! @$ c" ^9 L. ~$ A

    8 }# e4 w* p% o! ^0 F8 N从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
    . u1 [$ Z% H& A( A$ r0 _4 G* c* N: y$ I1 u! ~( T' l) g: `6 y
    <1> mysqld_main 入口函数 => sql/main.cc( T3 w/ U9 L: Z& F1 t4 u& l3 L( B

    3 p  |7 Q0 p3 q- b, I) w7 c/ _6 V0 o0 [- u% q* \
    extern int mysqld_main(int argc, char **argv);- T  N1 @  O' \
    & e2 w3 h. A. \1 S
    int main(int argc, char **argv)6 g. C- G+ ^! O% c4 \
    {4 P5 ?; T+ d0 e8 r& T% U
      return mysqld_main(argc, argv);
    2 t& d7 [+ B( H, N; a0 W( B}
    ( g+ G' e6 W$ [2 [0 S3 o' u1 g  p: W9 |# k' g3 |+ Z

    9 B( b( _' l5 V5 {; M5 k. g这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。# b1 M& l+ d" i: ~
    8 y4 R0 T4 n; m: e% a: t
    <2> 创建监听; S4 L/ n& F, C9 c8 ]& v2 i

    % J9 s7 v" B- k/ g) @
    ! u) \4 ^8 {- Z; fint mysqld_main(int argc, char **argv)' ~1 {+ {5 K: P' J6 v/ ^5 k
    {
    ( s5 a$ @& I( V  m0 O- Q7 G$ N    //创建服务监听线程
    + C' i; o) o: |! `/ ]: d$ j    handle_connections_sockets();
    . o) k, J/ H  E}' t7 o- I0 J) ~, {* T* C1 O
    6 ?5 B: t  }7 A; R0 {
    void handle_connections_sockets()
    1 N3 ^1 J' g! S$ O7 d  K. T  }{' ]9 }$ g- H& m5 R% M( w/ y
         //监听连接, y, s& ^9 e1 \& k
         new_sock= mysql_socket_accept(key_socket_client_connection, sock," _9 R3 a( l6 K7 d
                                        (struct sockaddr *)(&cAddr), &length);
    + Z  y8 ~# [* M1 C$ K
    / p3 ~1 D* C" V2 l3 `    if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))" }3 X& G: y( ~
          thd->security_ctx->set_host((char*) my_localhost);  G% g) P! s3 A5 Q
      _9 [5 _3 a) N% H
        //创建连接
    & \2 N6 q9 e6 l- ^: ?4 I8 ~    create_new_thread(thd);6 L9 {7 v  w6 x! Y  [
    }
    * c) z4 G+ K1 D) ]/ o; D( Q9 p" U4 Z% R
    //创建新线程处理处理用户连接
    0 ^2 X) F+ F; i$ I# R* ]% l" o  x5 \static void create_new_thread(THD *thd){
    9 V$ l  B3 `) @! j4 F8 E& B( [0 R, n& Y/ \) Z. F2 B
       thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
    2 ]# e3 m  [+ s: ?% S
    4 z; f8 L- `3 R1 a   //线程进了线程调度器
    2 m1 S6 r; I2 I: H( t" Z7 W9 h   MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));     j+ l7 u% j% R4 z. f
    }
    / ?' ~& E/ q. X1 r5 M! ^
    / i# C6 g4 F! k& W+ b7 y0 D- A# n7 M, {% F) F4 n; U- L
    至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。8 R) b4 ^9 ?; i9 q

    " E: Q: o& n: Z" D; f6 T+ c7 X7 |# c/ l: e% U8 H
    2. 理解mysql是如何处理sql请求2 [3 b; f+ X6 K, f& B
    这里我以Insert操作为例稍微解剖下处理流程:
    ) ~: o  h6 J  b) S! R" ^6 F4 W: h3 H, y: w. w9 a& l, Y. X6 d4 E
    当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。1 B1 ^# [4 M; a4 ~$ L
    ' o# i1 U0 {3 d" ]* o9 e& e
    ) O1 u  L7 M! G  o' A
    static scheduler_functions one_thread_per_connection_scheduler_functions=
    4 A6 m: n. Q# Q/ d{$ o% D6 D5 }# `9 g$ H
      0,                                     // max_threads
    ' Z1 H1 `1 q% y( ^% ]  NULL,                                  // init, ?5 B: ^: h  V+ b; I( L- U$ W& B6 D5 J
      init_new_connection_handler_thread,    // init_new_connection_thread+ h' q9 v0 m: A4 O* ^+ X+ D
      create_thread_to_handle_connection,    // add_connection& Z8 O0 L4 B' x6 U- V; U5 K  \
      NULL,                                  // thd_wait_begin6 X% y: J6 l, a" H" C: z' q
      NULL,                                  // thd_wait_end
    0 R& t% I, u* }9 a  NULL,                                  // post_kill_notification
    - s) \" r7 M8 z; \  one_thread_per_connection_end,         // end_thread
    ! Y/ t0 i  d  m: W% w9 @% y  NULL,                                  // end
    ) F! q5 d& B+ D; ?};2 m& {' v3 O/ Y: b) T* h2 U
    2 I3 s+ w4 q, \2 J
    ' b+ k8 ?9 C/ L2 ^" i7 Q" S
    从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
    ; z8 Z5 B3 ^# w( s! Q, N! K
    1 J8 h6 Z* ]6 ~4 S2 R$ y/ p1 Y<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
      o. m' u6 d4 M* x" F, w: m! {4 F4 L! |, ?) v
    void create_thread_to_handle_connection(THD *thd)
    / q- ~2 U" |8 }# K. h: b2 x{& c: e9 X4 Q: @; D1 b  T, z0 t
         if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,! S% l( ~% k& ~1 ]4 c
                                         handle_one_connection,(void*) thd))){}
    ; k5 V! i3 r3 O$ V}  A: a# m3 g5 j5 F& n; l/ |
    //触发回调函数  handle_one_connection
    % Q$ w5 B5 Y' O; M/ ?! m$ Gpthread_handler_t handle_one_connection(void *arg)8 s/ [' W5 Q" P, j$ O  t2 F" f* D  c
    {
    - @4 Q7 H' D1 `$ S- m$ @+ [  z( F9 j     do_handle_one_connection(thd);
    7 B( O4 K! q7 d% c}! }3 n2 q- H, ?, w! R1 o$ k5 }
    //继续处理
    & I0 d" k( O% E" zvoid do_handle_one_connection(THD *thd_arg){. p% _  g/ x7 m$ _2 e! [
        while (thd_is_connection_alive(thd))0 V" |5 y7 M0 I4 m2 q& [% F
        {8 I1 ^  w( F/ b: M1 P
          mysql_audit_release(thd);
    0 R" B. i) t1 J( {8 M. X- I      if (do_command(thd))  break;  //这里的 do_command 继续处理8 H) L& K) w# Z0 A- C- J- q
        }
    ! ?+ W! z: y" i9 g0 }}
    % O4 B3 a9 ]! W: C" P% H9 m//继续分发
    * t- U% L: m) T1 `3 f2 U; ]. W' Fbool do_command(THD *thd)/ \  t% Z9 I% [# b9 D( ^
    {; X2 W) \8 t0 M' O: W, P9 D
        return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
    + G! [. a- [/ o# r* Y}
    2 N" D' C; l& I& M( t4 z5 B3 o2 {bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)' [. N; ^% {) w! h/ P% V' u2 R5 o
    {
    8 e4 a, V* U; Z3 X3 U* s& Q      switch (command) {/ ?5 K) F9 w  H# s3 f% j- M% E
             case COM_INIT_DB: ....  break;! p5 o2 E0 e3 Y
             ...) `1 ?, c. |) c/ m+ `8 b6 j
             case COM_QUERY:   //查询语句:  insert xxxx7 _3 q8 j3 v1 c
                 mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析' u' P! ^! X/ t3 v/ `- L& a# M
               break;
    . m% ~1 S! ]1 D0 \0 B      }
    5 X7 y+ Q2 q- `( {: S}5 s- z+ f( q* A1 B) |) @  o
    //sql解析模块. H) t, G2 G, d0 B. D+ }3 `; S
    void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state); S4 ]6 E( i8 _' U- j; E6 ], n3 O9 T$ u
    {3 z  g1 ]/ o2 a& H- o. @
          error= mysql_execute_command(thd);
    ( D) n2 p8 Y$ s7 p. v; F7 G( w( ~}
    * e6 r  m' i* L- J8 q, R9 Q' s9 \* t0 B8 C: h4 w$ J9 e4 I

    * v: H5 e7 ]% A3 ]& Z, [<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。/ V3 O7 Y5 K, r( M/ ]8 M

    - [, ~. ]9 V6 p. h& f, N# @//继续执行
    4 _" t. u% f3 c6 D) H' i9 ^3 E5 tint mysql_execute_command(THD *thd)2 L% p3 T$ K; H' K
    {
    " b$ X6 P; {! d1 X) I( }3 ]  switch (lex->sql_command) 3 Q6 Y( B9 w# u
      {0 C, ?$ s$ c, n; u
          case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;9 w) R/ B5 |% d" r# ]4 @' A
    * Y1 Y9 \- A5 }
          //这个 insert 就是我要追的1 x, U/ z* x: b3 e* a- L
          case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
    : ^; d' n* w3 [$ q( s; ?                                              lex->update_list, lex->value_list,
    3 M* w6 l9 a2 m+ T0 C9 G                                              lex->duplicates, lex->ignore);$ O0 R- W: a1 ^8 n% l8 l
      }. s8 a. c6 l4 F  M& M
    }9 o" b6 `- C; R  Z! A* K; n
    //insert插入操作处理
    3 B* h( g' I( |. m2 @" xbool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
    7 l3 s6 g; h, B% g9 d) u5 L                  List<Item> &update_fields, List<Item> &update_values, ' m' f2 q% i5 Y9 A& ]3 X
                      enum_duplicates duplic, bool ignore)7 Y! F* d8 Y- a0 j
    {
    ( \! n, W( i. P5 E: O) s5 W; q      while ((values= its++))
    : m  A; g- v5 b' e  O2 @- @8 u      {
    3 L- f( `" m. t           error= write_record(thd, table, &info, &update);/ S% N" X1 {; H& q, \
          }
    " h/ ^3 k% j) `# T; N/ c$ p: p  p}' U' N) k# Z5 ]1 Z- k$ ?% I! N5 O
    //写入记录8 v5 ^* L! i( w% h: \
    int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update), _& B5 T" W; ]( T! K& _; C
    {
    8 C6 U- a7 {! o6 e/ |5 j$ z! A    if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
    ) T, n! F" x% C, g    {" }! }) Q5 n0 T2 y
             // ha_write_row  重点是这个函数: f) ]( ]4 t  C$ q
             while ((error=table->file->ha_write_row(table->record[0])))2 C6 }( V+ N2 j* S9 c
             {4 c( Y: e0 a* @( _# K
                 ....
    3 u" N* K1 g4 `* H2 R         }; a8 m, @" i  }% |% Y
        }$ v/ ^% j4 j+ |+ K+ D6 v& A
    }9 X( Q/ L) n+ L2 H4 s; j8 q" ^
    , d! b% ?3 e1 F0 [
    4 d& q. ?1 S/ c+ F. g- }" e

    9 i4 D, k/ q5 ^! F( }可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
    ! D/ O& n+ ?7 E, `; Q: l4 b7 C! x* F6 Z- T% n. u/ `
    <3> 继续挖 ha_write_row
    0 V0 {; f+ `' s2 H. ?  r: Y5 z! b- ~& g) P" e: K
    int handler::ha_write_row(uchar *buf)
    1 b) [# w6 C( ^{
    * m4 ]- ?# m# |; F7 T  S0 O    MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
    ' s& |: ?$ e: g$ k: z) g0 i}
      [, N) x: S1 S8 z+ b- ]& j! `6 @5 W
    //这是一个虚方法
    * H4 E/ R. d" E2 {* evirtual int write_row(uchar *buf __attribute__((unused)))
    # s4 _) \* J6 a{
    6 P2 ?6 I" M/ z7 i& P& ~    return HA_ERR_WRONG_COMMAND;
    4 U0 m7 S& _2 [( f# r}
    " [. {  q. ?4 D% ?% [5 z4 T4 W8 X$ p- l7 Q
    8 b2 t0 m" h7 }6 m
    看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;
    8 D. z& }1 J  C1 r# k2 t, ~3 @# M2 p' {# X7 }( q/ n
    3. 调用链图
    % p7 }$ `6 c7 V0 M这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
    & q# x' i, }9 }, E+ V" W7 m, d  y( R+ J9 D7 M6 @

    7 \; v0 K& q+ w* U- U" j- J! T+ r/ w% F" t. P0 D
    三:总结
    ) d* Z7 m/ d# t大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。) ?0 s. A0 T; g
    ————————————————
    " Z1 a+ v% [1 i2 S2 n8 G" _7 [版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    " [. P. z( _  a原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415% k  n; @& ~5 f0 H7 O2 A9 R
    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 16:02 , Processed in 2.729590 second(s), 51 queries .

    回顶部