QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3704|回复: 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
    一:背景
    ! e2 C% E  W* d' k; M- N1. 讲故事
    ; L3 S% e0 F4 T5 c1 K/ |) D最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。6 Y. l4 Q, V8 z3 c5 H! p# D9 w

    & q9 U( J+ q% w' S9 t二:了解架构图
    0 b) P" D# W9 M$ W  C' O6 y/ Rmysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
    $ Z/ @* y/ s* U* s% @6 _
    + ~3 s7 L, p2 ~, M* y% W6 l1. 从架构图入手7 R3 c2 H5 C3 c2 r. }& L9 n
    大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。5 M9 W$ o5 t8 t

    & W. a) R& s7 U) t! P8 x& y
    : i5 ^( z5 Q2 b$ A# I6 w2 H7 k1 j" R9 l, h! c. G  ~! ?9 C/ U
    其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
    # B0 a; r  @6 H  [4 t/ P
    ) Y* f. p% P8 ?2. 功能点介绍$ y3 S  L8 I) j! u8 B! z& Y
    MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。, U1 g: r2 |) }, n

    0 F  c6 E" ~3 b<1> Client0 {4 E) u' [. r, m
    不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
    ' s7 j+ f2 U  x2 x
    ; ?$ @/ c+ U1 y) m<2> Connection/Thread Pool9 x: b7 P# B4 x
    MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。( S9 M& y! s% ?& w7 T; ^3 f" x
    9 S" H7 O* R  L  A8 `2 O
    <3> SqlInterface,Parse,Optimizer,Cache
    9 B' t8 l) {+ C  X对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。  H- i4 g4 G: A' o5 D# b5 G
    7 j) |: B* o9 W' T6 @; Q. T% G
    <4> Storage Engines  q" k+ C* t. ~. M% A
    负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。
    + p; z5 m! d& ?+ n% J
    ) D1 s6 Z6 Q" }三: 源码分析
    , R5 F3 I& s/ \- ~' @& L关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
    5 x# ?, J8 _+ D% l! r4 t1 i, z
    * ?4 F+ ?! `( u5 P1. 了解mysql是如何启动监听的  ]; V3 H- Z1 @; X6 M4 \6 {" `; G
    手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。$ m" L+ A/ o9 W; v% h

    5 @' w$ v4 D# Q4 g# H' v$ S0 t- @" @$ F( X. B, I" R

    $ O' h2 D0 G7 F从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
    ! W" M0 W8 g: J
    9 |! |* R3 V) m9 j8 k<1> mysqld_main 入口函数 => sql/main.cc1 ~7 Q, ~- k$ I8 n# p  n
    $ P5 V1 f2 @4 {" R/ f

    5 q. R" {' @2 c/ p- vextern int mysqld_main(int argc, char **argv);0 D4 f. m6 {+ u" k; \) U

    6 V! _; v- e4 [: r$ Sint main(int argc, char **argv)
    6 x$ j9 w, F- ]0 q  O& C! j{
    & {  I6 j, F9 t$ p  return mysqld_main(argc, argv);
    9 m$ B/ B$ a. _$ o- Q' w0 f- q$ @}
    ( ?1 L2 D& l, a5 B! C$ g4 E2 q2 a4 B- ?1 i
    1 ]$ v6 G5 Q$ m7 {- L' F
    这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
    : I" _/ D  g' L/ f$ j* r) r, s  ]: G# j
    <2> 创建监听  }$ U6 }( ]% Z4 C) |1 \

    - b! H7 f9 V$ n$ \) g& N2 r$ C& s& |, D# C1 s  Q  T. Q
    int mysqld_main(int argc, char **argv): n. R9 D+ x! a. f8 R8 N6 H* S% V
    {
    % d5 l. D6 L* D& Y    //创建服务监听线程
    $ ]" [( S/ w- _+ a) o6 @9 @    handle_connections_sockets();
    2 P3 M- \/ B6 L6 ?}
    & l8 t" N% F4 B7 N( Q: ^7 j! S: [1 Y% {. ~) m. E+ w
    void handle_connections_sockets()7 i, @0 {/ B$ d  c2 }& G
    {
    5 g0 `3 L" N3 q; Y     //监听连接& [% l0 Z2 z1 y# Q2 }  P& V' }
         new_sock= mysql_socket_accept(key_socket_client_connection, sock,- ~+ H/ x# k4 `2 ~  d- @. K
                                        (struct sockaddr *)(&cAddr), &length);1 [. {5 Y4 }+ L# j% o

    7 p3 S& M$ A) `+ N  P* l    if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock)); w- e" Y, @" X5 {5 I: P- V
          thd->security_ctx->set_host((char*) my_localhost);2 |# ^+ u; W) f( O1 H! c9 I5 L; p& H

    & r4 T( \- r! ]5 M+ K$ L9 n  J    //创建连接
    * F6 `% d' n" Y  r! J6 [# |    create_new_thread(thd);
    7 [0 l% R; y- `5 v# M7 s}  a' ]1 P* u0 C3 k- ~5 x
    . C  V" t5 O* R( }
    //创建新线程处理处理用户连接5 m& k5 a  I; _# ~
    static void create_new_thread(THD *thd){
    + j. E1 N; o2 Z6 m1 m" N8 k# v4 `& w6 |4 G
       thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
    " D' F1 I9 H  _3 W6 d1 {' K* Y0 R, T' h0 Y  ^3 b, j# c# s$ _6 q
       //线程进了线程调度器
    # w8 }+ P3 l" Q# j* B0 m  y   MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   2 i6 G8 c% {* a; s* i$ Q% f; ^% a8 }
    }+ S$ M4 ^( L& T' h4 A
    % ~; h4 A0 U% [  F( ^4 l. _
    ; ^& t# T2 D# B+ I( n
    至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
    0 W) M, ^3 m& t$ }  V: D# K
    # O, b8 W. Z* U  S" E9 r$ B; {: a- @1 E$ w
    2. 理解mysql是如何处理sql请求
    % n1 y! V2 C2 e( |- `2 [这里我以Insert操作为例稍微解剖下处理流程:
    7 d- e  b% Q3 a, B0 }9 p/ r5 [8 J7 N4 L9 O$ \
    当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
    . F; x" z) f" E5 Y1 r2 ?! {
    4 B3 o8 Q" l$ H* u; _
    2 @) T* Z& ]9 H! F3 Y  _7 V, c2 Astatic scheduler_functions one_thread_per_connection_scheduler_functions=
    % J: E% G# W* @) u6 i$ m1 }{
    8 B* {& y, f  k, v9 T; l  0,                                     // max_threads, u: y$ `$ }. f) N% r9 F3 @  x5 R
      NULL,                                  // init
    ; g! j3 D6 I% C1 c' F* H1 W! W3 m  r  init_new_connection_handler_thread,    // init_new_connection_thread3 {' [+ y( s, B% `: ]6 ]
      create_thread_to_handle_connection,    // add_connection
    8 {/ T; `* o2 U0 M4 J! R2 c  NULL,                                  // thd_wait_begin
    . M: r$ F% q) u  NULL,                                  // thd_wait_end* u0 D* M  b  O: s
      NULL,                                  // post_kill_notification" S* p  O) z" N( [
      one_thread_per_connection_end,         // end_thread
    , ~4 ]* w9 g+ D- q  NULL,                                  // end) l0 ]$ U. z2 Z4 `" J
    };
    5 q6 w; C/ O4 F4 K8 s4 w! F; z8 l; b$ Y

    ) l' z# E( E* t9 c  w) @从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
    3 ]7 t4 T3 z6 o
    " Y; X. }+ G7 C. c% ^<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪! I0 _3 j" ^1 H. q# u. m

    . i! J. W: L4 }. k* Q9 \! T4 ?. Dvoid create_thread_to_handle_connection(THD *thd)- u' U1 c8 S$ \, S  ~& ^/ d9 _
    {2 x& B  ^- u* H! }
         if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib," A0 {$ Z) y8 W' e6 `0 M
                                         handle_one_connection,(void*) thd))){}- B/ B4 ]% M5 _
    }
    * ^1 Y% q5 A4 k2 I& n! H& [; E//触发回调函数  handle_one_connection; ~2 o" ?1 L. ^" \- M* y
    pthread_handler_t handle_one_connection(void *arg)
    $ }9 V' P% y" b- O) h5 v{" s3 k* ]( K2 y' F! |3 z- Y5 G8 {$ P
         do_handle_one_connection(thd);
    # F% J0 c! G4 j: m' R}& [3 u$ r2 Y* s- w9 ~
    //继续处理+ D( u% z0 v" h' u
    void do_handle_one_connection(THD *thd_arg){' a. b9 O  ?6 c
        while (thd_is_connection_alive(thd))
    4 _. M  ^* W) G+ j9 v$ C    {4 H! }+ r; i' N5 l
          mysql_audit_release(thd);7 f2 R2 n* d' r. [
          if (do_command(thd))  break;  //这里的 do_command 继续处理5 V# m4 R% X% _) ]$ ~
        }/ V" O/ Z* i2 U( e- J
    }- C  d2 E- i) w' H: u0 A
    //继续分发1 F$ I" O! B! u3 R# O
    bool do_command(THD *thd)
    ; {( G" M8 r, t+ g. F' p{
    . K- S. }, P7 l( l# [2 A    return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));* q4 W& P6 f+ z! K- W7 R  o
    }
    5 Q4 E, F  p, P3 E, @bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
    , k$ S" n+ H6 g" q. ]4 A{
    2 L1 p: I) _4 V7 X' c* ~      switch (command) {
    + U" S2 h- s& W- e4 W         case COM_INIT_DB: ....  break;
    9 ?6 k" R6 E+ E7 }         ...  u( Q# J0 U& M, [. k
             case COM_QUERY:   //查询语句:  insert xxxx$ p' B3 Z3 |1 i) J) R! L6 j6 W
                 mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析' o' U" e- z) ^/ A' ]1 E. X
               break;
    8 F" {, q# Y: L4 G* H      }7 ^  v, s3 _: w# c, B
    }; t, H7 o! _; s; h
    //sql解析模块7 O( ~/ Q$ C. c& ?3 w
    void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
    ) u; i4 _3 l4 n5 p# I{6 O& L% \" c: M# U+ U$ L
          error= mysql_execute_command(thd);& s% F' r. R( U1 `
    }
    8 o, r; C( `% J0 ~* ^
    9 C! B4 e1 n; {, G1 G- X! C8 E
    ; f: r; b" s1 D<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。* f6 J# B- {3 K! H* e1 D
    ( X. A- V2 K6 _4 B$ \8 ]' k" J
    //继续执行
    : j: A5 ^$ N+ N# u; Sint mysql_execute_command(THD *thd)
    : b% d' |7 A9 e{
    / D* C8 H$ h9 R  R& \" Z  switch (lex->sql_command)
    " U# U: u. s  V6 L* m; t  {2 |8 _2 P7 l( ^' s
          case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;$ n% j) [  S/ M5 H: W4 V
    8 `0 [: e! @( N
          //这个 insert 就是我要追的5 ~4 m9 u; U; u& ^
          case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
    ; M& w6 I/ V  F. e                                              lex->update_list, lex->value_list,
    9 j3 {1 N& F# ^- }0 o& C: d                                              lex->duplicates, lex->ignore);$ B! l0 Z4 f/ [  g: k# T
      }2 T8 ]( W: z, \* q
    }, \2 F1 P9 R* ~7 o
    //insert插入操作处理
    , l) e2 Y1 y. f* j- obool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
    1 }; a7 [& n9 t" c) z, |7 T$ {" M                  List<Item> &update_fields, List<Item> &update_values, $ f& g, z) X# b% {  h
                      enum_duplicates duplic, bool ignore)5 o1 u1 N9 o& w0 z9 T6 @
    {
    " C0 S' a3 m6 w- _  H  s7 Y: _7 |      while ((values= its++)): A( N; ]4 O/ n& _7 k/ P: T% w
          {
    ) J; z/ R& u+ T           error= write_record(thd, table, &info, &update);
    : p% P5 _( e  r( ]% V" {' c$ F      }' J& C* ]: N. X2 @
    }
    " [3 I/ M, A  \4 _//写入记录8 S8 y: i( [. h* `- h: [
    int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update), h- D6 g* ]% r6 Y4 z5 c
    {" n2 T$ Z, m! q4 c) P: g6 K: j, o1 V
        if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
    ) }2 u' V5 O' {' S( H0 w. X    {: P9 e. V; A: n5 Y% D) I
             // ha_write_row  重点是这个函数
    2 {: {9 v( G+ h" }9 l         while ((error=table->file->ha_write_row(table->record[0])))
    ( U# v/ J( g$ G' z9 x7 E3 W1 }         {
    / \! s/ @+ S: v3 E) T             ....2 Q! o6 j7 q( l. O3 e
             }
    * Q* z' R8 F/ e$ c    }
    5 h( R( l$ L0 T}
    % K: [: H1 t& x* v# F
    . b6 Z$ w! I- m, e( ~# p6 W0 ~* T4 ^7 U6 ]$ g

    # k/ u% [6 l* \7 V6 Q7 i可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
    ; w; D& @& E' o# u/ T
    3 r  p7 j3 v! |. R<3> 继续挖 ha_write_row
    9 {( [) D7 ~# b) j) D9 ?/ l4 T. }6 O  S% @; ?
    int handler::ha_write_row(uchar *buf)! C0 o1 L2 n! I9 d/ y& N- H
    {
    5 U6 t  j$ M/ j, m1 \5 C0 X    MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })$ e9 V% z  @; E1 l6 X: p4 n
    }7 @5 X6 ~0 ]) A( \
    % O5 i4 [" f! ~0 d2 P  B4 T. g7 S. L
    //这是一个虚方法
    , O2 H1 c2 ^  ]" g3 q: Fvirtual int write_row(uchar *buf __attribute__((unused)))
    8 E5 l& B3 T" D) p) _+ P* {{; g' U$ [8 a+ b/ P& M* P
        return HA_ERR_WRONG_COMMAND;
    ' C# l0 W" E8 I  O+ b3 D. S* N" x! w}
    3 d* {1 ?# d7 L' @
    4 H* S( {* L: D# e- ~4 E0 o  ]: v8 C0 S
    看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;! u) N/ y9 H- U- A

    * U7 }1 e' o0 d1 l2 y) {3. 调用链图& p0 J8 `; X; H$ }
    这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。3 |' l7 e$ j0 W1 X" s( f

    3 X7 M- \( L+ a
    ' ^8 n: L- x, p% i5 z
    ( f/ T' G( E  x6 ~% p三:总结% F. Z3 P2 s. y) R- v  j0 z) M. D
    大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。5 F6 ?* H, Z5 K3 @0 w. H
    ————————————————) O; Q: \! ~/ E8 [
    版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。) l9 s5 R, k  B+ v% E
    原文链接:https://blog.csdn.net/huangxinchen520/article/details/1064874154 i$ t: y: x* r2 a$ _
    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:50 , Processed in 0.217827 second(s), 50 queries .

    回顶部