QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3702|回复: 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
    一:背景$ ]$ Q' I2 H$ _% f; X. R
    1. 讲故事
    4 z9 J. N/ ~2 Y最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
    7 {& {9 L: T' y- b
    , E2 S3 \% S* o5 r" W) o. Q' ]二:了解架构图0 O0 o7 g2 J* ]. z1 A
    mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。$ h) l; X4 n  I/ c" m

    3 k+ W& W7 D. L+ W' C1. 从架构图入手7 F. o* g' x* q1 F6 B' \
    大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。* {! T* G8 _  ^
    / b' Y2 D0 w* T% r$ t

    ; T3 D& w2 G4 N) m9 v4 [! n2 Z- t3 B9 `" ^5 o
    其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~* I9 W+ [1 N) V& f
    : s7 z- T+ w# o
    2. 功能点介绍
    0 f" \, H8 }: A& ^1 jMySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。0 H' u8 u, z  t; ^2 A/ l! h1 P

    + N5 C1 O/ o+ e+ h# o% ^8 l<1> Client4 a& t7 }! B* @9 ]. W
    不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
    2 N& f' o7 i# @! v; y
    % D7 d  }6 @% C& p& \  P/ t<2> Connection/Thread Pool5 |7 `0 H# r# a" f4 N
    MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。- A% E; `, C, a5 z$ b$ ~
    8 N8 a* x' I7 C# P- S
    <3> SqlInterface,Parse,Optimizer,Cache
    8 h. ^$ h( M3 \2 r2 L对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。3 m" d) F' x5 z) b

    6 E- v$ Y& Q1 K( i7 d' N& ^<4> Storage Engines
    - L) }) l, {5 n负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。
    ' j2 P; _0 q% B0 V4 W9 ]
    * b# m( d7 M. b# w3 h三: 源码分析% b4 j% _% I$ I; V: h- B* i. \" J
    关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
    - X5 U2 @8 d7 k" `2 M# l) w
      W. g6 S4 B$ p& h1 e( l( m* U+ b* z1. 了解mysql是如何启动监听的
    , X3 ?7 |* W, z* J+ M手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。; }7 a% ?' a% e+ E7 n8 A' n& T

    , o6 Y; S2 k% {; f9 [& b. X
    ; G6 ?/ T0 M5 K! z' C
    , ?6 X* N9 d- L# \$ Y# t9 E从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。7 e+ b  k7 h1 l
    ' E7 s' w. h. |) @7 ~6 C
    <1> mysqld_main 入口函数 => sql/main.cc
    : F! ?+ m2 q% m- q4 k" X. _8 G6 A" A) U: u

    : L6 `0 E: h# q8 B$ h/ bextern int mysqld_main(int argc, char **argv);
    ' y$ _! a2 I# q( n( I0 G5 n" P& L0 |) g- Y- _& S
    int main(int argc, char **argv)$ f- n; }( a. r( P9 B. o
    {  U0 t% n# {6 Y) S) r/ ]/ Y0 E
      return mysqld_main(argc, argv);, t! A( r, v6 C" Q" ~* \; a9 D5 N# e4 M
    }/ v% ~; s# ^6 L4 s, U, f

    , x8 V! V/ a2 O/ T% q1 Z0 D% D( y1 \- W
    这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
    . s* h, ~2 k4 u$ U+ g" I2 N6 \+ D$ e- q7 ]6 E. V
    <2> 创建监听
    " }) f4 F6 B# y, g0 T! M; ^% |* I# o0 r# K7 _6 c; _' l% Y

    - m6 Q* J; L6 T$ A1 kint mysqld_main(int argc, char **argv)  \8 ~( K8 ^2 H: A/ d5 X
    {3 a0 B' Z- x2 B4 Y8 E0 I; ]
        //创建服务监听线程
    " ], _6 n2 X1 A/ U% ^# M: F    handle_connections_sockets();% Z' z8 A. P0 Y& V* M
    }2 W! H0 Y9 ~3 P4 H

    / x0 v) N1 k5 y4 L, b  r- Gvoid handle_connections_sockets()' R. s  n% S& ^2 F* A# N0 u2 {8 B
    {
      j/ z8 p1 t5 \% m& v     //监听连接. L+ V4 a/ z: r: t6 E6 I$ T
         new_sock= mysql_socket_accept(key_socket_client_connection, sock,8 |* o, u4 F, e% v
                                        (struct sockaddr *)(&cAddr), &length);: c6 b" _. ]4 s
    4 Z2 x$ @6 t+ A9 h( Y9 t7 }! @
        if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))' w9 k- C" j+ e. E) E, ^# Y+ T
          thd->security_ctx->set_host((char*) my_localhost);$ }$ q: i+ g, b/ l/ `, U2 Z

    " D* \( b5 V- }+ J    //创建连接! f' U) E; [- z! n; M
        create_new_thread(thd);
    4 B! d" E- \2 E- i. T1 U3 u}2 ]6 ^( ?; |$ H0 X8 K6 s( f
    - g0 W( R1 F( |7 J( y; Z4 V; O4 x4 }
    //创建新线程处理处理用户连接2 _2 j5 k7 ^. r; ?
    static void create_new_thread(THD *thd){$ Q; D. _% e; @4 ~0 _2 L9 _

    2 ?, H& b( x  ]2 P, |: U   thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
    3 f: v5 \  k1 ], [
    3 \7 S. R4 p3 x" E   //线程进了线程调度器
    ! |# H1 m9 d9 i/ N. t   MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   - c! Q; Z* X% K: `. Q
    }
    ( t- s0 `& t3 o  Y* s2 c# a8 _2 L- V! E) L: j; V
    6 B4 r0 J# g! |% `6 r) l
    至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
    2 v" U* J, U" r$ x: C% U0 b
    7 z  F  K0 X+ `- j5 N/ Y. I
    * m) }1 q  {. L9 W. ]& W4 F2. 理解mysql是如何处理sql请求
    - h1 C" {9 I  \6 B+ o  H这里我以Insert操作为例稍微解剖下处理流程:
    ; x/ I+ ]# i3 z4 `, D$ M; O% s* R4 y/ g8 e* c* a* M3 J
    当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。7 D& L% R' d$ @; W$ e+ R! Q1 `

    ( g9 q, x" |0 X, `1 m8 h
    7 k2 U4 t: u/ ^  Jstatic scheduler_functions one_thread_per_connection_scheduler_functions=" J* ?6 J, D& F1 O
    {- [9 f- c: ~- \7 @0 R& [  C
      0,                                     // max_threads
    9 v9 j! c7 L' y1 R3 n3 S  NULL,                                  // init8 b0 T& w% s+ y4 Z) Q6 c
      init_new_connection_handler_thread,    // init_new_connection_thread
    # d/ D+ v5 O! x6 h% {" r8 I+ [& x  create_thread_to_handle_connection,    // add_connection# s* R7 ^; A; [" i# c' }5 X
      NULL,                                  // thd_wait_begin
    3 m) Q" c: ^6 |. A) B- Z  NULL,                                  // thd_wait_end& q- ^% _5 L% \1 `
      NULL,                                  // post_kill_notification
    # D7 U' r5 }9 B3 K( n  one_thread_per_connection_end,         // end_thread& i+ F* ~+ W' f
      NULL,                                  // end
    % E6 ]' \  c- c( l) i+ u};
    6 ], [: s' }4 O  A" A1 v
    ( [4 a+ z( R7 w  a0 L, A4 j* ?4 u( G5 x& u1 n
    从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
    : w8 |6 k$ ^' Y+ o3 ~. `9 e. `
    5 p6 H8 j' D, c& I5 n7 H<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪9 h/ E; x) e. u* n
    6 t1 H5 }3 n9 Z: W% y8 i
    void create_thread_to_handle_connection(THD *thd)
    + s$ e/ N% Q1 ^& T' S: e7 c# P{
    . T+ y: |- M& l! g+ V1 z! V     if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,9 {& `0 [9 k' l: |
                                         handle_one_connection,(void*) thd))){}/ X) f1 d! J$ |9 h2 Q7 n
    }
    4 n, G$ T# S- P/ E4 j//触发回调函数  handle_one_connection  o7 M) N$ g( Q( C
    pthread_handler_t handle_one_connection(void *arg)
    8 k  x7 M: ~+ @. T2 T' T{
    4 h0 k3 p; `: `5 l     do_handle_one_connection(thd);
    1 @( n% }4 K* e* k4 L  e. P  y5 P  w}! B4 k7 k7 d6 }5 R
    //继续处理+ k# I7 @# {6 ~2 D4 W" C
    void do_handle_one_connection(THD *thd_arg){
    9 q9 ^, {& H2 O+ {' W    while (thd_is_connection_alive(thd))4 W! H6 ~5 a" K- g( x7 [- w
        {
    , S8 _9 D% e/ r      mysql_audit_release(thd);
    , }& ?' S, H) W$ R( {- I" W      if (do_command(thd))  break;  //这里的 do_command 继续处理
    $ F4 E* }0 ~" g8 i/ a% |    }
    " J. P- c$ v1 M- v1 w}) }" `) D9 r. m1 p4 u; d. w! s- }) `( P
    //继续分发6 c+ O1 K  X) a, f/ k9 h
    bool do_command(THD *thd)& H% T2 G' ~) f4 R
    {
    ; O" y) P) \" }9 L. r( t' T" y    return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
    ( _2 {: a" _. R" w2 h1 K}
    + g3 Z$ h1 \$ w9 U* Vbool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
    # c5 \7 a( Z2 t8 i9 E{
    ! [4 C* {1 K0 B/ W      switch (command) {; _8 r( T2 a& P% g9 u% O& t6 m
             case COM_INIT_DB: ....  break;
    ' c9 p- O! X5 P& L. o         ...* _' a# j' X: F. Y( y+ ?4 Z
             case COM_QUERY:   //查询语句:  insert xxxx4 @- F0 {, W/ f  R$ i! F% l
                 mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析
    1 |/ z. ?! ?1 \: x$ l. Q: ^9 D: W           break;% F2 F- c. P. d4 ^
          }" b7 e; M" T( ~. E
    }
    $ `: r1 P* L$ k7 z//sql解析模块
    - q  ?& d6 ?) e) |2 ~; dvoid mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)* b; j+ ?: M% F7 ?
    {0 h5 y8 V' ]" t5 F4 F- y0 |) l
          error= mysql_execute_command(thd);. i+ d' W- r, x
    }
    4 O2 }& u( A6 K1 z  L0 V, a
    7 S# ?5 l2 m4 i0 @" Y$ D5 v( |% Z/ `+ t# Z
    <2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
    / `" Z$ }. h. w" }; O+ z0 z2 B
    ) L' E( d( G: Y* u//继续执行
    ( {, Z7 p4 C7 i. O, Q3 r7 sint mysql_execute_command(THD *thd)) C% F+ i& G# Y' h& ^
    {
    * b; v8 u2 r% X7 n* M9 G# A3 |  switch (lex->sql_command)
    5 ^- t' t' n5 Z  {5 Y7 e! ?8 X7 m! S, t/ b6 P
          case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;
    ) j% s$ Y# u7 S. ~2 u6 F) B/ r/ t$ Z$ T0 S- Y$ r4 N
          //这个 insert 就是我要追的
      H3 ]4 M1 h3 K& H" m1 Z      case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,6 q* J( f. _* ^$ E3 o
                                                  lex->update_list, lex->value_list,0 |  {; U* N  C0 H5 u
                                                  lex->duplicates, lex->ignore);- g7 L+ @& ^9 g, k
      }
    ) Z. J$ ]8 {# M" e}; k$ F) w' u/ d0 j) @
    //insert插入操作处理8 l* Q( e3 Z! w+ P* c0 X2 `
    bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
    / I6 O3 [0 O3 H! [# {! z                  List<Item> &update_fields, List<Item> &update_values,
    8 m" G% C/ X& r3 G                  enum_duplicates duplic, bool ignore); \$ t! m) S+ N- Q
    {. `6 L5 _" b0 w& i0 {: B2 ^2 L
          while ((values= its++))- t2 p7 w" {+ K- S* a% U
          {
    ' j" B2 c; R5 d           error= write_record(thd, table, &info, &update);
    % a! T/ R) d. y3 C      }
    & J& L5 _+ t. T1 Z}
    8 A& [  o( N3 e8 g//写入记录8 M7 J3 P0 M* _! C6 E
    int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
      P1 O/ ]3 c# ?! V/ G{- S3 M( `# z0 ?- ~2 w; X) x2 j
        if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
    1 p, {* x; Y. |1 S; r    {* p1 T8 y. o- S/ v/ w1 G
             // ha_write_row  重点是这个函数( s" N7 R+ H$ g8 J. Z  R0 E
             while ((error=table->file->ha_write_row(table->record[0])))  \# X# v. M# K! t0 b$ ?
             {6 h' G. c5 x  K# X& I: d# g
                 ....1 G, L9 ~3 c7 S6 }8 H/ Z6 B& ?
             }8 H/ e2 v9 k3 n6 z6 x  W1 O
        }
    0 n4 x! E# f- `2 A, [" B) k& o( \}
    $ y( l- F9 N# U+ g0 l9 |. [% \# v7 ^. K/ `& e
    * ?! {( ^- F& X) _6 J. S

    5 P% B0 f* y) I5 V; Q7 q% m- _" q6 ]可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。. @( {! B; [8 W7 ?$ E1 _$ Z7 C
    ( w8 A* w' W! u2 g) I4 g3 [/ Q
    <3> 继续挖 ha_write_row
      P: Q: G' x4 K  X5 Y) z+ ]3 t& K+ Z( r( h5 w* q; C1 Z% ^8 q4 n
    int handler::ha_write_row(uchar *buf)' n6 R( Z" B. _7 u" U+ w
    {, f2 d7 E- Q/ M7 s. y
        MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
    8 |1 F8 P! ]' F8 R1 z}8 l4 {# g* a; L8 r9 e7 s2 e

    6 P% N. v4 j' j1 f+ d7 m//这是一个虚方法
    8 f, d0 B+ C! x- u1 h, V4 V6 ivirtual int write_row(uchar *buf __attribute__((unused)))
    " m* k' H* @& S( |8 t{
    " |* ?* I/ p% K; h+ w& r: }    return HA_ERR_WRONG_COMMAND;1 l8 @! S$ R" d8 H
    }
    1 z& j; i$ n; W1 ?( L, \- R
    6 _: c. A( S) q+ c( R; w. y
    , n, p, c  I8 y9 M# \9 C/ O- X看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;) q/ n$ [; b6 }. b
    2 N% Z! E: w# t9 |' n
    3. 调用链图7 w$ v0 W/ \9 k; u
    这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。- w( x2 ~, X9 |7 H( D9 w
    1 S* s& `6 F- N" V+ L
    0 \3 N+ Y! J2 B$ N

    5 D  D0 a9 @3 t三:总结4 s3 S* l$ j$ V, y
    大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。' {& V' |( Q" I. v
    ————————————————
    9 w- C8 S, h3 ]! X  E+ `版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。. v3 d4 t8 I: L/ Q* N$ X9 c
    原文链接:https://blog.csdn.net/huangxinchen520/article/details/1064874154 d  C5 k, H( _
    zan
    转播转播0 分享淘帖0 分享分享0 收藏收藏0 支持支持0 反对反对0 微信微信
    您需要登录后才可以回帖 登录 | 注册地址

    fastpost qq
    收缩
    • 电话咨询

    • 04714969085

    关于我们| 联系我们| 诚征英才| 对外合作| 产品服务| QQ

    手机版|Archiver| |繁體中文 手机客户端  

    蒙公网安备 15010502000194号

    Powered by Discuz! X2.5   © 2001-2013 数学建模网-数学中国 ( 蒙ICP备14002410号-3 蒙BBS备-0002号 )     论坛法律顾问:王兆丰

    GMT+8, 2026-11-9 19:21 , Processed in 0.207377 second(s), 51 queries .

    回顶部