QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3595|回复: 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
    一:背景
    # m" A  }8 A; k9 k1. 讲故事6 V4 u1 j+ e0 N
    最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
      d6 w' U; t* _1 A6 @! d$ A: c
    , D( R. n: X/ |1 U+ E0 k二:了解架构图
    # S# \1 v2 F0 I) g5 w0 k; {0 e( Fmysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
    . }: g) k2 Y1 d8 a  I. U- w9 x
    2 y/ g2 ^$ A* h3 x; O1. 从架构图入手" x" e/ P+ `6 l* U* _0 G- j
    大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。4 P# Y+ \; `2 t2 n$ B

    8 Z- s# l$ ]' i, a3 H3 W, v9 V3 M( k# K# G

    + N$ g; ^+ s2 u% t! g; s其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
    1 \: A) H/ G" i( J, Q0 ?) A
    0 C+ ]% B6 |4 i. A2. 功能点介绍
    3 d) }5 t9 a5 B2 mMySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
    1 W% L& D: T- }; p" ^* ?8 A1 n& G( F6 b" R9 D9 K* j
    <1> Client  b2 Y0 y6 B8 O+ ^
    不同语言的sdk遵守mysql协议就可以与mysqld进行互通。) v( v. c; I8 l& N9 [, {4 B$ ?

    ( z7 C* m' O+ m2 b<2> Connection/Thread Pool1 \! @7 S3 ?$ u! `- W' ?0 ^, ~# @8 a
    MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
    * b: q4 n3 ^0 I7 B9 o
    7 G# C' E8 C) q' @5 Q4 h<3> SqlInterface,Parse,Optimizer,Cache
    ! c% K3 }& t/ g, {. n  p对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
    $ @6 m$ t* T! I5 K* E$ P; s  p
    " p( B+ R0 `$ R! A4 M) j<4> Storage Engines
    / h( c8 b/ W4 V9 ~9 |" ^8 C6 w负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。& k/ i8 ?, h# q% x" y2 M4 S

    % f2 y& M+ q; i8 I7 N! v三: 源码分析
    6 z( d! F, {7 L9 ]- y' F关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。3 G* n" R: [$ K1 @; V$ |
    1 h# o5 M  t% Q* ~0 D
    1. 了解mysql是如何启动监听的: \, s1 s  K9 O/ Y9 M8 Y( o
    手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。  d( ?, t/ s0 E  m/ @) `

    / E% i5 S0 X  a# _( {
    ' b) o, F, X( R) @& s
    + O1 Z7 v( x8 h8 b  v1 Z6 u$ j从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
    / y  T9 S0 r: k
    . E% d4 D6 n& I$ ~; s6 I0 I2 K<1> mysqld_main 入口函数 => sql/main.cc
    % `' j; W, u+ D: w1 j# O" Y* b7 W  Y4 G! g; j
    0 B3 w) x6 o- U
    extern int mysqld_main(int argc, char **argv);
    : \! a* K2 T& X! n! ^' e! }- Z& g+ x. l0 w5 M9 a1 \, H" I/ g( _
    int main(int argc, char **argv)
    . U! K2 d2 J0 r  d( x$ _: O1 S$ D( B{
    8 Z8 y+ M4 A  Y6 m* z" n! @. @$ l' f  return mysqld_main(argc, argv);
    * R8 f' E" i- l3 h( [  n3 _0 ^}
    ! v: X( B& g7 G! B' q& L1 w$ k$ o- x" ]
    . s& k% D4 j1 C- o3 P
    这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。- ^) Z9 |# F, I; O( ]& `& C3 W
    9 t% [% X' r* J' R! }
    <2> 创建监听  l' o6 l0 u# o( e+ A0 p# n) y  f

    4 ]7 _0 d) }9 P4 J4 O
    ' K  [* [0 J, r% i& ?5 m; Eint mysqld_main(int argc, char **argv)# {6 x, W- [+ a1 s
    {
    / I" e0 m4 `( I" H2 z% G    //创建服务监听线程! {6 i# h& C1 F$ ^& y
        handle_connections_sockets();4 I+ M! R, w7 d  _+ G
    }
    9 U- E2 e4 y% X' U0 }  a5 n. {/ }5 S3 e  N1 D$ e, z5 G
    void handle_connections_sockets()
    / G& j* F& T8 n* ?4 e( \{5 Z( t3 P! f+ a4 V
         //监听连接
    % I6 [6 Q( ]3 G) r/ [: q7 @$ Z" \3 Z     new_sock= mysql_socket_accept(key_socket_client_connection, sock,
    4 S! e; }1 i( X0 Q/ N7 J$ R+ e, u                                    (struct sockaddr *)(&cAddr), &length);
    # w5 U- z1 U: s9 D$ x+ V' n: |8 v" {! X: w9 E
        if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
    2 x: e9 D7 J% a      thd->security_ctx->set_host((char*) my_localhost);/ b4 T& O' t+ o; H
    3 K( q5 \% z8 ~& ]  M
        //创建连接# O% X6 t9 D9 e+ Z
        create_new_thread(thd);+ ?0 `8 W" A7 [0 U7 P8 s
    }
    / q. z* w" y" A0 e! C1 S% G& Z1 m7 ?- A# j" h
    //创建新线程处理处理用户连接: y3 a. M; B/ a3 z3 w
    static void create_new_thread(THD *thd){
    8 ^. e4 F+ B6 [- x" i; }  \* t  x( C: ^5 F' ^6 w% P
       thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;1 X# S) ?% P. q, Z1 t7 B" M& e
    % e% g6 A! |; E7 Q
       //线程进了线程调度器0 F4 [: u9 C7 D9 F7 _! r7 ?  P
       MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   
    , c- r9 K, F9 A9 }& _}
    . y4 ~2 l7 s8 x8 w; T
    # ?% `4 K4 R+ m7 T- x& u3 }
    ! Z+ _; k7 q1 h% o  }+ _+ B: y至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
    5 z1 A' h- p* U- O1 v2 G, v# U# a) _2 S" v+ r! V8 h! {

    * v* b1 V/ l7 r" x, Q2. 理解mysql是如何处理sql请求
    0 L" S* T, ^5 x3 E" w. G这里我以Insert操作为例稍微解剖下处理流程:
    8 @. ~, K) H6 u$ H2 @% B: W  u5 h5 h2 f0 Q/ f: i
    当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。" b5 S: C. n+ E% E9 a9 W* P
    / M& q4 i- a) a; \! ?9 ^  s
    : |# t: x* {& I5 m: g
    static scheduler_functions one_thread_per_connection_scheduler_functions=
    5 F6 I7 b: v& Z& ]% z' V, E7 G{; {3 e0 l! b$ a+ A9 V/ ?  }
      0,                                     // max_threads" C/ _6 F& S3 ^% J
      NULL,                                  // init
    ( D1 x% L9 @0 F2 B$ s1 r$ e  init_new_connection_handler_thread,    // init_new_connection_thread4 ]9 Y$ p1 r: M3 D
      create_thread_to_handle_connection,    // add_connection
    - a( o- T: ~0 y) k" q  NULL,                                  // thd_wait_begin
    3 n, H. G9 E% X, m( ?7 P; C  NULL,                                  // thd_wait_end1 O0 L! z* A6 C9 E
      NULL,                                  // post_kill_notification
    2 S# y0 t9 O6 ^0 |  one_thread_per_connection_end,         // end_thread
    8 n, h2 y9 {5 v2 j) }& F4 b  NULL,                                  // end
    % j( D4 \& j" }/ d" o( [- m};9 V# T2 D  _! L

    5 U) c& F% p+ [* V8 P/ q
    . ~% a2 M. }3 u1 O8 g0 d! z4 q: H从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
    " J2 p: K$ ~- b2 t( N2 R, }" M
    : w: ~4 }8 J7 D: m% ~5 J5 Q<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
    " v- B& j/ a3 f9 ?! u. B
    # E& J+ R7 t: e: t9 \' Cvoid create_thread_to_handle_connection(THD *thd)
    5 z7 D0 }" a2 o" U) [* Y{
    - U# s8 T6 x, U! S3 s     if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,( ]3 p; u: m9 ^
                                         handle_one_connection,(void*) thd))){}7 n* Y/ I- U7 Z, B. d# p  }
    }
    5 J2 B  {3 e, d7 P0 A% o//触发回调函数  handle_one_connection' L& o9 Y5 i! e; S, A
    pthread_handler_t handle_one_connection(void *arg)) Y4 i8 M, L: ~/ ?
    {
    " K$ K/ d- Q& b     do_handle_one_connection(thd);0 B& E) f: y3 W
    }
    ! @, K9 ^2 k) s' y/ J% X//继续处理( |/ E1 M* j, M, v3 l) a% ]9 R& {
    void do_handle_one_connection(THD *thd_arg){
    . m) N. ^' s" U/ J& q4 V    while (thd_is_connection_alive(thd))9 L9 P/ K9 t1 w8 P
        {
    ! Q1 r3 q& ~6 J7 Q; Y7 ?) R      mysql_audit_release(thd);2 z1 b5 @$ @2 O( Q  W
          if (do_command(thd))  break;  //这里的 do_command 继续处理4 l  m+ v; Q$ u0 j' u( Z1 R/ R
        }
    3 a! |0 D' r- c5 w7 \; Z6 H% @- f* @. [}
    ; a- C0 Z& d( D3 C! m, v! s+ ~//继续分发% W. L* ?5 k. E7 [8 c- z0 s) H
    bool do_command(THD *thd)
    ) S/ F( h- u3 V( H, j& v{& Z( M( D( `, d: K, r0 b; Y
        return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
    ) O6 U! t8 B, N% b$ L# C}% A/ v* P  A( b$ F4 b
    bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)4 ^2 [! W% C* |- n$ ^
    {4 |7 t+ \- x8 y
          switch (command) {; i) m( m) a# e# B! ^' E6 ^
             case COM_INIT_DB: ....  break;
    ! A% G5 P" J5 Z/ V9 B         .../ _. l3 d* u" |
             case COM_QUERY:   //查询语句:  insert xxxx2 `3 x  C7 F' }% q2 }
                 mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析
    ' e4 n4 B# k& L3 Q% I' ]           break;
    : y" g( B+ \  {. v+ ?2 w' ^      }
    / }4 A! U4 B; n5 p' o}' K. ]# |7 S0 z/ e" ?  Z
    //sql解析模块, F( y; d5 c5 \9 q0 V, B" b8 l
    void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
    . l# {! `' `+ y8 [{; ^9 \1 e# K$ n7 U- m
          error= mysql_execute_command(thd);
    * H$ O1 k' p4 g+ r- k}0 C9 m0 H$ b% i6 F0 P0 Q  @
    : O! r9 P8 _. ^1 B' {6 i( g

    : j; e8 H( t! F% c( m: _. H<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。1 w5 I" n* H) Y

    5 L5 Z* {, A1 u) f8 @4 D//继续执行( H+ g$ m* V2 A1 g
    int mysql_execute_command(THD *thd)
    ' \' O# k: ^4 p$ c; ~{" M8 A" r7 q3 q$ L9 I+ Q+ w
      switch (lex->sql_command)
    $ m' Z# J; Y3 Z" f, h' {: p4 s  {4 A0 v. _3 M. V* Q5 u0 L
          case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;
    ) q! B- B3 s1 Q. {" b. K
    + C* y1 G5 S/ N      //这个 insert 就是我要追的
    % n- o0 S; B; b2 |      case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,, F5 R5 D1 Q* x8 ?7 o+ ^! x, p4 P
                                                  lex->update_list, lex->value_list,
    ) ~7 x8 C% B/ s. V                                              lex->duplicates, lex->ignore);2 U9 X: j/ W$ x+ C9 u+ F
      }
    / u! ^5 r6 C( X}5 u- Q9 l  f# ]! O: V4 V
    //insert插入操作处理
    # }+ K; H% y1 U/ F8 c# d: C, q  h, Abool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,6 q( o; }$ o1 g
                      List<Item> &update_fields, List<Item> &update_values, ; `" Y8 y3 l* D7 z3 x
                      enum_duplicates duplic, bool ignore)
    ) A# K/ p$ i3 l1 ]; U: s% ]% v{
    7 f$ O2 _& r1 H1 b; v) b0 @1 w      while ((values= its++))/ U# I+ c: F7 `! T
          {
    ) E+ D" g4 w- S3 B+ l7 }           error= write_record(thd, table, &info, &update);
    6 I5 r& }3 l5 E3 T      }2 E; v' w5 q* L: P
    }1 W7 A2 D* l  g  q, m2 G: g2 G3 Q1 P
    //写入记录
    . R6 d% E1 ^, O. y  Oint write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
    ( _$ Y/ s  v/ S9 K9 E/ v{
    . r) P$ E9 _) l$ E2 ~. B    if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
    1 D9 W& ]# X1 a' t/ V; S    {
    3 l* M4 I( m  |) ]$ Q7 J         // ha_write_row  重点是这个函数
    ) D( b, D+ P' Y* i- a         while ((error=table->file->ha_write_row(table->record[0])))8 U$ j  j& v* R9 W
             {
    7 M7 Q" Y+ H; V# S* Z1 \1 @" Y             ....4 \9 L  B# J& i: E
             }2 E: ?2 P8 k8 ^+ W
        }
    7 A6 x, M1 a1 x. F$ g+ w/ C}  f: ?) R+ i& E4 z/ _

    + ]! S, j9 H, h" {4 h# ^2 U8 ~# o4 `& p+ b, ?- p

    & o0 t) F0 @9 b' r7 W0 X  G可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。" x5 [- [  w: R" z2 L
    8 g$ y7 t$ n+ C$ ]5 H
    <3> 继续挖 ha_write_row0 i' v, ~5 r& e4 h6 a
    ; I1 x+ \7 H# }* R
    int handler::ha_write_row(uchar *buf). z- o' L: G/ N4 g' ^% x. s- g) U
    {
    8 C+ k! V. g3 z& @    MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })8 n- n4 {5 ^, G
    }
    ( P  O+ j) p! p
    1 a/ v# i2 g3 B( F+ o9 ^) s//这是一个虚方法
    2 M5 d3 ~2 \4 _5 {) G# ~* mvirtual int write_row(uchar *buf __attribute__((unused)))
    : ?4 v* y- P" ~! v0 B- L" q2 z& h{
    5 O$ q) F. Y- V0 G$ ^    return HA_ERR_WRONG_COMMAND;8 a7 l2 b/ M! r% ^- Y' \
    }
    3 t$ ^% @7 c/ j" u0 S
    : w9 Z! E' m: T3 G8 F+ l% c$ r. M" ]; ]
    看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;0 o& p$ b6 [1 }9 b

    . Y2 _7 C3 n5 j- c6 p" M3. 调用链图
    1 u" t- E7 k: ^9 r5 }0 ~+ X这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。* Q# n! Z9 ~& }, I
    7 S' b2 d1 l2 i

    ) M: M' b1 p4 D2 v, Y
    5 G8 @- w) l0 g, t- r1 `! A' \5 a三:总结
    7 e5 g; j# O" O/ J' ~  u大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。; l% D# k+ {( U7 Z6 D9 n) _3 r
    ————————————————
    & E# e  u9 M# S( L' R9 u6 C4 B. `版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    + J; E& m+ s; |/ p7 F, B& X原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
    9 X0 x2 V! I2 u! t5 y- a/ 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-9-13 06:25 , Processed in 0.325070 second(s), 51 queries .

    回顶部