QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3557|回复: 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 n; w9 ^* Z6 |; C1. 讲故事
    # v! `( V/ h, k. O/ M0 x6 d最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。1 W- V+ r# a1 H$ F. |- j9 P% U
    ( _4 j4 K1 F' u6 P/ O. \, Q
    二:了解架构图
    ; t4 J4 o  l4 G; Hmysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。" Z; X% t/ |- [( Y

    : O$ X! }5 ~5 J0 c+ ^" P, }# l. T1. 从架构图入手9 [- p  S8 x3 X4 u- f/ R
    大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。% E4 H/ R2 T# h9 H; R. ?

    9 e' g% Z$ B9 b: Z0 L; E
    5 l: @* I& Q/ X5 P6 O$ d% j$ q, N& r- Q) m: |7 S3 T- Z/ G8 E
    其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
    ( C+ O. f9 w! h1 I5 J" w5 |
    % g% M4 u5 l; h3 ]! d# v2. 功能点介绍" U% `9 u- M9 Q" J' D5 D6 q
    MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
    6 E6 D# z& p' d* z- C% d8 q  B* O( N( Z) u: o* ]% Y) p0 E
    <1> Client
    ) j1 O8 i1 H) d6 b9 B* m不同语言的sdk遵守mysql协议就可以与mysqld进行互通。" ?: r% t: A" k2 f0 K

    2 K$ `4 r5 W% d5 o* r; u( _' E$ k8 u<2> Connection/Thread Pool
    $ p  ~: m; ?' ^& O: _MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
    8 Y, T9 |/ c, F  {& c
      h5 O' C+ Z2 c) t6 z<3> SqlInterface,Parse,Optimizer,Cache
    ) s8 Q5 w! E, m/ `9 X$ ^7 b6 d! N对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
    : g0 _8 h! U/ k: i7 c
    9 f( H" Q8 N8 m' e1 i<4> Storage Engines
    & `1 [- a# h7 D# g3 H  D负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。3 E' s; H# T6 L1 r$ ?% J1 `

    . P9 ~3 M/ e5 D三: 源码分析: t7 g( B) z( h% Y" F
    关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
    # D3 v. Z" w) ]  u$ }+ x5 j
    + e( t& w& q  k* s0 y- S! W5 }1. 了解mysql是如何启动监听的
    4 ^" v5 p2 y. y# W手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
    + o' n% |% k! @: ^
    ! t; N. m) d. U" Z- A7 \
    6 p' G, U% j9 U, N. p! g: h) v; B. J7 F5 @
    从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
    1 x6 Y/ ?  E0 ]  g  i
    2 W( I7 Y- _1 G1 W: h6 G8 L2 Z/ L<1> mysqld_main 入口函数 => sql/main.cc2 y5 ^4 v" z+ a! a4 E- ]/ U7 q
    0 Z. U7 ?* R. y; X. p
    0 N0 \; Q2 ]) ?  W# [, h8 I( O
    extern int mysqld_main(int argc, char **argv);0 x% v: ]9 A$ J' m, G( s  v
    : a! c0 }# I% W' x: Z# u
    int main(int argc, char **argv)  D2 [2 ]+ o( [/ \. k; P; e
    {/ p" q! b- h) i. [6 w" R
      return mysqld_main(argc, argv);* O& ]6 X% F5 z! i
    }
    ! N: d8 ?9 ^* \3 c  I( j
    # h& W( l7 l! H
    ; Z4 ?9 |7 ]0 j这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
    2 H" X* S# s& R1 x) y% H9 G) O9 J% `: u2 X! u7 B
    <2> 创建监听6 H7 C. i, g4 ]1 \8 p

    , F- E4 T4 S* q; m  O. Z$ N; R4 a
    / L6 k! A) Z& r% I( W' {  nint mysqld_main(int argc, char **argv)
    / Q, j6 h) e+ i# Z/ m# O- z& K2 v{$ r; t7 N: ^7 _6 w
        //创建服务监听线程
    3 S+ D+ ]. k/ Q7 I* X    handle_connections_sockets();; G: `! D. I! ~* k
    }
    0 H8 D; Q4 e* E' ]: A$ x' t5 s5 b% h7 M6 w7 {5 t. B6 w4 g
    void handle_connections_sockets()4 G; P, R+ g% r# P
    {
    : B/ g& `- K7 d, p& \* g- G5 c     //监听连接6 }* G! e8 q7 W
         new_sock= mysql_socket_accept(key_socket_client_connection, sock,7 a5 i% ^" I% H3 `+ M, m
                                        (struct sockaddr *)(&cAddr), &length);9 U( Z0 J7 T$ [
    3 E) T% G4 o" y% g& X
        if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
    1 n& y; {8 o! D      thd->security_ctx->set_host((char*) my_localhost);6 p# n7 r2 H2 g9 }

    8 ?% n7 f% r* B7 d+ N9 L3 I/ u    //创建连接5 f+ m4 }) Z7 S' X6 k- ]
        create_new_thread(thd);
    8 Z) W* J0 e7 X- u9 f( O% w}
    1 i- a6 @2 C, x- Q' ~1 x$ y' T- q) e- p& L$ R" p
    //创建新线程处理处理用户连接
    ) w9 o3 k9 o+ P8 n) sstatic void create_new_thread(THD *thd){
    # D/ D0 J/ b$ C* U- n2 ^9 N4 H. m9 u8 F5 @4 m! {# ?! H/ o
       thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
      ^0 `% A; e6 g3 T. ^7 P
    . ]$ q# @& X4 F1 I- F$ {   //线程进了线程调度器
    + ?$ i* i3 C6 @) |   MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   1 u, B: I; D! R* T
    }
    4 b& Q, A% t( s) c6 x- _2 L
    , w* _6 }3 b; u8 z) ]6 @' u  m8 j3 S
      W3 [" Z1 M4 A3 }! U; h" n$ `, U至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
    4 i5 A3 y, `3 O+ @8 @8 j2 ^9 Z( t( l6 _1 o. K

    . l, |+ F2 T$ n0 x$ c- I2. 理解mysql是如何处理sql请求
    $ T9 J& g6 x- R( S9 A这里我以Insert操作为例稍微解剖下处理流程:  H3 U6 p- ?: |, K2 B
    : I/ w# W; a) t$ N
    当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。* Q5 f! w0 p# C# v

    1 x! K; k/ ?4 a/ R2 R0 M0 Z6 `, Q: o5 r+ C8 `5 ?
    static scheduler_functions one_thread_per_connection_scheduler_functions=/ f, b; M! @' P* x! e) M
    {
    9 r! ^% C3 a8 K1 Y. ]  n) B  0,                                     // max_threads
      E  z; O5 o3 y; _4 O  NULL,                                  // init
    # u% R9 a  q+ E1 a% l5 G  init_new_connection_handler_thread,    // init_new_connection_thread
    % O& H2 D% t/ @( x3 z+ Z4 ~6 E  create_thread_to_handle_connection,    // add_connection; ~9 R* m0 q" m- S
      NULL,                                  // thd_wait_begin
    ' r1 ^; l! ]* X; D, R! }) C) t  NULL,                                  // thd_wait_end; ?; q* ^5 _2 R7 y
      NULL,                                  // post_kill_notification
    $ n1 z, g/ z2 R$ q, }  one_thread_per_connection_end,         // end_thread
    4 m( Q6 H, v! N# l  NULL,                                  // end
    8 ]" U$ O9 U4 p, J};: U1 p5 c: ~+ L

    1 c+ @8 t  Y; B: ^1 o$ L% N; D( F3 E
    # M8 w4 q% A9 Y4 Z从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
    6 I$ Q% l$ r* V5 V
    8 N6 N6 B, G  e+ {; r. i( [<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪- I6 Q$ g  A4 P" L/ Q9 }; r8 }" F
    * n4 m3 t4 o9 e' t# j6 ?0 y
    void create_thread_to_handle_connection(THD *thd)0 L2 L  O" |5 c2 b* h
    {
    ! g8 O1 ]2 \, J3 V) z# i" q- E: \     if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
    " q0 J, w' j1 K) S) x( H7 S                                     handle_one_connection,(void*) thd))){}
    : `. Q+ K, D- l9 n- `}/ W1 Y6 ]6 h- U# r: }( L% b
    //触发回调函数  handle_one_connection
    1 B2 r# y: t) z: I" g! Q' `1 I8 Fpthread_handler_t handle_one_connection(void *arg)
    : a7 v$ q: X' J4 ~2 M{
    / b+ X+ R; N0 `9 C+ |* f4 r     do_handle_one_connection(thd);
    5 N, ?+ R( W& z$ B! i% n}/ S1 G; B' m/ R0 Z1 ~
    //继续处理
    7 v& F7 ]( G5 W8 v+ Gvoid do_handle_one_connection(THD *thd_arg){
    . Y( F& T2 G$ D( `; c( f    while (thd_is_connection_alive(thd))' B9 G  L/ g. [. t3 h: f$ A
        {
    3 e3 V3 h. i9 B% b/ s      mysql_audit_release(thd);
    6 W! v/ _; n8 e- F1 W* p; h      if (do_command(thd))  break;  //这里的 do_command 继续处理
    " m! E5 q" x" @" B$ i    }
    5 G' U8 I1 e: E3 b4 h0 G3 Y}
    6 m# g- B  w! {, X* U6 i//继续分发0 ?9 f! w+ t! Z
    bool do_command(THD *thd)
    1 I6 c# {7 x# G) z! O( H9 V{
    0 C, s+ Z# e2 }    return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
    # Y' B4 r$ V7 ?$ {% _}" V$ \% ^& t0 v, s( s9 L
    bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length), Q1 X9 ~4 Z7 T0 w' |# c  Z
    {/ ~' O+ i! B( w  I. _- [
          switch (command) {
    ( C) s% l  I; L  b, T+ S         case COM_INIT_DB: ....  break;
    2 D' V6 V8 x9 s4 j/ Z; @3 }$ v7 ]         ...' C( ?6 q- V$ S0 Y0 A& V
             case COM_QUERY:   //查询语句:  insert xxxx, H$ Y+ `% K6 |/ O, [
                 mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析
    7 ?0 H. \3 F9 \6 _8 c/ j           break;9 E* [6 |5 C0 |) g, q* o4 c
          }9 w+ d+ i$ M$ p* i; P( G8 q
    }
    . Y2 S& [) V8 d, I1 f" S3 W+ X//sql解析模块$ _9 f; f" c$ x0 G) p& v& Q2 Z( `
    void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)* E2 z/ q/ h; h/ z5 X" x7 i
    {
    8 o: z' S% o) S9 D) T      error= mysql_execute_command(thd);
      {. a. L* I, e3 d; S}$ s+ v$ T7 x6 n

    . i8 R+ H! D6 ?5 `7 B" \6 j6 T" y1 m2 {' I) S* \, M0 B: m2 u
    <2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
    ! I. |, @  a/ |
    2 Y9 o) w6 t; a; T- f/ g( c! |//继续执行
    - |+ Y1 J4 R& ^% nint mysql_execute_command(THD *thd)
    * T, h! C$ P  w! A{
    % i5 d6 F, [% K+ A' @  switch (lex->sql_command) ' T7 m: P, O0 @6 [
      {& ]1 X+ k+ P* V) }2 _7 S$ e- [
          case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;
    $ s5 M$ \3 e( z* z8 Y) u- A8 k5 B0 P7 w: V* D1 a) H  l8 t
          //这个 insert 就是我要追的
    : M* z9 ?! V' u2 r      case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,3 a% ~7 A6 x$ {8 o# d& S. {
                                                  lex->update_list, lex->value_list,
    0 g! l3 a* j5 g8 W7 T: l  C6 J                                              lex->duplicates, lex->ignore);
    - b8 J7 T3 ?1 t. [" y9 M8 ^  }  S7 t* Z7 ^+ @2 y
    }
    3 z$ T& u' Z4 W//insert插入操作处理7 a7 m/ Y. P9 v9 H9 F) z
    bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
    5 }) P4 k$ V5 P+ i; \* F7 p2 Q                  List<Item> &update_fields, List<Item> &update_values, / {$ O# o7 ~* ]: J/ t8 F/ ^
                      enum_duplicates duplic, bool ignore)
    $ K7 }; x- I' `7 Q{
    % ~8 c$ D- f& k+ f1 ^      while ((values= its++))
    6 a3 j2 i1 o) N. l      {& m5 E  I; R) R& s$ ?
               error= write_record(thd, table, &info, &update);
    : }% G3 w; \$ p6 s9 ~' [      }
    ( D8 F* p8 |% k: a' G}* ]5 ^( K: f" S
    //写入记录
    ; }# w: k$ J: h( p; `) V* [3 L$ z  ?+ {int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)! [+ d; H% [5 z4 |& C1 V
    {
    3 H, R& o. H, j3 V    if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
    * P9 u/ c3 [& i, Z; r1 h    {
    ) m" z# R6 g% h" h: T         // ha_write_row  重点是这个函数
    1 y8 r8 N$ [1 V* F# V  O, V         while ((error=table->file->ha_write_row(table->record[0])))2 s" {) Q3 e7 n8 G3 i/ [
             {
    + {( ], |/ B+ D; n             ....- L2 i6 h8 v2 V/ q- H' @
             }# z) d) Y8 F: w2 w, w  _+ W
        }
    7 U/ n* d1 ]3 z' N, R* z' ~}
    " m  u7 {' x% O4 p" B+ p) U/ M, u2 c/ E
    % r6 K, ?2 E  T& b6 h% d3 ?
    8 @/ i- S2 H' q) y- @6 m
    可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。, E- i- H7 E' f9 E. m
    1 W. W) j# N* m5 @
    <3> 继续挖 ha_write_row
    0 h: q1 |' s; Q, Y' m2 c% O" F  e  Y/ o  T8 }" m/ o
    int handler::ha_write_row(uchar *buf); [' D: o7 R( R$ j6 c( M) J: D
    {  K" X. p3 O( h7 H; k7 L
        MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
    ) _  Y) {, U7 x( E1 J! D}
    & a5 O$ |6 M0 d" ^; O4 I4 Y) q( @3 t  [+ ^+ w
    //这是一个虚方法0 E7 I( \( m+ Y
    virtual int write_row(uchar *buf __attribute__((unused)))
    . I: z& |& u& `! `{$ r( W' \4 e. S8 m. h
        return HA_ERR_WRONG_COMMAND;. A* A' Z/ `6 y# H0 r7 \
    }
    % u0 f6 X! K: E/ I9 |( ^
    3 c$ y3 h7 }# A( J+ j1 `. ]$ ?4 h6 A2 ~: k6 J: F) E1 b) ]) w* z: p7 ?2 V* p
    看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;! M) u' u, Y; @0 H/ e; Y
    % x; M, e4 e% ]( q" g! D
    3. 调用链图
    6 @: B6 d5 ^( F* P- r+ h这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
    ( U) |# c& U; I& r: \& e
    : _  P0 T( ^( o. Y
    3 l/ L4 t. l9 B1 n
    * n, W; |; @/ o三:总结
    7 j- Y8 n% Z, P+ i* t大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。
    ' C3 X5 q/ \  I6 I+ s- q' m2 i  ~, d————————————————4 c6 E% x8 B6 J( [
    版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。. J- s& Z' z$ |% B* T0 v
    原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415+ r  z8 @) K2 U+ W
    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-7-31 00:43 , Processed in 0.371539 second(s), 51 queries .

    回顶部