QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3520|回复: 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 |+ Y2 d# K6 Y, H7 O8 _1. 讲故事7 s5 J" m# o2 Q- G1 I
    最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。8 R* M% _9 @2 r+ k- @+ f1 k# @

    : y8 P- X: p% E/ _# _8 l* @二:了解架构图
      n7 C$ d8 f: v4 b$ I  Tmysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
    ; A: \' ~2 b: Y* Z, O6 T* ]% I+ Q8 G3 s5 }& p# P
    1. 从架构图入手" _8 I* F* `  x0 M8 e
    大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。. e1 A4 G* n- M% Q6 y% j
    ) A' Z( ~$ \' `/ Y: w7 x
    0 u* {$ y1 w3 H
    0 R0 n; v/ j& a, s7 _, g
    其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
    7 {1 W; I8 N  h* H8 A2 N/ g
    3 ?# n! p: q& q3 s1 R/ B2. 功能点介绍! D( S# C6 _0 E$ c+ U5 j
    MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
    ! K# g: E/ T. o! N! u
    6 A% |/ Q% [1 g<1> Client5 J6 G3 T7 w/ d7 \# h
    不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
    0 o2 d$ p$ a! |4 f9 j+ v5 S; n/ ^. T. w4 I" r) z
    <2> Connection/Thread Pool+ P. Y5 Y# Y; E' F
    MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。; U. ]* F, _1 Y' y+ l4 Q" K
    ! Z$ F6 x. U7 Q# a: H8 Q5 O" [
    <3> SqlInterface,Parse,Optimizer,Cache5 \8 O2 h! p% n2 |8 s* j* _" q
    对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
    8 Q/ p% X2 j9 {" e2 W0 j% @0 G# O8 K0 [& A# Z% \) o" _
    <4> Storage Engines, h6 h+ C% ?- j8 l
    负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。
    1 Z: e. j+ ~2 F& k
    2 \2 }; g4 m5 `1 R; z& }+ j三: 源码分析' H. a: T7 p. D# W
    关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。* r8 J* G- s0 b$ g7 q6 x

    ; d3 }: S& c& O2 F4 y" }' o1. 了解mysql是如何启动监听的( g9 m, A$ F0 D) u1 X  T* U, D
    手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。4 v7 h/ P) O! J8 |; N

    2 ^8 J) _5 |+ F2 }- l  z" b0 K7 Q6 i/ F# K+ R0 J6 \
    # y3 @1 {, V9 H3 v8 C3 E4 ~3 C
    从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。- b$ g) ?/ |+ W3 k& S
    9 N2 A6 w# T0 E) ~/ d3 n$ D
    <1> mysqld_main 入口函数 => sql/main.cc6 G( `" }* f4 m& i# ]
    ! |& k; Y" _$ Y# D( f- S2 ~7 {
    # C' {; f# A2 \/ }+ z. b' }/ t& O
    extern int mysqld_main(int argc, char **argv);
    ! C2 J0 N7 ^) f: I6 p* N7 P* z8 y
    int main(int argc, char **argv)
      `% G9 E( F% n+ H/ d{* S, o" ?* t* n
      return mysqld_main(argc, argv);
    # j  x9 s- {* T}
    $ T4 U, s0 `. O* F0 r6 c. H* A7 d# E$ h7 R

    1 g% n. M: Y6 g( J: y这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。& N. M* ]$ m  a4 @% c" B, R
    - E) K) c5 z: O) h$ p4 E6 y/ x; t* T
    <2> 创建监听
    + i. ]# H: g! P& Z$ `) s" j( h7 H) N, ?* ]

    6 z! t9 ?5 S9 J( O$ kint mysqld_main(int argc, char **argv)
    * }" n& c- i& g" y6 _3 K1 A, e{% Q! z( m) C0 S" j
        //创建服务监听线程
    / l" H3 C+ p( E1 V    handle_connections_sockets();
    7 i3 \! @4 f6 h}8 d  J! K$ Y& m8 `# b

    - V+ b  H. [1 y8 svoid handle_connections_sockets()
    # ?. @4 X( }# i9 k5 [{" g9 N  E  u. e& v' ?% E( r
         //监听连接
      |# e# O3 l. n: v# M' P     new_sock= mysql_socket_accept(key_socket_client_connection, sock,
    . A! ]+ s9 [/ \3 |                                    (struct sockaddr *)(&cAddr), &length);
    ; x2 k1 h( v) R  t+ f7 y
    4 N: {3 D; H) v; E    if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock)): Q; A" T# X8 a# T. q
          thd->security_ctx->set_host((char*) my_localhost);
    , }' Q3 y" z( Q) t4 @
    ! y3 g# E/ m2 F0 J; F    //创建连接
    ' ]; P9 l. `3 ^( E    create_new_thread(thd);
    2 m) c6 Q' ]; _; n7 ?+ \( z! u( ]}3 F! l% a/ |! t8 k2 j; K! x
    9 Y- L6 e( M2 C. B- v9 Q, k
    //创建新线程处理处理用户连接
    ) `- ^* A# X  I+ astatic void create_new_thread(THD *thd){
    & i* X2 Q8 F, U% i, T8 p9 f
    3 b# ^9 S1 e: L6 n) N   thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
    ' m6 y) I# E  S; }% t" Q
    2 }) \5 d( k; p% C) o6 e3 ~8 q   //线程进了线程调度器) C' F! }$ a9 w- J
       MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   0 e0 c6 L1 r; T2 G" [+ Y3 d
    }
    9 z" k8 a0 x8 T; k: b/ Z2 _
    ( F! F0 `( L0 l. a3 t* Z, e: v  i# \- Q7 t  T
    至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
    9 C* [0 Z3 |6 Q( q0 \0 A) p  s
    . `7 B, Q% i3 z3 c: v. `0 c! j2 @6 V% m# N; e5 ~3 H, M( k* @
    2. 理解mysql是如何处理sql请求; B8 A) s  i5 y' t
    这里我以Insert操作为例稍微解剖下处理流程:
    * ~$ L$ @+ x* V; x' u* h# D& W2 u( u% W! t
    当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
    + v# o) Z: B8 w
    0 `: K- O9 C4 F7 p
    0 K  p; B' P! @; F. Cstatic scheduler_functions one_thread_per_connection_scheduler_functions=
    8 l6 H; @: C' o9 W% k6 G/ C# s{
    9 i# S/ r6 T' c0 {' y  K7 B6 `& Y  0,                                     // max_threads- Y9 i. n1 x8 p" \8 o' [
      NULL,                                  // init4 F1 L) U" n! @7 k  V! w& Q
      init_new_connection_handler_thread,    // init_new_connection_thread
    5 N! m! X4 ^  l4 O1 O6 i2 E) F  create_thread_to_handle_connection,    // add_connection
    3 [( b6 P* P+ a  ]' k. b  NULL,                                  // thd_wait_begin& ^0 f+ A# C) k. i
      NULL,                                  // thd_wait_end  G9 u  X1 _+ p: {' t6 }; q
      NULL,                                  // post_kill_notification
    0 m2 P, v/ m$ n: R  one_thread_per_connection_end,         // end_thread
    $ r- q2 |. w1 ^: T1 k' f( U- p  NULL,                                  // end
    % n) P: }* F+ @/ j, V1 X};
    ( o" b$ {8 v8 u5 h( f( u' ?
    ! P, `( o5 W, I( |; r$ F# v# n
    + X" Y' C! j1 q/ U# T. ]从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
    & q$ n. }; R0 Y" ]1 ~
    ) N4 d& _: E  n<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪1 i8 K3 ]+ u+ Y$ {

    2 K6 V1 q  `* X/ V# K" ]void create_thread_to_handle_connection(THD *thd)
    : [( Z1 V9 c. B{
    / ~! b5 z; t7 A& a5 A/ ^     if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,! R6 T$ |9 ^: e) Y3 F- j5 W% U
                                         handle_one_connection,(void*) thd))){}9 k5 O- q' }) T. e( A0 s  L5 Y
    }
    9 w3 S1 o8 L* S2 h: ?& L//触发回调函数  handle_one_connection
    7 @7 k! Q; W' s" s0 R/ y' Fpthread_handler_t handle_one_connection(void *arg)
    ' i3 J7 z( u! P% y4 D% A{3 o% n  i0 [+ |7 x; G- r3 y
         do_handle_one_connection(thd);
    3 W$ t8 q6 k8 j- i+ l}& |/ Q/ {& c+ k' J' ]9 K
    //继续处理
    0 S) c, ]" d! D0 Qvoid do_handle_one_connection(THD *thd_arg){1 g$ ]3 b# ^4 \: u, u" g
        while (thd_is_connection_alive(thd))
    7 I" y! q2 j9 m, C    {( l' h( P$ _* u* Y
          mysql_audit_release(thd);
    / y/ H# z( b# D/ q. f      if (do_command(thd))  break;  //这里的 do_command 继续处理7 h# R2 E' @) Z( k* N" _3 d6 U: ~+ b6 S
        }
    " T. r3 i* d" I}
    ; W$ k" {. j! P. N$ {//继续分发/ j) @: q( [, B% n  i, `. F3 H& R
    bool do_command(THD *thd)- T' u' z+ g9 H* R( F9 u) H4 S
    {
    + X8 a- K# k$ T    return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
    0 [$ O& f& l8 T  s* _. G8 F}; G1 i# {  _0 _5 e. J0 I
    bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
    # N# `  I9 X1 z& K7 [9 a{% I, V; P; |  V0 o
          switch (command) {
    % c; j' G9 H2 G$ @! {         case COM_INIT_DB: ....  break;/ p) S1 h8 F, L2 o! z( y
             ...
    9 |* q/ R. N' [3 j; C8 E. T         case COM_QUERY:   //查询语句:  insert xxxx
    0 b1 W# h, a8 g& x             mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析
    + i, T) ], y/ u( u6 E' L           break;" u. W. n. T% A$ e% h: t
          }* W" e& c8 k! q- |  Z
    }& X7 e$ L" U) M. V) y
    //sql解析模块
    * k5 R6 W" x& x  i( J* ]( K. `void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
    . y) z7 l7 k& g- e& W; M{% y7 J5 H- f8 ?- [; r5 Y
          error= mysql_execute_command(thd);: y0 G( y1 e' Q
    }
    4 r" \6 I; `2 K2 v* j5 M
    3 O6 O, y" H2 C' B
    , T1 l( u0 E( H4 H; [, ^# f" S<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。; ?( P* E: H7 q+ H: T9 _7 j

    * M9 |- F5 y/ @, ~" G9 C//继续执行
    % f9 x/ G+ |+ [/ Q$ C! W4 a  ~int mysql_execute_command(THD *thd)
    0 M5 D7 L2 D8 Q8 s1 e5 s8 I6 _{
    ; o+ |7 A/ n8 p4 h4 X2 i: W! p1 s1 F  switch (lex->sql_command)
    ; A6 d0 T3 v$ i2 E5 F2 T  v  {
    . P2 H* P+ @0 q1 z% U5 E8 m* g: v      case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;) L: A% ?6 m  ~) L  O* \) {7 G
    & e( y$ q% y# E/ v' y( d
          //这个 insert 就是我要追的
      j+ p& w, G% u/ G      case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
    2 n& }' Y- B7 q7 o; W. h                                              lex->update_list, lex->value_list,0 \6 p* T1 r% r6 w% f% S
                                                  lex->duplicates, lex->ignore);1 v, q" a; @; T8 |+ C
      }5 S# w' o1 I4 b
    }
    ) ], H+ T% s. J- h* K5 L//insert插入操作处理' N6 C8 b! }1 Y4 |# K7 D
    bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,3 d" n# Z  e5 v; {2 L: n8 Y
                      List<Item> &update_fields, List<Item> &update_values,
    & m6 f% ^5 z) q5 J& f+ ^5 q! o                  enum_duplicates duplic, bool ignore)) B( L/ z& Y% H& \
    {
    % w$ P2 ?) E$ K* l/ ^      while ((values= its++))
    # g: ?" e- P* V" f: H& Z      {
    ! X6 z- v4 e' W1 K. t. h5 A           error= write_record(thd, table, &info, &update);
    . A4 b9 h. `" T3 ]$ B      }
    % \. u1 G+ y" g}4 a4 H& \/ D' Q0 |
    //写入记录
    1 Z+ h! d1 `% |# K$ Oint write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
    6 c7 ]: |0 L" w6 q{
    1 T" L! Z& S- B6 k5 G, }5 P: V3 F% ]    if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)+ H0 T' J( N: {" a1 Z
        {
    + e- B$ S% b1 s6 t, m         // ha_write_row  重点是这个函数
    . c  _9 P! A5 p! N0 J9 B         while ((error=table->file->ha_write_row(table->record[0])))
      Y' [* m5 U6 L9 R5 u; @         {
    5 q2 [5 v8 Q" B  g* c+ ~             ....
    9 e, u4 N. A/ t) M' z         }$ {2 `: v- r  G; v2 v; g2 i
        }# T5 b1 o6 g4 h
    }7 A$ N3 f7 y; S

    $ ~& u/ @+ g/ h. y6 o: A: l' L
    ! s8 a  ]2 v" u3 c; d# q* `- _# O6 B" b  a+ B3 n2 j
    可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。0 u6 b( w: {5 ^2 w
    % F$ x/ f% S7 a! ?
    <3> 继续挖 ha_write_row
      ~, l  G3 z1 F, J) Y% E" p$ U; \" T& \- U  r- I3 d8 v
    int handler::ha_write_row(uchar *buf)- \0 v/ G- S, p3 _) \# e4 p: e% M! |
    {
    / t) [1 M: s6 x# D* q3 m    MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })( i$ B. }  [9 Z" O1 g# I! C
    }, y) `. S% m! e5 W  A
    * r/ U1 E% [) f3 V$ m+ y
    //这是一个虚方法
    : u( a7 T3 x/ w. g; n$ Q1 ?virtual int write_row(uchar *buf __attribute__((unused)))4 J% p0 c# Y* X2 ?+ b
    {/ u! _: ?' L) k8 g. S/ w
        return HA_ERR_WRONG_COMMAND;: Z6 F, U0 s" T% b2 g# a9 T  C8 F
    }
    2 c, }1 ~# ~0 b/ z9 p) F' L) B, o- J

    / u9 N9 S' y7 r看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;
    5 N- Z$ D6 k8 ?% ~6 @  a. x
    ' D( V' i9 Y; r9 V1 c3. 调用链图
    2 u" x7 u/ d4 {8 h! `- a4 G* |这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
    0 V+ D! V  U  n+ N, `7 |; x; Q
    + R) g- _& r7 W5 m4 ~# A7 S) L& C! A7 R

    $ e& u6 H" L/ X2 k& Y! d0 z三:总结. n/ U0 o# E( j# T- D! _9 j2 X" M
    大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。2 ~6 Q% {. C5 z7 g! j6 Z; l! E4 U
    ————————————————
    " v( c& d3 ~# B6 _9 `版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    # H. J( Z* o5 k$ c! ^4 Q$ ~原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
    " F. s/ _' E$ \
    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 03:34 , Processed in 0.413147 second(s), 51 queries .

    回顶部