QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3167|回复: 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
    一:背景9 l6 R5 h$ `/ M6 s$ E
    1. 讲故事
    % h' M& S7 l: l最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。  T# q5 f% K5 n4 u- W% `

    ; M. T( _! _) j5 O7 c$ _二:了解架构图, v( X4 u3 C( H9 E+ v; J7 }6 X" L$ Q, E' `
    mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。  M2 \3 {: s. q! _2 Z% H

    6 }$ I1 |5 ?' r8 U# ?1. 从架构图入手
    0 X9 j+ m) D% f6 j% i9 A$ A大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。$ d) `7 ^7 z/ I: \1 x2 e9 I% x
    % p  F1 Q1 e0 v% c" w1 C4 A
      G! y* ?4 g, Y! o' q

    1 x) a$ Z# |9 N* a# t4 ?其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~  `2 `6 b5 U% E3 i, B% g

    % N0 i: q5 `  }2. 功能点介绍
    / [# B$ |( h6 E4 A  ^0 U0 z. aMySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。/ p  }- X7 D) R4 _4 _+ ^( [: S

    % H2 ]3 J  r1 h3 R& l<1> Client
    4 [( s6 E- _0 Q1 A- U, N不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
    ( u9 B' Z% s! G: |9 Z. z) r+ w
    , W8 J& D5 h  Q% u  {2 u. j# C<2> Connection/Thread Pool
    5 x, t& z3 A: G5 cMySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
    0 E; T4 ?. B/ z* a3 @" J9 T5 i: e* n" N! N6 {( ]# O3 K1 x8 {
    <3> SqlInterface,Parse,Optimizer,Cache+ P0 j* k$ V. V; G% r9 `4 ?
    对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
      M- M" ^. |: j" o+ a, L( X8 U0 A7 f1 ~$ W7 h7 w$ t6 C
    <4> Storage Engines* X& R. D4 t% F3 v6 Z: ^
    负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。  n, _: Y0 ]% q7 r. i& J

    % {! g. K3 X% L! X: u三: 源码分析
    $ d3 d2 G0 p/ @/ m1 w2 F* @1 K关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。1 W5 }3 l$ D6 R
    / T2 f# ]2 v$ y
    1. 了解mysql是如何启动监听的( G7 a* N( H: \0 V" Z1 p5 [
    手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
    ! E! h4 c/ I$ O) r% ?% z& k4 y0 V/ X2 B; J! Q5 _! W' g

    . J$ a4 w# o" a
      |! D+ C% _0 y8 v; |从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。6 S- D, r; j8 D

    3 z( U; J" e) L  y+ f4 Z! k2 N<1> mysqld_main 入口函数 => sql/main.cc
    % X5 D+ N; \9 U% b' J# W5 e" r4 ?9 t/ c+ i+ X
    ! l% N' w% O; g. @! k/ O
    extern int mysqld_main(int argc, char **argv);/ n/ A, \2 m0 d" N* E5 ^. z! p7 O

    9 g1 j4 q6 V! R1 D  w$ cint main(int argc, char **argv)
    , i) j1 n6 x  e# l* s* F{) c# f. c  y! H$ r! ?4 n9 ], a
      return mysqld_main(argc, argv);, S6 K+ d4 y" I% ^4 Z$ g6 g4 U4 W$ ^" ^
    }
    0 o+ O# H& z+ i/ Q* k3 v' q) A5 O4 w% X9 @" e- Y

    9 R% i" G2 I4 z- |这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。% f5 B) p2 m4 P# J# x6 Z* N+ a

    ( b" k' \# I) I) e/ S<2> 创建监听9 z% ^% ?' K3 k- e. x5 r

    8 K5 o$ z) e4 s
    / v, [* G3 u4 ]: E) U9 ~. Cint mysqld_main(int argc, char **argv)
    # f3 X7 [. ^2 J{
    - t" e! O! V. t' x    //创建服务监听线程' r/ e% x3 U0 }$ f
        handle_connections_sockets();
    5 n) `- {/ T( x}1 W2 u/ J! Y. A9 a

    4 J6 N, p& [$ {" rvoid handle_connections_sockets()% r3 A, N8 Z1 F- V
    {
    4 p& A4 O  ^) o2 `, o( j+ H8 ?) u7 R     //监听连接0 ~5 w3 t( o$ J: K5 I, w' \; }) y
         new_sock= mysql_socket_accept(key_socket_client_connection, sock,, V  ]) z0 N2 z9 t6 c2 o
                                        (struct sockaddr *)(&cAddr), &length);
    1 \. E5 {) Z% E- d# w. J3 J) T
    + F7 d$ M) C# N. I, A- ]! M    if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))9 V& R; q3 h" \% |2 [/ |3 U
          thd->security_ctx->set_host((char*) my_localhost);
    / p6 V; |- K. {
    ! `; E2 U2 D& M9 P1 [    //创建连接! U( X& `  w6 l! I" h% @) |$ H0 e
        create_new_thread(thd);
    8 b- \6 z$ c4 B. X5 i9 o+ }5 _6 ]}* e! V! }% h( s  |7 p  t' N

    . j% A! l; V( s* Z! t//创建新线程处理处理用户连接. Z& N! F* D/ E: b4 n* ?8 V$ |
    static void create_new_thread(THD *thd){) A! t6 i6 @9 @5 e" m0 i0 j
    , ?8 O) T" M* G8 l* @7 Y, O6 e
       thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
    9 `6 E/ V. F% |9 s
    , B, V) T) f+ y6 ]. @/ Z# [   //线程进了线程调度器
    3 D* d4 a9 S) N9 {  u3 m& {: t   MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   
    / H& W4 y! l8 r}$ H1 I. o5 d, L
    % V; `& w- \9 _5 R. z( M7 \- P
    / }1 q6 V9 y3 j' L& X2 a
    至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。* ?$ X1 U) b& h; p9 ^6 ^0 p3 Z
    . P7 r4 ?6 K0 B- Q

    5 F* h3 v" q- q5 N2. 理解mysql是如何处理sql请求; J2 ?$ }$ x/ e) H- _% f
    这里我以Insert操作为例稍微解剖下处理流程:! D1 H1 I, q4 h# }2 j. h5 r

    0 `2 T# z( H: T$ i. M当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。( f( j2 {+ S5 d: E+ m9 K& T5 }
    7 f) |2 D, A; d9 Q4 \: }) }1 N

    $ c! Y' ?/ P: G2 I) s0 e1 a2 Kstatic scheduler_functions one_thread_per_connection_scheduler_functions=! g. V% A8 w# d: r
    {  U- G# j  B8 O
      0,                                     // max_threads
    - j# |/ e4 e3 B( a  NULL,                                  // init
    & Y% m" M- x) Z  init_new_connection_handler_thread,    // init_new_connection_thread! j8 G5 I# k( n6 Q0 \3 T
      create_thread_to_handle_connection,    // add_connection
    9 d/ i% t' M7 \2 P; ]9 t& x  NULL,                                  // thd_wait_begin. }0 i: G: a9 H2 T, ]; E
      NULL,                                  // thd_wait_end* i1 N4 I9 ], h0 S
      NULL,                                  // post_kill_notification% e3 B) s) I6 u# |
      one_thread_per_connection_end,         // end_thread
    ( I# ]% D1 Y' _8 Y' n  U  NULL,                                  // end
    2 U4 t7 x; g- _+ z& E% x2 e* v, s# X# k8 {};
    4 i, T$ o0 H' i3 V' L* U. F6 n" `
    / C1 g2 s% [  g0 S. ^  v: ^& @( S- i1 N1 k1 r. V( b. f
    从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。5 E9 D% ]) f$ f4 v7 q& F; |
    : J% ?+ }' `* @( W5 c
    <1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
    2 }: ^3 M3 }7 e' v
    6 w6 J2 \9 y1 q! U3 lvoid create_thread_to_handle_connection(THD *thd)' R! v1 J$ D4 \. g/ x0 d6 \5 {
    {1 B# y9 O- H: C; T* d4 t- E4 |
         if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
    " f8 g$ L% b+ P0 @) n" T' d" Y                                     handle_one_connection,(void*) thd))){}
    9 J# X% T  @" A5 j}
    8 l6 {4 i' |- F% z! |//触发回调函数  handle_one_connection
    ) n7 F- c+ X2 \0 \: N$ @  ^pthread_handler_t handle_one_connection(void *arg)$ d! Z$ w* B# U
    {
    " q# I$ G% L  M$ d. z: I- s9 z     do_handle_one_connection(thd);$ ?7 h7 B# O7 X( M2 R
    }
    + K6 ?( R6 t2 t; J1 h. T2 B//继续处理; A# R' Z9 U5 [6 X! S: r
    void do_handle_one_connection(THD *thd_arg){
    ! @% {) G7 |  E    while (thd_is_connection_alive(thd))
    + w5 |5 E5 {8 G( X! R$ b+ m1 x" M    {: C2 T: i( m( r# C; R' L7 B
          mysql_audit_release(thd);( K. j% r+ q9 ~& c
          if (do_command(thd))  break;  //这里的 do_command 继续处理
    * E8 ]% x0 V7 h    }0 z3 P- ^' }/ S3 G6 a
    }
    8 P1 y& A  I+ G3 ]  m* z! g. t  J//继续分发- |0 X- u# L& b- N
    bool do_command(THD *thd)6 ^1 h; k! h4 ~6 r. g, g
    {. Y, W& c5 S! k& C( w0 l& A
        return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
    , K  a8 @+ k: g. _+ Q# A}3 p' n, }8 {4 b: s
    bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
    , T1 |! N! [! s3 q& L+ \- o{
    9 g4 d6 ]- ?0 F/ B( _      switch (command) {
    0 ~8 }/ w- E0 }! G) `( L! z         case COM_INIT_DB: ....  break;
    & r( }2 k4 }0 n8 c: |5 B9 b, q         ...+ ~9 ]1 {7 e5 m3 y- i+ e8 e
             case COM_QUERY:   //查询语句:  insert xxxx
    : k# n2 d* T5 T8 m             mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析6 r. m( ^1 |$ F$ z  q: ~
               break;4 n4 f: C, j! D
          }
    " @" `2 ~! O9 j}
    5 ~. v, I. X3 s* b9 J7 J3 ?//sql解析模块7 ?6 D6 d. m; C+ N) j
    void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)' G. ?, L; t2 A2 X" D9 N' f3 z/ _2 K
    {
    / `. C5 C0 N" H4 D, E$ o      error= mysql_execute_command(thd);0 |) Q+ a- t; K
    }
    # g4 Z( R% V# ^4 o# q& @
      ]" J0 }2 P/ q" Y- ~( j% Z: [, R# Y  C% I
    <2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。3 q: S$ ^% X/ v1 S/ w1 u0 H
    3 V# @& K3 s% X  ^. G5 p$ R% w/ X$ q
    //继续执行
    $ ?1 v6 s) c9 W" F" b% Pint mysql_execute_command(THD *thd)
    ! F* D' r& F7 p' n( Y  N{
    3 J3 E1 W" i3 B8 h' c0 C5 f3 z2 g& c  switch (lex->sql_command) / `+ E8 C. \8 J& Q4 d' ^1 z: T
      {9 R; q3 ]" }2 G/ {( b
          case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;
    ! C8 C' E: I+ e# h! B; M
    - f8 V8 C3 {" p( m( Z      //这个 insert 就是我要追的/ E& b, V. j- X2 G1 L
          case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,4 I3 _+ L9 @3 _# c( i; I6 m
                                                  lex->update_list, lex->value_list,
    / ~: E  `# v* P3 P! O6 |. W6 X8 e) L                                              lex->duplicates, lex->ignore);+ l7 L/ q  J" g) ?0 W! j, K! @
      }4 T7 ]/ m3 s7 D9 W
    }4 q1 `! I  C& H7 O5 c0 a0 ]
    //insert插入操作处理5 l2 x/ G: I- m$ d
    bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,  _: _; V1 a( I
                      List<Item> &update_fields, List<Item> &update_values, 4 i+ k9 r6 U$ `  E
                      enum_duplicates duplic, bool ignore)1 _5 @3 O+ k! v8 N& D3 b) V, M+ F
    {
    0 P  Y! x. x, Y6 @5 [2 [( y" K      while ((values= its++))
    " W7 r3 V; N5 R; @      {
    / k2 \0 D' j4 n1 ~$ Q: G& k& \           error= write_record(thd, table, &info, &update);. @" h% b% T8 i! K7 o( ?5 s
          }
    ; z% C4 g$ }6 M6 ]' p}
    5 G0 b0 Z$ G7 V' f( U//写入记录
    3 m: K( K0 f/ {& j) D9 h  }6 k. ?int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)0 n( v* o2 D. o/ v3 A" i
    {
    ( D8 P* l4 N/ L' O# L    if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
    2 L$ R/ w6 H9 R9 r    {/ T0 D6 `) [# B* J- ?/ ~2 W1 o
             // ha_write_row  重点是这个函数
    ! F: D' \7 E* h: e( i2 g+ x7 j         while ((error=table->file->ha_write_row(table->record[0])))
    " F" B* Y" p3 c- u2 M4 }         {
    ; L/ p, I8 K2 A' Y3 Q. O' g             ....1 s7 G" |* y9 v( l3 T/ F
             }/ n4 o8 n; B, q' H# B
        }
    $ P  t; o/ H# F- v% J/ v9 t}% Z5 m: u7 ~  h& L. ]) \
    % @1 [- D4 P; V; j
    . \4 U4 v) i* d5 s

    8 i* K+ I- M1 _- n9 P9 `+ {$ M可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
    ; n0 _3 [  X/ \' z' b4 Q# L  \# d7 V. e+ d- p4 _
    <3> 继续挖 ha_write_row1 W2 V- U. Z: O7 Q$ R2 x

    ' f+ j, L% q) ~9 ~$ B8 p; `9 [int handler::ha_write_row(uchar *buf)
    . g, _( z9 e; D, i8 U{3 Z/ h/ g: E* J' X
        MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); }), i7 x/ u( K7 G; }$ d5 `
    }
      q- F8 \9 t, J: O' E- ?9 C" h" I% w, r0 N! I. @- B  ^. `
    //这是一个虚方法2 m7 G' T0 g, \: p+ m/ k
    virtual int write_row(uchar *buf __attribute__((unused)))
    ) e3 j4 ^5 ]/ f; H. X0 g: y3 E{; E0 V8 L% J& I/ m9 d' O
        return HA_ERR_WRONG_COMMAND;$ m) {9 w4 v3 R- `# k
    }
    & s8 a5 B' S% I- {0 [' v7 a. c  |6 Q

    0 \. l7 E7 Z7 u5 B; i+ s看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;
    2 n5 u0 D! a3 t! H) a+ z- {0 G& V( c  K0 W! `; z4 h: T
    3. 调用链图. U9 \( E1 y& Y& D1 w& y3 o
    这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。% ]- O' i' p9 d! Z
    ! J; ~* A  q) q  ?

    ; Q$ U/ D  B6 g1 l8 H" y
    7 y6 }6 n: b) O' x- {/ t/ o$ y! B三:总结# n" I: g" T4 h
    大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。
    , I3 \. H* z% `: P' ?& Z9 A————————————————
    & ^5 d. d' n5 R$ M% z( b/ v; X版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    : Z: c8 V2 O  ], m9 o* `) m6 ^+ Z原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
    . N# R1 V( D, L3 ]. Z6 A" N
    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, 2025-11-17 03:03 , Processed in 0.604833 second(s), 51 queries .

    回顶部