QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 2952|回复: 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  }0 d* X+ q: z1 Q8 V1. 讲故事) u2 t4 N+ _) O* Z4 U
    最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。! V6 D9 C. l# D" Z: A

    , d. B3 X3 P3 h2 x; j+ b* @4 \. J二:了解架构图, k8 l. `$ c' d& d; G+ Q
    mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。' X- A8 g2 Z8 M" ]7 A
    + B4 q3 Z, u7 P1 G
    1. 从架构图入手4 ^# f! B) w, A- y  C% o
    大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
    # e& X5 e; _' L' H& D/ L9 E
    ! o1 k% ~' J, g4 W1 h) n" o% n! r4 D/ P, |' u& E! a* g  u
    6 ?* L) A9 I; \7 V/ q0 j! n0 @7 m
    其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~& k8 `! _2 t& ^
    2 p% T2 k2 _3 v- m* d/ V" s5 z
    2. 功能点介绍1 Y9 G; }% Q3 `: V
    MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
    5 s# u" Z9 _( ~7 u# G7 f' }% `% j$ w' S, Y- X& m* I
    <1> Client
    ! d" }5 t& J$ Q) b8 A9 y$ s9 _不同语言的sdk遵守mysql协议就可以与mysqld进行互通。$ _3 V3 K; y3 I* t+ {

    7 ?% t. k& \, C% e6 l# ?<2> Connection/Thread Pool# c7 Q- ]4 l) n+ y+ ]$ V4 o: y" f
    MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。$ b' ~9 |/ Q; f/ u! `

    . k0 H$ z5 @: b+ r<3> SqlInterface,Parse,Optimizer,Cache$ y9 T4 R! W* _; ]  f- V4 V7 q0 S
    对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。* _0 d& I& K1 Y8 z$ M

    # G) I: S& J& x1 G- a6 o<4> Storage Engines
    / O0 s* ~! S: l$ k2 O' s* b" K9 ]负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。
    % ]3 l. P$ d1 @' U
    : e# ]& i2 }& ]+ i0 J三: 源码分析
    2 s9 _" {4 ~$ V  _* h2 h关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。8 R6 P: e, F7 A9 N/ Z

    " U0 _/ X. y* @0 M" Q. b/ s1. 了解mysql是如何启动监听的
    $ s& h: U+ ?( _5 m5 x手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
    ) D2 ^/ n- Q* ]$ ?* a2 K; H( K# V8 J" q

    ' |2 F/ @& N" u) J5 Y+ `" t' C
    5 q* _# A8 |- ?7 C从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。- l+ H) w# l7 S$ ]2 o
    / C5 x8 c2 Q' o+ A& `  V
    <1> mysqld_main 入口函数 => sql/main.cc8 L( W4 L5 ^( y8 e7 j6 `. d
    ! c) ], ]% x: c& q9 ?) Q
    / r1 S( Z6 ?/ j% k6 ~8 C/ {2 T8 w
    extern int mysqld_main(int argc, char **argv);4 j' b% ~3 X4 M- w6 p* l

    8 i. J& ~( p# E9 O2 j, ], dint main(int argc, char **argv)# a$ ~& _$ [5 I0 M# S
    {6 _: n* \* o1 H/ L# B
      return mysqld_main(argc, argv);
    9 W( i7 M- R, G: J8 N}, \& @5 ?- M. f: j9 O; Z

    ' E; L! T* g& m8 a+ W0 x" I, z: w- w, R( B$ n7 c
    这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。% ~8 b2 Z* c  U. D
    * y, F; z% z1 x8 S. p, o: v
    <2> 创建监听+ k- K% z9 o. h# W" |' b: N
    0 f- X2 v1 u6 a, J9 O
    + k, K  m4 p0 }8 D. ~3 S' E
    int mysqld_main(int argc, char **argv)- _* ?4 W+ N3 B$ H
    {: u7 Q8 n( A" [% \/ A8 H9 }8 o
        //创建服务监听线程6 |% a9 `- n& w; U( X
        handle_connections_sockets();; N  l4 C1 u6 P. G) Q* \
    }
    . a( g+ d2 g+ `( U9 A0 ], R! [5 |; x
    void handle_connections_sockets()
    3 B- m6 ]' k5 _+ O6 Y0 {7 w6 R- V) a{
    . p; b, o* ^7 O1 Y5 ^$ h% @6 l     //监听连接& J+ t' D2 B+ S; d; i* V5 _1 G  T0 R
         new_sock= mysql_socket_accept(key_socket_client_connection, sock,, X5 H# W2 T$ j4 _6 Q
                                        (struct sockaddr *)(&cAddr), &length);
    ; \1 p- E5 K, Q2 V* N2 }
    " F0 H! o2 `; |1 _1 s    if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
    ; c. s2 Z7 [/ S/ t+ h5 f: B      thd->security_ctx->set_host((char*) my_localhost);( f% F" ]/ Y1 U2 s2 Z

    4 J, Z, n; g: u9 |6 C    //创建连接2 \$ L2 ^2 b) e8 C9 w1 Y
        create_new_thread(thd);. o5 t. L2 I& P* a
    }
    + u" T/ N* W' g, X. O8 ?* `1 b5 E8 h) n4 e# J8 d+ e! R) N
    //创建新线程处理处理用户连接
    5 b+ E3 ]2 Z8 |% t- E  i0 Gstatic void create_new_thread(THD *thd){
    0 m/ h1 B' A! l# g" ?$ W1 G6 G
    3 {/ u, t* A8 ?6 e3 ^   thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;8 i1 q2 M9 D& U
    & L. U2 b* @# l0 L: D
       //线程进了线程调度器" j, z6 `9 ^$ T: [$ y
       MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   $ A& c0 {; |+ f1 a
    }
      f$ ?6 H3 m2 X' p# W! A$ Z0 W, _/ P* V# p$ \/ K7 v% c2 }1 M
    # l) P0 R, O3 @1 h, \
    至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
    # P  F/ o' ?1 l9 _% \5 ^, y6 w  [2 w5 D7 X1 o  @) Y, a$ l

    ( y/ a: R- [' A' U6 M2. 理解mysql是如何处理sql请求
    % s0 F' I# E( z这里我以Insert操作为例稍微解剖下处理流程:
    # @8 d( l: R1 f. c; b5 U: `* ?1 v) T3 ?$ j+ u& b7 {) |
    当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。  \: t: o% d0 l! M# b$ g  Q: w

    % e5 L  S1 q5 Y
    2 M5 k- t- G& e7 _# h5 ostatic scheduler_functions one_thread_per_connection_scheduler_functions=
    7 a" |* R2 D8 f9 K$ B) x{
    2 x4 O! C# d- M) L7 P8 @7 L  0,                                     // max_threads" y8 V; `- A6 i# u
      NULL,                                  // init
    ' H5 e1 m! s( ^8 s+ m  init_new_connection_handler_thread,    // init_new_connection_thread
    7 _4 ?% |& h8 H/ g! N2 _  create_thread_to_handle_connection,    // add_connection- K: {& s3 w2 x3 G7 m
      NULL,                                  // thd_wait_begin7 x7 Q' {2 p5 y
      NULL,                                  // thd_wait_end
    + E+ V' s1 `: q* a9 w  NULL,                                  // post_kill_notification
    6 X6 O; M3 ?: f2 I  one_thread_per_connection_end,         // end_thread" M7 k4 X/ A3 q/ v6 h% ^
      NULL,                                  // end5 ^' d# K! D3 G. P2 k1 D3 ^/ t1 v
    };
    8 O- G  \; v2 e$ g& @/ q6 i9 R+ A8 J- i( ]3 v5 _

    , Z" H( H+ X! x: M从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
    ! Y6 X( O' W: H/ L) x0 A7 \* Y! S" G" ^5 ^
    <1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
      K& c1 V/ e1 c4 U
      C0 }1 w. |: x* \$ ?void create_thread_to_handle_connection(THD *thd)
    + \" U5 R/ ~9 r4 L* V. c5 ]& u{
    4 _* |1 T4 W7 ~0 o     if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
    + }  \3 T; C/ L$ u0 s                                     handle_one_connection,(void*) thd))){}0 c3 f' n$ m, x% R0 b
    }: G) o$ L* p7 r( K2 g
    //触发回调函数  handle_one_connection
    7 w7 ]7 a7 r2 j  F- apthread_handler_t handle_one_connection(void *arg); f% _5 q+ @( M; j" c
    {* T9 G- f( M7 O+ a  c3 v
         do_handle_one_connection(thd);
    ; n7 l# c' f- N}
    : }) r" {7 _, u  X//继续处理
    ; q* Q) i/ r% H2 l7 A; F% g- ?void do_handle_one_connection(THD *thd_arg){
    % P7 `8 W; O# D    while (thd_is_connection_alive(thd))2 n! E7 X, @& W5 X  V' l/ b$ W
        {, @% A! A; L, M) H/ l
          mysql_audit_release(thd);
    - U* Z/ r! {& ~      if (do_command(thd))  break;  //这里的 do_command 继续处理6 A. r" i7 n# T- \
        }& [2 u6 Q; e8 n8 H8 }
    }' d' l1 U1 R* d+ M  D, H' G
    //继续分发4 }7 o! Z, |" n& b5 m$ J
    bool do_command(THD *thd)' ^' K2 G7 ~5 @# |$ p. [
    {& w9 G% b# f" E# ^3 d
        return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
    0 o4 d& d% P) D1 M! }( X3 Q! c}
      A: k! k0 L* t2 P' \" D  t2 lbool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
    $ c& H4 ^& F7 R) r" v# G9 I) U# D4 ]{
    ' V- K0 x: R  @" Z0 f      switch (command) {: m3 T" B. M7 i" E1 _4 K; u
             case COM_INIT_DB: ....  break;
    ) c" S/ @1 S) x' j         ...
      ]/ W1 ?) T! H' d) I  g; N         case COM_QUERY:   //查询语句:  insert xxxx
    5 A# _$ i  V- _& L$ `             mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析% e2 Z7 F1 c' _( v( e7 S8 O. L, F
               break;. \3 h. G+ X. D6 e5 T3 I
          }: k: H3 P8 |' l0 y* P
    }
    1 x0 n0 d' D; \% {0 J//sql解析模块
    . Z+ t9 |$ j: N, mvoid mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)- D3 B& F: S8 K2 I. ?
    {
    : G& R. ]% \1 H2 {5 K' [      error= mysql_execute_command(thd);5 ?* N* a3 s, R, t, L) a& h
    }
    8 g, \7 x3 l- X' y( i7 }$ @2 l" G; Z$ w; g0 I. o+ y

    , j% P: I/ ]: |% ~) ~+ E( n<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
    4 F5 t: _! z5 {2 `2 s# w( Z3 Q. i9 i3 v  e! ]' O, `$ u% V
    //继续执行
    1 k% ?$ g( O6 y! T0 ~8 `- g) I4 zint mysql_execute_command(THD *thd)
    $ S9 B: l% ?# }/ f8 d{7 n+ P# [( P2 r
      switch (lex->sql_command)
    , Q( }: f5 B" k) {" I  {- A* e: X9 l7 u3 a$ _
          case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;
    4 b" f4 ?& \  k
    8 n' v2 L2 s$ I# u/ Z      //这个 insert 就是我要追的+ Q6 U1 A1 B1 x* g
          case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,) J. j: M* ]& S5 `" c0 ]
                                                  lex->update_list, lex->value_list,' n+ @3 B/ H; F7 W
                                                  lex->duplicates, lex->ignore);- K9 g4 p+ s1 {5 @
      }
    1 S- R$ A5 \: s2 T4 t1 a8 R}- |) p! m; r: i2 y9 Y
    //insert插入操作处理
    6 t  L1 {. z8 z+ d7 {0 Sbool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
    9 Q- Q- @! ]+ {( L* c                  List<Item> &update_fields, List<Item> &update_values, - \0 f1 Q2 g- F3 y# O( ]& _8 G2 D$ L
                      enum_duplicates duplic, bool ignore)+ K% H4 C4 i0 t& C6 U8 U4 u
    {8 O+ y+ j) [6 q4 c( O! I# R
          while ((values= its++))
    $ ]3 H) ~7 H- T      {
    . H7 Y& C+ J5 W/ r7 Z6 E           error= write_record(thd, table, &info, &update);* l- C$ L4 c- O9 i0 @8 ]1 R2 e6 g
          }7 \/ Z; z" Y  v& m
    }  u5 @3 S9 u+ P
    //写入记录
    ) ~* i& O0 b0 {4 Lint write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
    : x3 I  J  M8 R1 k{& C' L# a$ _- }
        if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)2 ~1 m: Q$ ]) U, N1 `: d9 N
        {0 D2 E2 n' @7 u, K% y9 }
             // ha_write_row  重点是这个函数# \, w; v- e: g: |4 u* c2 D
             while ((error=table->file->ha_write_row(table->record[0])))- m; B/ r+ [2 u+ J/ |& R
             {3 \7 t& U/ J+ B1 Z4 b' W" _/ u1 b3 |
                 ....
    0 n6 n5 y  a; {% O/ B  ]         }4 f% I! _8 [& f' F# `
        }
    ( V" L: A  m. {; V) H) g}+ s( j! f" h, f8 g9 D% V
    ! c2 i! [" Q/ }) C

      G8 o6 p) F# s0 a6 q' R. u+ U5 Z3 _9 t9 R. c7 m# V
    可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。: N' z0 U2 E3 x" s" C8 n  B
    * ?. S: U% s, r2 M' e
    <3> 继续挖 ha_write_row
    + P9 k- F; g: S0 ]3 k3 l3 z! c# I
    int handler::ha_write_row(uchar *buf)5 M. A: x: x# w* q- m: O2 G; \/ K
    {
    1 x' c6 r2 `1 W& v: ]; i8 l4 O    MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })% B8 h( T- P( c8 m$ i8 y8 a" W& g
    }
    ( O: [0 b& l- W7 C2 p9 C$ f5 u* ~' C8 O* E$ i
    //这是一个虚方法
    ( n" w- [0 |# z, d, d3 {# w( S0 [virtual int write_row(uchar *buf __attribute__((unused)))% a) Q: k; K/ |' f3 _% u  L% K
    {
    ( l% _/ y3 ~  N6 j# I    return HA_ERR_WRONG_COMMAND;
    9 ~& v5 X! G' i4 H. Q}
    $ {) i- ]* x1 s1 I9 p8 ]6 l, V. i4 R+ q. X' A

    6 E, y, f: O' L$ I: T9 Q看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;: e" K7 l" y6 M2 O- q
    - C. |/ {1 J6 c; v3 z, P+ N+ E
    3. 调用链图
    4 z6 u$ X5 A# g# e! ^1 h这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
    9 e* f# u( x0 C* W) a" `- X+ ^6 Z4 s/ d
    ' ~3 }! h: `/ S( M8 |& ?, h+ d
    0 _7 a$ L5 I# h; Z
    三:总结" T! X, y' e3 f3 J/ W
    大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。# H& c; K, a) I* N3 Z
    ————————————————
    8 m9 Q& f- ^3 G! l) ~* c版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。$ S4 @( L) L" Q& w5 h. j1 L
    原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415; S' w5 B/ s/ t- F8 a& z' F8 O
    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-8-9 15:23 , Processed in 0.385961 second(s), 51 queries .

    回顶部