QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3538|回复: 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
    一:背景
    ( L5 O! {8 s- o5 Q1. 讲故事
    2 m8 G2 t/ ~) Z最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
    # Z8 `% b( r6 w; S3 g! ]5 d- D* g' j* Q: o/ m- s% Z1 E1 ?9 E
    二:了解架构图* O% r4 m# N4 `; U# @/ _6 M  [
    mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
    1 f+ F5 }* ~1 q# e
    " Q- r5 }+ l! D- a8 ]1. 从架构图入手
    # W( d# i( z& j. @大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
    . k& P$ M! ~! N" s5 a7 a/ e. a& {6 k4 ]5 s& W$ S1 q
    8 X: {$ M" V6 }

    7 ^( [  p8 o" m9 V其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~% ]/ [$ n, L: ~! K2 y: f# C

    1 `+ c3 u+ d/ e: y" L! c) K2. 功能点介绍* i- J- t) _) S! r% g: [+ x
    MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。' D- O- Z. @8 \" X" ?& E$ y0 V
    3 J" N) N7 s6 b6 c- K
    <1> Client
    9 q' Q/ _5 Z0 T: i* U. S不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
    ! I7 s" L6 G: d; x/ `# u
    ! S  @. m( G2 c+ n2 R<2> Connection/Thread Pool4 I8 p! B) I1 E. o
    MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。( J: J% P* p9 Z$ v
    2 X! q7 J2 n3 b
    <3> SqlInterface,Parse,Optimizer,Cache
    ' @# U3 J0 e8 e4 z对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
    7 X' L6 z5 w, Z/ P, o; i9 r
    3 [8 R$ T9 n6 f! T# M2 b& @<4> Storage Engines
    / Y0 t  D3 [$ c4 S* H1 a负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。3 w& J1 l% {* @

    ! u1 s* O9 X* e6 U; r; S三: 源码分析) Y1 G! t: X( ?1 q9 G' L9 Z
    关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。$ }. ]" ^/ t: H4 F4 W& W
    : X8 q9 H9 \8 H2 E
    1. 了解mysql是如何启动监听的
    1 a4 G. [0 z  W9 s  x: o& r# T手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
    ' M0 q/ V% g4 B- P3 d& I$ T, M, `+ Z! q# @
    7 O: {/ \  F2 L1 P2 |

    ' D! P) l4 Z6 s* _  n6 v( C从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。5 l4 w" B; V. X8 R. P9 a" r

    & |: S3 z* r4 i& u<1> mysqld_main 入口函数 => sql/main.cc" d! ?1 ~' |1 F7 b8 p' Y/ A
    8 A7 B9 _# T5 {

    # G: B" ^) L" T( B6 s8 o9 xextern int mysqld_main(int argc, char **argv);7 ?7 S- l- ^0 q( u  p! _

    , S* G* q1 }$ K" l% o4 A# Qint main(int argc, char **argv)2 L$ ^( o  B4 p6 ^: W
    {
    3 R; s: ]3 w& q3 X3 G  return mysqld_main(argc, argv);+ a/ t0 x' l! ]* ^2 m& B
    }$ B3 P' F  C, `  y, \# A

    ( c" i$ i- D2 R9 H) h$ j) e0 |: V% b  s- R
    这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。0 U+ y" c( F. z' J( K. ?
    6 Y$ T8 j0 N- C+ p8 ~9 Q
    <2> 创建监听& G& a' [$ `$ H
    " x0 Y+ c* C2 S
    7 Y# S- o, V: W5 V# p
    int mysqld_main(int argc, char **argv)
    . D! G) r/ j& ~3 G{( p3 C5 I$ J. W8 v
        //创建服务监听线程
    ' t! C+ t/ z$ M3 _    handle_connections_sockets();- T' Y* @: ^$ i( s2 D* c) K7 a
    }
    3 I* g! E8 S+ W0 }8 d, o& z0 H% ?. f) C, i) H+ G# C- A1 i
    void handle_connections_sockets()( r- f1 R* \2 C( U
    {$ D; a2 K4 x% m& x* Z# |
         //监听连接4 `, m5 B, d" W* g/ O
         new_sock= mysql_socket_accept(key_socket_client_connection, sock,, B- Z- y7 o( i8 U5 H
                                        (struct sockaddr *)(&cAddr), &length);+ @2 c0 F. ?" u8 j' j9 b- b
    5 Q+ d) i9 h$ L7 E3 z
        if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
    7 n- H4 ]% |$ s% B$ f( K      thd->security_ctx->set_host((char*) my_localhost);
    % ^# p+ k# f1 P( A9 n' i8 O( m1 S% `% V+ N6 c+ v/ ]5 Z
        //创建连接/ X. R" u4 X& T: y9 f7 V8 D
        create_new_thread(thd);
    / S* M) S4 U7 l8 K% ]}
    & ]2 V! k4 u7 _% h' o
    + S- _$ w3 Y* I* N/ L//创建新线程处理处理用户连接
    1 u$ H! [. s" z4 f! d# bstatic void create_new_thread(THD *thd){0 N9 L% Z. J0 c0 }+ ?  F
    " a/ t+ [- ]* V
       thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
    ' R! m# T& J2 R/ |8 @/ ?- u% }' u4 \: M( I9 ~
       //线程进了线程调度器4 o; T0 n1 {  w8 S
       MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   
    1 p3 R8 h+ s2 T. U5 [}" L" w0 k. Q  `  O% r5 j  ]. z" R

    8 E/ U/ n  x3 @9 |( }( {8 S1 P5 b; ]$ X" R+ I$ k$ ?  U
    至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
    1 P) i) C* q1 l$ u4 [. O. W9 U3 j
    " X) y/ q7 K" O9 U- T1 x2 ?& ?( `
    2. 理解mysql是如何处理sql请求
    ' S& E- Y5 l- y/ m这里我以Insert操作为例稍微解剖下处理流程:
    . N' v7 L% Q1 |7 M4 O
    8 _3 d3 s. Y- ^$ h当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。! {% T2 |6 C0 @: C0 w7 N+ p
    % C. k/ c3 M; U  C* D: U+ x

    ; K( x1 w2 e$ bstatic scheduler_functions one_thread_per_connection_scheduler_functions=
    7 t- B8 o7 ]( {) J{
    % c! i! c' q1 m- }  0,                                     // max_threads
    ( J$ [* o. s6 ^1 B  NULL,                                  // init
    4 J& d& C  W$ k; R9 H& Z  init_new_connection_handler_thread,    // init_new_connection_thread
    % b( q  s( a0 D5 E+ U: q7 E# K  create_thread_to_handle_connection,    // add_connection0 d9 b* d% u- A
      NULL,                                  // thd_wait_begin# P! K0 x, ]- y$ E+ j- r& [9 ~3 |
      NULL,                                  // thd_wait_end
    6 O1 N2 d/ M( C7 \8 _- [  NULL,                                  // post_kill_notification
    ) o# Q* [+ n) o' j  one_thread_per_connection_end,         // end_thread6 {1 X7 i4 C. k. M9 B% n8 W
      NULL,                                  // end9 Z2 o1 T9 f( _
    };+ c$ `3 X/ G, ~( M5 z; J( r

    " W$ |1 u  f# {7 d  }5 Z- r
    6 K  A/ c/ F5 ~( }从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
    * g# U( \: p$ |8 e5 _% c( p6 s1 ^8 |7 v# u! {6 S) l
    <1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
    * S& A1 o6 x) W+ R7 J- d$ A. R
      ?% a3 m; H$ \2 S/ Wvoid create_thread_to_handle_connection(THD *thd)
    . o3 I& k9 ^% |4 g1 X{
    - ]" p; O0 X& b6 }! u& x- Y# U     if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
    2 `3 G3 j: O& f" p                                     handle_one_connection,(void*) thd))){}
    , n! Y: k0 h! F" E3 h" \}, E* e& F6 h5 p* x& i8 H7 `
    //触发回调函数  handle_one_connection
    7 z" R0 Y1 ~5 Q) `) H5 opthread_handler_t handle_one_connection(void *arg)
    7 T: `8 R/ G; Y) B{( J( Q; ?- {. D% a2 L5 K
         do_handle_one_connection(thd);
    % d% N- k/ @2 L! Z. ^}( F3 q5 z9 n; `& M% q0 V* D
    //继续处理
    ' j" L: Q. I3 g; T9 fvoid do_handle_one_connection(THD *thd_arg){
    7 n9 C5 V. ^0 |    while (thd_is_connection_alive(thd)). f$ v# [, \6 B% |$ W9 o
        {9 \3 P- y. Y! @
          mysql_audit_release(thd);
    : u3 t% W; l" _! T- R, {/ q$ M' I      if (do_command(thd))  break;  //这里的 do_command 继续处理
    8 G1 E4 s; N+ h0 W& n    }
    4 C8 B* p* e" v' E# |}
    $ q' e  O( ^  {, a& \' m! d  W//继续分发
    1 m" Z5 v0 }0 C5 S0 ?& B' n4 `bool do_command(THD *thd)
    9 {5 s! T4 u1 G{- I" [" o* k2 j& [5 L% {" Z! I, X7 f: T
        return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));$ s: V5 x- P' N( S* e9 V
    }# g) ]8 K5 o, y
    bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length); g1 I# t% N  s% j+ v/ e) n3 V$ U
    {
    . t% C2 ]0 j; v) L5 _+ I      switch (command) {1 P! c: {9 V6 U& s
             case COM_INIT_DB: ....  break;! ~+ D9 Y. N! p6 ?3 w, d1 k- t
             ...
    . q1 h3 Y6 T) P# X6 b) ?0 f         case COM_QUERY:   //查询语句:  insert xxxx
      W, v1 c( Y: S; B             mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析
    8 s; w6 o! R. L6 b7 y% h8 Z6 F) s           break;% G: C) Z* X4 [
          }
    ! U6 l5 _9 x9 m& u}4 [3 l1 L  R/ p' u+ W# I; e' ~
    //sql解析模块& |# x$ f# N0 ]
    void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)8 j  |' x7 X& C9 B! r. q
    {2 N( O, c# K) o8 w4 Q
          error= mysql_execute_command(thd);1 t' w: C3 I. Q5 L* N" \2 e
    }) L; X( f0 k& e( P) r
      e; C$ T, V5 T8 j; x2 C4 J. H

    6 s2 ?1 A% [! V6 \! n  E<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。  x  Q- k, P( n/ X3 w/ v+ m) I
    8 N* d& n# X9 z$ N8 q
    //继续执行
    4 v( @2 j* A) k. N: G% u+ Qint mysql_execute_command(THD *thd)2 U( i3 t2 u  j
    {3 H6 Q+ k+ J' w+ F- d
      switch (lex->sql_command) 7 Q* p- P2 R/ E
      {
    - |2 q4 V( p" d9 m      case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;
    " f( d' \5 u: `7 u. h( U" c5 x( X# W. _" v2 r
          //这个 insert 就是我要追的
    / p& d- i  u: ^2 A      case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
    * A1 p/ X9 S# `* T5 }# w                                              lex->update_list, lex->value_list,; E$ E1 @! L% z" A: X7 e
                                                  lex->duplicates, lex->ignore);
    4 w( W2 S! a0 x+ ~" a  }
    ) h, B. B- J, H5 h: L8 p}
    . Y) S1 e9 J! h# n//insert插入操作处理
    / ]+ M( w3 G4 T' h0 ~+ nbool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,3 Z6 q3 I5 P( D  J9 W* r; m) E" c
                      List<Item> &update_fields, List<Item> &update_values,
    % z9 ?+ f4 ]$ u' B                  enum_duplicates duplic, bool ignore), ~  A' C4 h  A
    {
    + d) u9 n' X) R9 O# S      while ((values= its++))
    ) t* h) L1 d, k* F      {+ W6 _" H- B! w  J) P
               error= write_record(thd, table, &info, &update);- Q7 Z- b: R4 c' o6 g
          }
    * W. v2 v! l, a4 ?8 d}
      f: i' a$ x# f3 R$ n//写入记录6 R' ]" }1 f9 G/ Y* }) K! J6 J2 H) _# Z
    int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
    * n; w: \7 A5 r; W/ p6 m6 n{
    5 T9 o! \3 `+ e) `    if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)% M8 V* p+ w' h) `2 L- o
        {
    0 i  G: T' M% n& ?         // ha_write_row  重点是这个函数2 \$ }1 \, |6 K- j' J1 J
             while ((error=table->file->ha_write_row(table->record[0])))
    0 b. y3 ], e2 x" B& R: w4 d3 L         {9 t: i9 _3 i% H9 B5 k5 m
                 ....+ v8 G$ L0 |% S- |
             }; A& m! q! @2 N9 n6 l" T
        }
    0 ]* d. Z) k# Y( D4 G}
    + A! i, B6 l* E, G/ X! E
    4 Z! i) Q7 v3 B2 e! U' X% P; B" I5 Q0 _6 X" s: h/ k7 i' T2 f
    8 \' b/ I: N7 O( P* r8 j9 j4 m6 @/ j
    可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
    8 c. i8 R( t6 w4 A! L( o
    6 F1 D* O1 ?7 E1 I0 p5 Q' R<3> 继续挖 ha_write_row2 b$ ~' R1 {( K: @' K0 T+ i

    ! P) u# s% v) i# @0 Z' |int handler::ha_write_row(uchar *buf)
    , N' m" ?" M, |) f1 ?) u{. B; k( \6 O! z' l: V, l4 ]4 A
        MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })9 u: y' q3 g' ^  ~
    }, C4 ^$ K) |% p' b3 v

    $ u/ H: T" P/ @5 O# T+ V% Q. e//这是一个虚方法3 {7 S4 M5 k2 ^1 P
    virtual int write_row(uchar *buf __attribute__((unused)))
    % J* m' N0 Z, r' l{
    ! S& z, ]: O5 P" w7 w- y, X    return HA_ERR_WRONG_COMMAND;
    1 V9 c% M* n+ t% y}5 ?5 j4 |0 t7 d2 j3 F" b

    1 |0 i, \6 L5 m2 }; x3 B3 b7 H+ c$ ~' c' j9 ?* b' B
    看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;( f& q& _4 q$ \+ i$ Q( Q2 r& b

    $ E' X: E" t8 x. P& D+ M1 {4 Z3. 调用链图# w' j% w9 T9 Q& e) B
    这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。, U. g) k$ D, d4 D2 {
    - G8 f! T5 l3 R3 _& R

    . O4 _: d* Q1 L! W* B4 \
    6 y1 A1 C5 D! Q. _+ l三:总结( @# s5 B3 [* s9 l
    大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。
    & X. _0 \7 k3 c" w$ p: G————————————————: m9 {" b9 X  N
    版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    ( |6 [" O0 m% Z) }( T) L& C原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
    9 j9 x3 L  g; U3 Y3 x  ^% T" L; Q. v" 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-6-22 16:22 , Processed in 0.395314 second(s), 50 queries .

    回顶部