QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3525|回复: 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
    一:背景
    7 E6 x4 t' l  t3 e3 o. }7 M1. 讲故事
    ! p& V% h9 T+ C2 @$ u1 T最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。! i6 e, v* J! o2 \3 A
    6 c; L, Q, ?; |
    二:了解架构图
    % K* H+ Y, t: Jmysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。; S+ i2 H0 @, t" q6 [8 d
    0 K  b; l* k4 b+ c, V
    1. 从架构图入手0 @- d, U' @4 w8 R+ T6 }
    大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
    1 r! X0 Y# g+ u9 ]( D7 a1 I# T
    ) J/ h3 P1 m( B: U% E( I
      \, x4 K% z; z8 @$ Q# q- e
    4 ^0 F3 t+ g/ s6 a其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~4 A6 i7 A7 j0 P6 ^! \

    2 ^: M7 r0 e% n5 z5 H2. 功能点介绍
    # z- l' U! g7 I& v1 Y  R7 ^MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
    % P9 I9 o5 U- r4 e8 B& H* y8 ^( Y. G4 z! I: I" f0 C
    <1> Client/ @( o0 p' h2 ^" V
    不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
    / B) G% e, P5 [) {$ v5 e& h/ b$ P! j3 ?0 f! M
    <2> Connection/Thread Pool7 @; f9 |& I( C/ U* p. p, Y; g
    MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
      U; X: f7 y; C; h9 [  f% u0 \1 N& e: o( h3 @! M+ b7 f
    <3> SqlInterface,Parse,Optimizer,Cache9 s( h6 B9 C5 ?, S5 d. L/ C: }  P
    对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。& _5 O1 }2 o0 ^  h; ~: H# e

    - C3 L" d# N" [2 Y" p<4> Storage Engines
    # u+ A' p  B0 y' D负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。6 q) b- F2 o7 k2 T2 T1 }" T# R; D6 G
    ( M7 `) K3 R1 X( \8 n
    三: 源码分析
    ) i, j3 O7 Z" c+ \. f/ c关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。6 t0 s2 A; E  p

    ) w  L% |2 e; t' m/ C# Y, n6 C' O1. 了解mysql是如何启动监听的( b" ^$ [* F2 K& h( |+ l9 l  o
    手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。* e6 r7 Q# Z7 Z5 h8 Q" ^

    / x( K6 e9 w5 M; a, q$ x( j
    * A% |* x- ^3 ?$ L! _9 _4 E5 P; ~% Q1 H0 o# R# m) j
    从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。: m/ Y% q- ]& i3 j

      u. x6 G. M, d: k- S+ l<1> mysqld_main 入口函数 => sql/main.cc
    / K: G( @+ R" T* [3 A0 Q& ]' k& ]7 M5 |* o5 Y% f& i
    3 R$ }9 r! [1 d* ?
    extern int mysqld_main(int argc, char **argv);9 o- l- F1 h% \- v$ M+ K
    ! M  \" z- H8 K: g! b4 J9 Z
    int main(int argc, char **argv)
    * `; j' k* z2 ^& o& o) d{
    , a4 m& E0 G6 }1 E, F/ n  return mysqld_main(argc, argv);2 _# Z! s$ |- C; U, Z! w; R( W. q7 R
    }$ c" r1 ?% R7 [5 ?4 E* A. j
    0 i+ k$ u8 }) j; I) F2 O* Z' S

    / F. A; ~3 n* P/ c$ j7 }这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。# u" }( I- h4 @

    : ]  ^3 b! m* q3 l" T) Z<2> 创建监听" }" q4 t- ?4 a3 b# F% @& q
    9 ?7 F$ M4 p) N* W$ a

    / k5 ]2 ?1 H/ S1 g6 U) Nint mysqld_main(int argc, char **argv)
    2 J- L! g% [  y+ J+ ~{1 G* a- B1 K# [) c9 z; Q
        //创建服务监听线程! z' f$ t2 u- `# A% [1 _
        handle_connections_sockets();
    1 O" m8 a0 {/ D) A1 t6 x$ h, Y}
    ! \% r5 s0 m" q, ]+ ?, }' K0 a5 w0 Z  Q
    void handle_connections_sockets()
    ! k8 Y2 c) h9 J3 ~5 `1 q- k{
      T! g% n9 q% p) F; e" p4 V     //监听连接: b6 l: I) y- J- y% w* r, O- ~
         new_sock= mysql_socket_accept(key_socket_client_connection, sock,
    8 ]* `9 M5 E: C! l: x0 d                                    (struct sockaddr *)(&cAddr), &length);. G: e, z$ k% Y9 A9 ~

    : l0 P6 d0 o% P! G' U' @9 [, g    if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))4 |% d4 `% k9 u2 t! c
          thd->security_ctx->set_host((char*) my_localhost);  p1 i, u9 Y$ ^6 Z
    2 V+ [) k& o% j/ _
        //创建连接. X" E9 Z+ V' e/ d, }) Q
        create_new_thread(thd);
    8 V* b5 ]3 P0 `: E6 w}
    5 J# N- ~' Z: a3 O
    % K0 f# {1 K) r: |) m5 Q8 t9 d" }3 C//创建新线程处理处理用户连接
    - {' C+ @+ o* F" g* o" {static void create_new_thread(THD *thd){
    5 z9 }/ q' A5 p; [7 _" T# m/ B( c8 t0 L! Z; q0 w$ F0 D) i' I0 R3 I1 {
       thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
    & X/ c8 Q8 a; l+ g) L( R
    4 M; l1 E3 o, n) X6 y% y   //线程进了线程调度器+ H7 v- H9 l& D2 y" K( }2 {$ P
       MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   
    0 F# p: j6 N8 L# z  P}
    6 o( `, u# F0 \; G8 B4 g- j: i0 w* `0 K

    ; |! ]1 f( A- ~至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
    2 `0 }; H! c6 ?( ~$ h) R
      z1 R/ p) `3 ^, s! G. y) S
    0 O7 a" V$ ^" d3 D/ z2. 理解mysql是如何处理sql请求
    9 `8 I4 M- B( c这里我以Insert操作为例稍微解剖下处理流程:
    4 x5 w! S* b3 U5 r4 R5 N! M) r0 x, c5 X2 g# |  j
    当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
    : a* U' e! F( Q6 N& d% O0 y
    5 L0 L, p, P7 ^+ ^" H! c
    6 w# B9 o% Q! E( c8 d6 L, ~static scheduler_functions one_thread_per_connection_scheduler_functions=
    2 A3 [8 B) }" U, c5 y! N{- h* x# o/ W+ c8 W
      0,                                     // max_threads# Y$ [# {& c+ A% l' r2 B7 v
      NULL,                                  // init
    ' S. u7 ^2 S$ w9 V( Q' i3 L  init_new_connection_handler_thread,    // init_new_connection_thread* w. |8 N7 M7 ^; F, G4 C0 z. h
      create_thread_to_handle_connection,    // add_connection
    4 K; U* \. F( x# ?  NULL,                                  // thd_wait_begin
    1 k( B$ g* U( _" k, A' C  NULL,                                  // thd_wait_end
    * f$ }- l) T1 }; Q; V1 g  NULL,                                  // post_kill_notification
    2 y3 P4 _: z" T, _7 T; d+ I  one_thread_per_connection_end,         // end_thread
      f1 {/ V& N' }9 O  NULL,                                  // end
    4 `$ M3 m' M! U' {* X; X5 l1 W};
    3 Z+ r" w" f, ~
      ]4 R3 L/ D( K" h. ^1 R, H7 M5 m! [5 G' C- g) O" {
    从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。2 v. l5 o7 @. c/ f
    ) c/ T4 M, e* F% D3 t
    <1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪1 _0 U4 q% z6 s3 ~# k* ]4 S

    ( ~# i2 z$ Y/ D" d  ?void create_thread_to_handle_connection(THD *thd)6 k* U" H; b0 _$ X7 r
    {
    7 r+ y1 c* Y$ x$ R% p) n     if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,5 A  ?  c3 ~) _. i  C+ S$ J* I
                                         handle_one_connection,(void*) thd))){}
    2 |: M& P$ x4 W* j. N9 E! q}
    9 I% r) n# i/ Y  T" n8 T4 P//触发回调函数  handle_one_connection
    / J# L0 J6 D+ b0 {3 wpthread_handler_t handle_one_connection(void *arg)4 L2 P9 M# I  U! R6 E5 A
    {
    2 W# `& W- r) S5 @. ~/ e0 e     do_handle_one_connection(thd);
    / v3 }: K3 v4 u' w- H, k}
    4 i& h% V5 z+ d% [//继续处理' v0 L& V3 _( }5 L3 m
    void do_handle_one_connection(THD *thd_arg){
    ( ^2 l! _: g  v0 q4 n3 m9 [4 {    while (thd_is_connection_alive(thd))
    + F$ b' B& g  w# L/ X    {1 P) \* k; z4 f* H5 z4 R
          mysql_audit_release(thd);
    : }( E# A7 |. g+ h( k1 T, {  d+ f      if (do_command(thd))  break;  //这里的 do_command 继续处理
    / I% l0 j: y! t% x9 W0 C$ G+ P    }5 s" }6 L* q- w8 r9 ?: ]$ S
    }
    * o9 R  O) P3 G$ K//继续分发
    3 A( U% f& V5 d  v# Z( kbool do_command(THD *thd)
    1 P$ r" \8 ~9 w: s{* i( _  K/ C! Z5 C8 ~
        return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
    3 j; h5 d( B9 Q8 X1 E}
    ; P* [/ U* y5 ?: _bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
    6 u, s5 `  \1 M' }{
    ; R8 s# V6 M: F' E! f) O      switch (command) {  t' R! F) L- ?  x+ O/ ~( v
             case COM_INIT_DB: ....  break;9 Q9 G: k& o0 `/ ~& F
             ...! O3 K5 C# T! y0 q
             case COM_QUERY:   //查询语句:  insert xxxx
    6 p' C0 A. U# D$ ^8 a             mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析' X& K9 A& \6 M. y9 g
               break;
    7 t% e& H* \$ p) Y1 j6 a      }
    + X; Q) Y4 Q/ U1 P) {6 B, N}
    ; L4 o3 q8 O& r* Z; K9 T3 Q- G//sql解析模块
    5 H- u4 W5 A9 k$ rvoid mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
    - s6 N" ^/ Y7 b5 p  E{4 K) D+ h- i8 {( D% T
          error= mysql_execute_command(thd);$ M- L5 J) ]0 }( ~" I% j
    }! [9 y+ D' k% Z% Y7 L% p% Z' N
    9 W: c. V9 c& g
    , @, {7 D, z8 f. c) K2 \$ L* k( H
    <2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
    % @) j( a) f8 x* _; t$ L
    7 `: ^3 c/ I+ Q+ X) Q7 u" v) a//继续执行* f$ S* L5 n3 ^
    int mysql_execute_command(THD *thd)( S; R! A. l4 l
    {
    1 g' k5 N# r( V, U3 N  switch (lex->sql_command) 0 s" V1 J5 Y9 |* ^; F
      {
    . w$ C! @5 {# J$ `      case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;
    ) ?7 u' U0 ^, r& m7 w0 u0 R- h8 |- h2 n0 e5 u  O; u
          //这个 insert 就是我要追的3 k% m5 s% C. a8 V
          case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,/ j3 Y: B5 `1 e* T( C. I+ a
                                                  lex->update_list, lex->value_list,
    8 t( C: U/ y3 c) a; o                                              lex->duplicates, lex->ignore);0 h. Y) L* C  ?' E+ t
      }3 d9 X/ M" ]4 `3 Z0 u; @
    }3 y3 _: e3 Y; c% i( I8 ?
    //insert插入操作处理
    2 x& x- [7 K# H4 m$ Hbool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,  S" Y2 X# y# h6 [
                      List<Item> &update_fields, List<Item> &update_values,
    : T3 o2 q  Z& x6 n                  enum_duplicates duplic, bool ignore)
    . |1 P: Z8 O% d. B, X{7 u+ b8 Y$ E' A! m' N/ e* M' [
          while ((values= its++))
    2 Y5 H# c/ {( O7 e+ N9 H      {
    9 X1 L* U; y- B$ ]# G           error= write_record(thd, table, &info, &update);
    % U) O8 I3 h3 U+ ?      }3 f9 p8 H2 o! }1 q
    }8 ]: M; b. C( o+ P& L  A, p
    //写入记录
    # ]0 Z% H& ]8 eint write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
    0 [1 @% }* ]# }3 g{
    . j: H8 m& Y4 ?; L8 M. `9 Y  ^. D    if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
    ; @( d3 Y: s& z- R% H/ q$ f    {5 r) n  H1 L$ ^( y3 A( ]
             // ha_write_row  重点是这个函数9 \6 K5 G8 d9 c4 M$ [1 O
             while ((error=table->file->ha_write_row(table->record[0])))! @; h& d; c/ `, i0 Y1 H! x
             {
    5 k7 o7 Z5 t# S3 T! r( g             ....7 g7 l( D0 C; I  \# \/ I; p
             }+ B+ B1 C; C8 ~( g9 ]2 ]" c
        }
    : I- O6 n$ `8 B) j; `# V0 y}  s  |, f4 _: |* Y
    " n! g6 A3 J& H& N# ?' x5 [

    ) J3 ^. f! Y' Q) S: b8 c. S8 z* D. s2 \$ ]
    可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
    ' j* J; R" m( x5 b1 s: f% x% @2 x9 \( v; s5 O9 X7 x
    <3> 继续挖 ha_write_row
    ; o$ H8 R9 `; `
    2 j: h+ h$ J8 K2 w6 ~1 H$ gint handler::ha_write_row(uchar *buf)% |8 y7 B- Y& k: @: [
    {
    3 g6 z0 {& P, ^  @5 _' z& n- L    MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })$ }7 D( R  a! O3 f! q
    }: C3 w  [! g9 P- u/ b5 L9 Q' c
    ) G5 j; S1 R& z
    //这是一个虚方法- j  h, n' l# ~  ~- W2 V
    virtual int write_row(uchar *buf __attribute__((unused)))5 U' Z4 f$ F/ v5 U+ u
    {1 a0 Z! b# S. g% E" c+ L
        return HA_ERR_WRONG_COMMAND;
    ' _; U0 v- r1 a}8 I& M" ]4 g9 N$ I$ h

    . t/ q0 ^8 \  B% W+ I4 T5 t  Z6 J& j% E. T  I
    看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;) R7 J$ F& j  x- I8 P
    * J4 z" `0 Z0 z
    3. 调用链图
    . V  B& B2 W) V1 m这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
    ' t+ v3 G4 _( K+ X$ |& Y+ Q* g8 I5 O/ o; d# R
    5 P# l' K- ?* _! o
    9 C. E' V% n1 b( X: j7 Q, U  \
    三:总结
    " w+ R( C7 L; l- o大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。2 q$ Q4 H  R* ^* t, R/ z
    ————————————————; u# [) h1 t. \4 i) h' B
    版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。, V0 j' B$ f9 _* ]% [
    原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
      w6 A5 L' i* L6 x9 [
    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-10 18:56 , Processed in 0.629725 second(s), 51 queries .

    回顶部