QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3558|回复: 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
    一:背景* v5 Z: H& s4 k7 E. Q% m
    1. 讲故事
    ! \$ o7 F  Y1 H最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。/ o) F0 L" r2 [: ]8 I* x3 K6 a

    1 v0 d$ c' ?" h/ V# M二:了解架构图
    2 j, ~- k2 o$ P$ k) kmysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。& Q+ ]( C2 \4 i

    9 [  Z# G2 c6 P4 a: o1. 从架构图入手
    ( Y2 U& G2 k* p/ z0 S2 M3 t大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。; K0 X; I- A  M8 u% v. n
    1 y2 T3 b+ ~; l- Q/ H& n) F. v. G& ]
    " d' U* u( Y2 ?: s$ x3 \! b

    # z; X8 t2 b% K! ~! k: U其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
    9 p7 E. {8 V* q3 w/ x
    4 u7 d# U, c( k; Y8 P2. 功能点介绍" @8 e2 T) ?. Y7 F$ q5 Y8 d7 }! ~
    MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。5 \" b& C, f" s/ k4 R8 k3 y- u
    ! M. f2 U" [0 ^* G) M
    <1> Client1 ~/ R, ?4 q- B' Q
    不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
    3 M. R' P4 C! ^9 Q! }
    , l0 r3 V% B0 I: `<2> Connection/Thread Pool
    6 |; z2 W6 _; B( ]MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。0 f1 F; I, e7 L% W" |
    4 x8 v  O8 C. ^: N
    <3> SqlInterface,Parse,Optimizer,Cache
    - q3 l# C6 z. J9 J& _对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
    9 }, e! h: P8 {8 N6 G' ]; e8 {4 Y4 f8 J! m$ `
    <4> Storage Engines( z2 }# K, U# q2 e) ?
    负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。
    ! t/ D* h: j. b2 q  r9 F! m+ q5 w. `* ]) ~* B% M1 \
    三: 源码分析
    3 j* z5 N2 E0 z( H4 A. K2 D关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。' c8 w; Q# }9 l" Q
    ! g6 w, C6 X8 H4 T
    1. 了解mysql是如何启动监听的
    6 |, _$ V  @3 T手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。* N& M0 L" X; h  j& U- F& r8 h
    1 d5 P. ?% n5 k0 O  v1 ^$ `5 z. s/ h
    - l5 p* P/ H0 M" S% q) u

    " \/ L7 T4 {0 y( @5 @: a& u从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。" g# g" j7 P3 U$ r& o& I# F

    . ~" t% |" h& J* H- d<1> mysqld_main 入口函数 => sql/main.cc
    0 p) @" C5 r0 E3 G2 }) W7 h: e
    . G6 o, G# p# v1 v. p' a0 H! j- o( y- Y9 o8 P, o" f; \! Y
    extern int mysqld_main(int argc, char **argv);$ d3 a% v) a3 ~: j$ I4 C
    + t# n* s8 m" Q8 R8 a
    int main(int argc, char **argv)/ d/ M/ V, [3 r9 N6 A& O. {# ?: B" x
    {% x. B) P6 D" I4 N$ X# I+ Q4 j/ l
      return mysqld_main(argc, argv);5 w3 j1 p8 U5 E  C: u1 h2 B& L% s
    }
    , W: u* d- v0 {/ I4 m/ \' G1 L7 j8 R' Y
    2 N4 A% \6 b- Y' A
    这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
    ' [3 F0 @5 @7 r9 n/ R* f( x, X* L& @
    0 T0 w# ^. f2 Z% ~/ c2 _( @<2> 创建监听
    1 t% A, a( k3 g3 P" Q( q# ^' r: Q! s/ B$ \4 `: Y. s6 F

    % B" k5 O" r; c" J. fint mysqld_main(int argc, char **argv)
    $ @% ]9 y% m, c: R{
    . m* ^, L7 k# i. N0 s+ L    //创建服务监听线程
    # w- }5 L0 s. F4 H2 g    handle_connections_sockets();
      v7 ^( X, V5 [3 D/ u+ _}
    % x. i' E2 Y2 t2 ~% ?. ~
    6 W8 z' {" }# f1 A# G: P% Wvoid handle_connections_sockets()
    6 e7 [) F7 i& _{/ i' b( K/ e0 o3 T, @. |1 n2 Y( u+ ?
         //监听连接1 e2 W) q7 ^4 m
         new_sock= mysql_socket_accept(key_socket_client_connection, sock," F& F/ `1 }2 t) Z0 m' S0 p
                                        (struct sockaddr *)(&cAddr), &length);
    ! u1 F  r$ L; i& g2 n- L  ^5 \' O/ q8 ^% H9 m4 [
        if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))# M( _/ q9 W$ [3 C1 e7 z# v
          thd->security_ctx->set_host((char*) my_localhost);
    ! d: ~  h1 |7 ^9 I! G
    - z8 Q1 \+ A) a. Q6 l& g3 d" T6 v" [! S    //创建连接. c4 A% Q8 J8 n
        create_new_thread(thd);4 v2 W& G# C- `& J8 {4 D) i! Z
    }4 C+ H4 |$ Y# [! q) c
    - F; H: d" N/ }0 S; ]
    //创建新线程处理处理用户连接
    4 A0 v4 B& o0 X6 O( Astatic void create_new_thread(THD *thd){2 j! r; f' n* Q' t: @

    7 w. [5 v! s) e  c   thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
    . |* I  f. r  D* P3 k' T; u! k
    - e0 _  e4 n; T) _& G   //线程进了线程调度器
    0 m8 c* {/ Q$ ^8 |- D   MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   ; Z4 S  ?. H% ]; ~
    }2 ~, ?; q5 j4 t8 @4 N

    6 w( a; P. i( Z* |3 G
    0 G& b" k" B8 B4 `5 O至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
    : P! B4 J& j% ?1 [% N! w; y- `; d- N! I" c  X+ J7 P- a
    4 A/ {9 l7 A7 z4 ~' t% F
    2. 理解mysql是如何处理sql请求
    4 q, J' B0 q0 o3 P这里我以Insert操作为例稍微解剖下处理流程:8 P% N. Y$ H8 ]/ w' {2 p0 y

    % s0 Z" k) u2 ?& p4 g% t# y当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
    ; s  Q8 Y  t) A) e, c( O) l
    ; \# o$ `6 }, n& ]2 i9 N- p$ D6 N. y, }' ~5 k9 a
    static scheduler_functions one_thread_per_connection_scheduler_functions=
    4 w; w2 ], [0 ]% E{
    " U& `  S5 h; [1 m8 Z  0,                                     // max_threads5 Q3 [& m; y& A/ _. s) s
      NULL,                                  // init2 B7 r9 @/ R5 T* P; W. \) a
      init_new_connection_handler_thread,    // init_new_connection_thread" L: D& }- Z* u) z1 X+ z) C$ i
      create_thread_to_handle_connection,    // add_connection* s- Y7 y1 q( A$ |& t9 {
      NULL,                                  // thd_wait_begin
    9 C6 [' c: c/ `. ^2 C  NULL,                                  // thd_wait_end! }4 v: F3 c- Q& b
      NULL,                                  // post_kill_notification
    1 a9 k3 W3 I# w  one_thread_per_connection_end,         // end_thread7 q2 d( Y2 [. R& o, ^" z
      NULL,                                  // end
    0 k& _: E' F) S};; w0 D( `2 E9 `. f8 x0 ?" H1 U' x
    1 G  V8 n! j) o4 L  j: v; u; F; a

    6 o  X! F# h+ I* W从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。( |" s  ~8 J" M' n, Z; y
    7 r& w4 j. u" Y
    <1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
    . h" G% N8 r5 O5 p- g1 K9 R2 x/ d$ C9 `/ O/ Y
    void create_thread_to_handle_connection(THD *thd)
    2 g0 H/ B. s4 _) f8 g" z/ T8 C{# U  r4 B5 @# N! A. `) S4 s
         if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
    7 u9 m1 C6 g8 q7 \3 k                                     handle_one_connection,(void*) thd))){}
    7 }4 X. t; ]. W' U}
    , t$ K2 h- J6 O$ T- n5 U9 u  h* ?) `2 ]//触发回调函数  handle_one_connection
    9 r7 [0 v, H( A$ l* Qpthread_handler_t handle_one_connection(void *arg)
    " x9 ]1 i( @6 p' R{( K* S4 k# y9 F2 E6 R* H9 X' ?) Z
         do_handle_one_connection(thd);1 Y0 o! t6 `! N# M
    }* V# I- C" y9 H* ^5 `
    //继续处理
    9 v9 k  K  f  n& qvoid do_handle_one_connection(THD *thd_arg){8 B" n! Q: u2 _) }% B/ B5 q' F- p7 V
        while (thd_is_connection_alive(thd))4 Y5 Z% M, y: Y, K
        {! |/ ~4 [* F3 \& n8 H
          mysql_audit_release(thd);% h  W" |" k& n) ]1 Z
          if (do_command(thd))  break;  //这里的 do_command 继续处理
    & y3 o1 `7 ^  _2 ^    }
    / {5 I4 o( X6 u" Y6 K* l& ?}
    4 H& k) X; B% X& J) r; Q//继续分发: Z" f7 Z+ M- L
    bool do_command(THD *thd)
    7 G) H1 I! e) y{' Y( a8 C, e& G0 R
        return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
    8 F& K1 H* f9 T* S}
    ' Z. k% A5 z7 `% e( ~/ }bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)! \" v  }& [# A7 q6 X
    {
    ( S* c2 C' c7 T% t      switch (command) {" ~3 r7 ~' e( T8 j5 z
             case COM_INIT_DB: ....  break;2 J* l: S$ X, u8 @1 f1 T7 ]# t
             ...) S( g( X+ U$ V8 [7 [$ A3 h+ u
             case COM_QUERY:   //查询语句:  insert xxxx
    : A. X# M; k% n+ @3 K             mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析# t( H8 X- V9 q
               break;4 M& _( |) k2 f$ v* r8 K8 U
          }' @; q' X5 C+ d- R3 E  J5 R, c
    }
    7 y' X6 z% k  P//sql解析模块
    6 w* J  ~9 J2 G% n7 b) W# m+ c" ~3 Svoid mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)" @. X8 I4 q* ?& P  o- M
    {7 W( N- S3 Q# B5 _6 n% U" x
          error= mysql_execute_command(thd);4 B& y  M- \9 c( W$ w% g
    }' ]) E+ c/ W  C( ?$ G: n( {5 i
    , a% t5 L( L  |5 d) F8 _0 @( i8 p
    # [: Y" c3 k! |( `0 z* c* \
    <2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
    - `- J; v3 @+ E! a% n2 P6 l
    2 F" L' w2 J( M) B$ S5 z" l//继续执行
    % [' B# y4 O: ~, iint mysql_execute_command(THD *thd)
    / o$ F5 |# i& ?( x7 Z/ S0 L+ B{
    3 l5 p# A+ |( Q' P% ^, D  switch (lex->sql_command) # D0 r' V, \  I. i2 x! W
      {
    & L" Q/ b2 \# B# Q" k      case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;+ [) Q% o9 N' [5 B: `. x

    , ~% _0 i6 b) a1 l- S      //这个 insert 就是我要追的
    " V/ g; u2 s2 e! ?8 ~* L      case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
    ( [1 h. v8 R) I, W                                              lex->update_list, lex->value_list,. o' Z. b1 t, v5 B! q) {0 Q" C
                                                  lex->duplicates, lex->ignore);
    ) b. r& b4 i7 X+ k( u) [2 O8 Q2 p  }  ^7 ?9 ?1 R( x3 q9 q
    }
    - N2 }" w) @. _6 @4 ~//insert插入操作处理# o0 f) k3 [' i9 [+ @: E6 T
    bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
    " A) ~! ~7 y  e) P                  List<Item> &update_fields, List<Item> &update_values,
    2 E9 S& _. F+ W+ ]+ G+ I6 J                  enum_duplicates duplic, bool ignore)
    8 R4 J" J- R5 W/ G+ o{8 y; p/ N1 a) B1 \% R) C5 S
          while ((values= its++))
    7 k- L1 y2 q* w! X      {* m- ~- I7 q# u. p
               error= write_record(thd, table, &info, &update);
    8 J, i7 f$ {6 P( ^1 f  w$ e      }
    . j) _/ b$ L- v# w) J) K}9 E3 x0 H& L6 y9 ]+ R5 Q
    //写入记录. U- T& m. w" w7 [# ]/ E
    int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
    % y3 W, f" r3 W0 _( |# M{
    8 C( v3 T8 c. M$ F3 p1 z    if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)" S+ H, X( A9 r/ S5 W6 \' ]
        {
    8 ]. I- t% w% P& ~         // ha_write_row  重点是这个函数
    7 }2 m9 J7 {# b         while ((error=table->file->ha_write_row(table->record[0])))
    . _( q# s! f0 G3 U" Q: G9 c         {
    3 c8 T( h% s. g* T0 E$ Q             ....( Y  q; P& x0 Z4 r) [9 Q! {
             }
    3 e" H$ T+ V" e/ |. C. K; E    }
    # V! C$ U/ Z& N7 s, T, M* f4 ~; V}
    5 w# t$ a$ D: z; R, e2 a, O
    1 ^) D3 |; S4 @/ n  i0 p# g. E* O4 Y( S& R. X; j4 g3 F
    0 m% U2 S) T& Q0 n! |+ c
    可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
    , q# M9 \3 K& B2 B  g; O$ k; ?/ S: Z3 S
    7 f" F7 q8 |1 l  s<3> 继续挖 ha_write_row
    . _" j- t- L8 S, z0 `# R8 \! d8 R" {5 S+ U# }3 c8 K  S9 t
    int handler::ha_write_row(uchar *buf)" z0 T1 i. V% c) Y
    {
    6 J" f2 Q' i# b' }$ N% z- n- y    MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })+ B! c* h% b: E4 w6 Z: M3 c
    }( s; E8 I4 R/ a' ~  E+ \
    . N& Z* _# a9 x) v! W2 O4 W/ Q
    //这是一个虚方法
    $ s" H% y$ f) e. `5 jvirtual int write_row(uchar *buf __attribute__((unused)))
    7 C- ]# F" k4 a5 H( r" T{
    8 a& V0 ]7 I: W+ m  m$ c& X9 x7 N    return HA_ERR_WRONG_COMMAND;- I7 j2 [* Y, j$ w) {& f
    }, b4 L% n% G' C* Y) L

    6 j2 \) d" H& S6 J4 b9 Y' k0 m& S$ F9 X0 ]
    看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;7 k% k0 T0 r& ^! _- w% a0 ~5 K, S
    2 R$ [" |0 R) L0 e
    3. 调用链图. j: K5 F5 F7 f5 d4 b7 h
    这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。0 G4 G' f9 ^( f$ t& X3 u- y; H

    ; a( J& Q9 u. `; y( o
    4 u, f; J# J$ g( q% _
    2 r) a( f5 P9 S2 y3 z三:总结; X, p; b8 d5 T9 m
    大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。) o# y+ @' Y. v8 t6 h
    ————————————————
    1 M8 Z# l: C; `- `版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。! v1 }6 _$ I& v
    原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
    3 [- d/ E3 b) M+ x2 R: J
    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-7-31 01:31 , Processed in 0.404678 second(s), 51 queries .

    回顶部