QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3516|回复: 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
    一:背景
    2 J) j* @- w6 X. u1. 讲故事; c9 N! N* ^1 z, l
    最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。7 g" K+ ^( c3 T7 X6 |# O4 E3 L

    # _. x2 s, S& C9 ~3 r8 F. V1 J二:了解架构图3 o) E- U3 q. h3 k) Q
    mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
    & r5 r# H+ u% c7 t9 Y% E& o( H4 l$ I7 i7 M- e& ^0 ?/ g# ?7 s
    1. 从架构图入手
    8 B" W( ~2 _" O5 m大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。3 E8 k. u- y7 u8 c3 E( s7 v

    7 G9 z& O: P2 k6 Y* k8 `
    ; Z3 Q: u/ I4 y$ h' W+ t( h( m; m
    ( \+ B) o0 h" T8 Q2 k+ X6 R其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~% g8 H3 m! `) x+ H/ |* {+ p& @

    * O+ k1 [! u3 V& K/ K% T2. 功能点介绍2 A2 `' M- B: @; f
    MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
    0 a& P- F5 U* B$ b4 l6 s) }+ I% b5 S: _& Q" m0 C" s
    <1> Client7 \- }3 t5 C7 a- J- _: e$ H6 S: m# e# `
    不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
    " K  W  R5 f" o. t. ~8 P) g, @+ x
    <2> Connection/Thread Pool
    " [. V6 B" F! B1 G4 r- t) aMySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
    " d; t- x* C% K; ]
    8 u5 E2 W4 o$ _. ^% y! H6 G) }6 }) j<3> SqlInterface,Parse,Optimizer,Cache
    + u! R. X3 Y( ?" N1 `# p2 Y对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。- a! f. j; _6 j6 s+ q2 }+ s
    ) }" _# W# a7 O: Z8 `
    <4> Storage Engines/ _( [3 w+ l* v8 c
    负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。2 i' F- M6 g* N7 b- I/ t1 ^& {

    ! _1 k  @( T' n: t1 w三: 源码分析
    # w5 M4 j! L  a3 i3 G, w关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
    : I* x2 z3 F  S& |% V% Z( c  O! D1 J1 o- f: v
    1. 了解mysql是如何启动监听的
    & M) D+ _9 `. v手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。, b) R1 w  L# {2 Y0 I+ s( _
    8 A8 e7 S' {- p. p+ Y" O

    % M3 o5 h; s7 f9 g' E' ?7 a9 B4 u% G4 d
    从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
    5 f8 N$ E8 h% I* }, E  I% U  f0 H9 k! u( w
    <1> mysqld_main 入口函数 => sql/main.cc! x5 K/ Z; r: V  W0 |- a4 P
    % z, q' c# s) y3 i) ]9 M& p- y& l$ p4 s4 r

    9 _7 n5 j" z- @* Oextern int mysqld_main(int argc, char **argv);
    9 m4 \4 W: A, f* s
    , O( |- g/ Z* _int main(int argc, char **argv)
    $ d7 H$ R1 h# i1 N2 v/ {{: q4 c$ s8 c% @" H1 b
      return mysqld_main(argc, argv);
    8 \' e7 G7 U1 f}
    0 \% _7 k" I. y
    ! e. x/ ?% T2 v$ y, N! |7 S% o
    9 h9 @6 n; Y+ k0 Y  r# u这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。, w' f' Y% W5 H! b& p/ |+ U
    : w* W* d( N  ~6 h1 a5 Q
    <2> 创建监听' {" z* M& q( v% K3 h. J6 J- ^) u
    ) t1 z2 U& E. \& J6 M
    7 }: P/ \$ B$ ?( q
    int mysqld_main(int argc, char **argv); |/ W6 x( D& ~5 l5 O, D- N8 G
    {! G9 q" Q7 `) R% q
        //创建服务监听线程
    " x- h- O5 `* Z8 F! A( E    handle_connections_sockets();
    # M1 w4 Z/ i( c- P}" N8 O! T( \3 E4 V3 B

    $ M8 ]. H4 A. Hvoid handle_connections_sockets()% u  ?& c! N% E
    {
    4 T8 N) P  v+ g) q     //监听连接
    2 Q0 h: J$ o2 x! ^2 c4 G# m. }     new_sock= mysql_socket_accept(key_socket_client_connection, sock,
    9 y, q6 i; Q+ j; `                                    (struct sockaddr *)(&cAddr), &length);+ p: _# z3 N8 k: k" h

    6 o& b0 R. R* d( S  m    if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))+ d- p" ~; r9 b/ M
          thd->security_ctx->set_host((char*) my_localhost);% l- F0 [+ z& y" S& I& k5 t

    $ k3 Q+ o  R/ D: C    //创建连接
    ) f( w" v4 f6 C# ~* u5 p, O    create_new_thread(thd);1 H& {0 d6 v" g. G" \5 s  D
    }
    3 a; a3 T# m/ l# |9 P3 |7 s$ T& l
    ; G& [2 S/ o) V//创建新线程处理处理用户连接: L# w3 B4 D8 @  r% o" |7 k
    static void create_new_thread(THD *thd){
    , L3 \+ I6 d" c. [7 Z0 d) }9 S. n, h- k1 [: n9 h) n
       thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;0 |, R& k7 w- F) G; p1 |8 c3 H- a# f

    6 u" m! q; L, K4 A1 u6 K   //线程进了线程调度器
    # e7 p) W5 _0 b! Y6 L3 T/ w/ g& [5 m1 I   MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   
    1 P+ Z2 D  ~( c# O$ n3 I$ k}' C: Q2 d3 K# {  _5 T

    # e: ~% z' K# v1 [0 b
      d& _- N; N' k3 Z' G' s4 p2 B, i* `( L至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
    . v9 i1 r7 U+ H# z" ]* y0 k# ^( C- V/ t0 P/ Q

      `! Z5 a2 t2 L2. 理解mysql是如何处理sql请求& r4 o! r$ j5 }2 d% ?1 H+ g
    这里我以Insert操作为例稍微解剖下处理流程:
    * R3 b: E0 O  w& n& J6 E: @6 p  n* `
    $ I/ @; Z% J) R! f- @5 y当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
    0 W3 h- `% {7 ]2 b2 g$ q
    " Q, O% G6 @) M8 N5 S* W8 `1 R
    static scheduler_functions one_thread_per_connection_scheduler_functions=
    ( w* J: j& F2 e' s) a9 N! R. E' m{
    5 l" |6 ?/ u  O2 O/ c* q/ O# w  0,                                     // max_threads  s  f$ b* R9 i5 S
      NULL,                                  // init
    7 t8 ^6 H4 \: f- S; S+ j  init_new_connection_handler_thread,    // init_new_connection_thread( a: H, Y' S$ Y4 {) Q! U1 j
      create_thread_to_handle_connection,    // add_connection2 s. g# V( ^0 Q( x: E
      NULL,                                  // thd_wait_begin! E# S) x  d7 i: _/ H7 L4 S8 t
      NULL,                                  // thd_wait_end$ b; t0 \6 j4 x$ B
      NULL,                                  // post_kill_notification  n. {; L" |7 P8 G
      one_thread_per_connection_end,         // end_thread
    . \4 v2 i3 f  p' i5 y  NULL,                                  // end2 T6 }  ?0 k% ?1 \4 @9 C4 d2 s# J
    };
    . c" }8 O4 @6 t9 C) a0 m% n' w& h% J) x& z( J
    " w! y3 Y; F& m; I0 ]4 ^7 }8 {
    从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
    3 |% O9 X  [. p$ @/ [0 R
    ; z( P0 \  Q5 H. ~$ U9 l( |8 R<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
    ( S  \0 }* M# ?7 y. k
    & x- p; C& `! U8 B2 v6 Wvoid create_thread_to_handle_connection(THD *thd)
    * m7 d7 I+ Z5 k{2 i. F8 X. C) W) d; G
         if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,! Y' E3 W1 ?+ Z5 G' [1 |9 Y- s
                                         handle_one_connection,(void*) thd))){}
    6 e9 a7 d9 P. A& c7 }}
    ( J! V. W; u+ `5 g' ^# g//触发回调函数  handle_one_connection
    8 S7 V* F) B( q! e$ I" Ppthread_handler_t handle_one_connection(void *arg)
    2 Y; c9 h0 k3 |. I; |{
    ( t7 O# b1 ?8 O     do_handle_one_connection(thd);
    / E# I+ z7 E4 |9 ~7 ?}( @" N' h+ A* z8 b7 i6 F
    //继续处理
    - [) w! G, g2 M' B+ a$ O. X9 Svoid do_handle_one_connection(THD *thd_arg){
    * W! j) A5 y, d4 Q    while (thd_is_connection_alive(thd))% T- B" ^; {" D& ~
        {
    ' d0 R7 W3 L; S& L1 a0 Z      mysql_audit_release(thd);
    & P3 H  f. U7 J  }# ?      if (do_command(thd))  break;  //这里的 do_command 继续处理: U  H$ G3 a: H, Q
        }5 t1 }2 h8 j  M- M
    }
    3 G; a( d( K! l! h//继续分发8 j+ x$ u7 o: ]) L$ L
    bool do_command(THD *thd), A( B' D3 @. c6 a( B6 u
    {
    9 C" ^6 n5 z5 \7 I' t% V6 Y. x; O    return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));% K2 t& t8 W- a4 O
    }
    ; Q8 c( t# `: J  E0 n2 pbool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)! z, y# t+ ~  l( Y) U
    {* r9 ~1 ~+ p& F! t/ v' L
          switch (command) {
    * a* R8 \+ @8 w( S$ ~" ?         case COM_INIT_DB: ....  break;
    ' B& w% s  X! d: [8 \0 k7 q. Q         ...
    / w1 V3 b5 Q$ b6 l         case COM_QUERY:   //查询语句:  insert xxxx
    $ M" B  r: B1 M% Z( ~             mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析
    6 {; [- W, b) J# t& [* u% H           break;
    0 x2 D* ~  m7 O; b- G' d      }
    # I, C/ H- j1 p. z}" C' [/ i" a: X9 i* x
    //sql解析模块
    " x- ^" G/ }- Avoid mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
    8 F5 X$ m* l/ C& ~{! v- O" H! d4 h: W8 ~, F3 p
          error= mysql_execute_command(thd);
    6 ?. P: c) }4 w9 S! z6 J! P, N}
    ; y) N( {1 n0 G, S% X6 `/ m
    ; I5 e. k- G; `0 P5 ]6 L
    ) `5 ?% q1 L3 Y+ m2 x4 S; W; _<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。* P- T, Y7 q) `3 m" ^! Y/ R
    - f% S- x0 h3 v5 J: F/ t0 a
    //继续执行5 \3 W9 V2 j5 _8 @  @% v
    int mysql_execute_command(THD *thd)* ?& r1 c3 ^* f8 p3 \
    {
    ' G; x* q/ [2 w* V( E; }  switch (lex->sql_command) ; y$ q2 Y$ G# \1 r( P/ A
      {
    : [: A- u# C, n7 [! Q      case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;8 m  u5 e- l# i; k3 o% J9 `" O. ?% E6 a! M$ A

    # h8 a! f, ?& }5 p# b% d# N      //这个 insert 就是我要追的
    6 t- R/ Y; ^3 |. b; t0 }  V9 ^, G      case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
    6 w! @1 ]' ~0 S                                              lex->update_list, lex->value_list,
    * O! a, M( A" a& v1 r) @  G0 M                                              lex->duplicates, lex->ignore);3 b# ]% ?7 r5 t2 B9 g
      }5 {$ V$ u4 H3 R5 V: U; \9 T5 Q
    }
    ( w3 C# Z# v, O2 o" W; f: N; V- B; ?//insert插入操作处理
    - D/ d  E7 |7 D1 g& [bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
    6 v% @1 P/ K0 A* N                  List<Item> &update_fields, List<Item> &update_values,   ~  d! E0 @9 P; v
                      enum_duplicates duplic, bool ignore)
    5 Z8 f! Y7 U& A. `{8 L5 L& l) j: _- m) ?) {5 r
          while ((values= its++))7 g* N& K0 n6 [8 _1 I1 s
          {' `( _, |0 j8 U  G
               error= write_record(thd, table, &info, &update);9 o- F2 s) M# R9 A* I6 f7 W
          }
    ) `# z5 W) {5 v; M  o/ v% m6 i}- o+ e! a1 t% `6 e! I$ {: I
    //写入记录
    1 c# V+ y- I. `% D* _, y2 Mint write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
    ( G: t# {) t/ n, _5 `0 g8 X{
    # n2 q: N$ F9 s; R. u    if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
    ! w0 U- F( h* O. l  y; m    {
    7 `! L6 b' s! T* z7 j9 R         // ha_write_row  重点是这个函数
      S( O4 x/ W, m6 v6 M5 M         while ((error=table->file->ha_write_row(table->record[0])))4 m6 X% b+ B1 S" U8 |4 R
             {
    $ K8 }' o/ L, I; f4 V1 M, A! z             ....
    % Q  L/ a3 v6 {         }
    " ?  J+ w8 s7 l    }
    ( f" {- M1 p  m9 a8 Y}
    ) R7 B7 R3 t! o
    ' s" B" ~/ f! v$ y5 c9 V+ I- E; x3 m, b( d2 M+ F

    + y- w! ~0 j  H5 a# _; t5 j# }可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。! ~, b& F: C3 Q+ A$ }  v' b

    $ `( l, P* `4 F) [3 l4 C) S* l9 R7 z<3> 继续挖 ha_write_row7 o# b$ S2 F8 c" v8 Y

    4 z! s& a2 e5 s5 F, u( Rint handler::ha_write_row(uchar *buf)
    4 l+ v9 Q- c- R5 f5 O{- m& S/ s- L# b! _6 R
        MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
    , a; ?7 w0 H* K$ |}
    3 X- k( }2 E+ B* J0 A( d) W
    % z, d9 Y7 c. p# K/ s//这是一个虚方法
    3 H) h5 n, H% i3 }1 \! a( Kvirtual int write_row(uchar *buf __attribute__((unused)))
    ( N6 \- f' l- h) B1 u9 F{
    5 g/ N- a7 g. W- j7 c+ O    return HA_ERR_WRONG_COMMAND;
    8 E7 r% k, H$ K9 Z}/ S. C* Z  G& J$ v8 m& S; h2 V, h
    6 \- A1 m7 X1 x: J; a% p7 {

    3 L/ ~6 o, ~6 I$ W看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;0 l. N/ P' c( L2 |- D9 p
    & P! Z& J) ]; w' v6 p
    3. 调用链图
    8 [5 ^) \6 l) n; w1 a. U- \这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
    / s5 a9 ?! d  Y  H( q
    ' |1 p# M; M1 D' }/ r; U; i/ W
    ; n3 {& n9 }" T" ~5 o: m5 ~
    . R$ v& E# B; |6 r5 F2 w+ m三:总结
    6 j0 g4 e  N* o0 r, H大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。8 t1 b6 K  p; _1 u
    ————————————————
      @. Q4 A) O( ]1 Q版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    8 S7 e+ l  l' o% y原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
    & R! K* I- Z9 }, h  f4 `
    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-27 02:35 , Processed in 0.649782 second(s), 51 queries .

    回顶部