QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3027|回复: 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
    一:背景. }( \' v9 T8 q+ I
    1. 讲故事5 ^! s! D% `5 y( \! h0 A+ c
    最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。, E2 X- [& k) i8 |3 @' o# F

    ( h, J( [( D1 L* f& q) b+ N& O二:了解架构图
    9 v0 J* w- ?& O" D. Q: t' ?mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
    , g+ r6 D/ T9 d) ^+ d9 T# p. k, W/ z+ p
    1. 从架构图入手. |- G# A" }4 T* k  P9 j4 w
    大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。3 X0 `+ e- v% g* ^+ @. K
    4 m0 s, n. G1 T+ f; a, s; b

    8 ?- F% [: Y' I4 X9 w9 ]; V) g7 X3 j$ ]; r& r9 B  k
    其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
    : `$ c' G) x" |- @) U, {) e1 r% R2 \, q! |6 {7 P* m8 m
    2. 功能点介绍4 z- @  D8 D5 ~4 Q
    MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
    7 o; `* J5 N6 B7 S1 e9 n0 Q+ Q( N+ F' X9 _
    <1> Client
    9 S/ `& I" l0 `不同语言的sdk遵守mysql协议就可以与mysqld进行互通。) c9 G+ o# Z" j9 f

    5 V7 y. t1 H0 Q; l, F9 L<2> Connection/Thread Pool2 x; G4 k  l% ~4 {- G4 I( v/ G$ Z% `' y
    MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。& r! D0 e, D7 A8 K0 D. p

    ; l% r9 D0 N8 i5 r9 G<3> SqlInterface,Parse,Optimizer,Cache$ E3 D) v1 O# v& e9 [
    对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
    ; U1 N/ V/ }# u! r7 `4 B3 R! f7 o) |2 B; C
    6 `* [2 R. c) T( ]- X<4> Storage Engines
    ' J+ |  V# ]. v  Z' c5 O负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。0 V1 M' O7 ^' m' z2 s+ ^" l- B

    / z( |: u5 K( M! O1 k7 E三: 源码分析. M+ U+ ^; s- a' G6 v1 J; l+ @
    关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。& @8 p, ]; [6 [/ D
    ; |- N/ H. J3 Y  N. u7 o
    1. 了解mysql是如何启动监听的7 ]4 A% q+ Q: z% m
    手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
    0 ]2 v5 r& [6 T+ q0 h4 u6 q
    , i5 Z% m5 B& ]2 L9 N$ A+ n' y0 V8 d1 Q; Q8 h0 i7 P
    , S- {% Q- @. `; @
    从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
    0 @. `) o+ D1 K/ n2 p. m
    & M' a9 R4 w4 |; K5 Q<1> mysqld_main 入口函数 => sql/main.cc3 g* T: Y' N9 a# ^7 s% `+ L
    ' R/ g+ l# U- ?6 e3 {

    3 {" A' k' }$ Y# q- I# w7 P' i7 Kextern int mysqld_main(int argc, char **argv);
    # X$ P: P! W; u
    3 C0 B" i# O: T4 q0 zint main(int argc, char **argv): b9 [0 x4 z/ [0 l2 u
    {. d1 S( f: D; `
      return mysqld_main(argc, argv);
    8 v" _; d7 W3 h8 L0 k  r! d9 B9 b9 s}
    ; ], W, r  J, j0 @- J; Q  E& |' V8 u

    1 ^# _/ i* t1 e3 y1 P1 q这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。( G9 v- _$ w) z. K0 r) D2 T
    ! G9 j- D( W1 |$ h9 v# _
    <2> 创建监听  I3 O* y$ U4 {

    9 y  ?, W6 B, O! u5 [! ~7 T# p: G9 ^9 `) O2 e2 C& @
    int mysqld_main(int argc, char **argv)
    " o! C6 U$ l% o) P: i) L{9 e# y7 ^' T4 b+ z
        //创建服务监听线程
    3 P6 ^2 A& s9 ~6 w' E4 |    handle_connections_sockets();
    & w7 V5 C1 Z! M/ f" K}
    / [  ]6 C& U9 r. Z' @6 q4 r) ?3 T$ i. s' O7 f
    void handle_connections_sockets()
    ; l0 i$ a; a! D+ h3 m{
    5 u8 x9 P; @1 O# j. i     //监听连接
    * g& O4 Y! J6 x" ?     new_sock= mysql_socket_accept(key_socket_client_connection, sock,
    5 N6 F9 i0 a5 z3 l                                    (struct sockaddr *)(&cAddr), &length);2 y. T% N$ ]6 C7 Q, u4 n  z! y

    1 W& v, S: ?+ U0 m/ k6 c8 c    if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))6 V. ~0 l/ p3 E- i' ?6 C2 u, c$ J
          thd->security_ctx->set_host((char*) my_localhost);/ q1 J( Z5 q5 f, z# v9 k. d. x
    - g+ `" z/ t: ]
        //创建连接
    . t& t( `, s& ^$ O& o. ^! ^    create_new_thread(thd);
    & v1 N" B. C2 B0 n( E5 C}' X% _( q3 Z' }" u! E
    2 E7 C: n+ t& a8 J+ {/ N
    //创建新线程处理处理用户连接2 G$ F* b" E: U2 `+ ~! Z
    static void create_new_thread(THD *thd){
    : A5 e! y9 m4 A- m0 n
    # l9 s8 J2 v( a) {# M# }   thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;$ W. j7 g9 B) \  z9 C. N& d
    ) v0 C8 t8 B. D7 m( t7 w
       //线程进了线程调度器$ M; y0 A- v! S+ }( e4 {% Q) j# S
       MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   
    ( e) W0 X  f  ~$ e}$ d# Z) B( P% Q4 H

    5 v2 ~4 F4 `7 d+ e" T7 b  G7 Z6 `4 W# Q" Y* V/ q
    至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。9 S$ _% h3 J- K& q
    " H4 C7 N6 ?0 p
    % C' A; @1 I5 p7 U: D! D
    2. 理解mysql是如何处理sql请求4 y3 W2 z. w9 c  p: }
    这里我以Insert操作为例稍微解剖下处理流程:) K) {# [7 @' J1 R4 q" m1 m- a4 b

    3 l0 W9 g$ I- q3 |$ V2 E当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
      a* j6 T9 E; P2 z4 t: U7 @# D. L7 k: h, Y! S

    $ Z8 K5 |- B8 f1 y8 \3 Tstatic scheduler_functions one_thread_per_connection_scheduler_functions=
    8 x' Q7 `4 N1 S$ S- p{/ s6 k" A' `/ _0 `4 s4 ]# [) u% Q6 X
      0,                                     // max_threads' s, R, E1 g9 ~; y6 j+ ~: x' u- W7 H
      NULL,                                  // init
    # {# w/ {6 R+ {. o% E7 b3 G  init_new_connection_handler_thread,    // init_new_connection_thread
    : W( _) Q) P# V* p  create_thread_to_handle_connection,    // add_connection
    ; C, v5 Y- q) q1 ]* C( K  E. T  NULL,                                  // thd_wait_begin
    ' x7 D" Z, [# m  NULL,                                  // thd_wait_end$ k) u5 s1 J$ X% b  [
      NULL,                                  // post_kill_notification. A  k2 c5 K# h
      one_thread_per_connection_end,         // end_thread
    ( x, [3 b0 E2 `9 x9 p  J  NULL,                                  // end
    ! R4 F' S. u$ p: w};
    # K' V7 v" ?% U% y  j# E$ r  B( L, @2 K/ g! W; _2 d2 G7 ~4 L

    + l, T! M+ A* E8 [4 \从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。9 ^# H! _4 q7 h" w! T
    0 f* Y( }% v* ]
    <1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
    & n0 t" R5 s  h4 \- \3 s: _4 H0 [# e, g( y2 M" k" J
    void create_thread_to_handle_connection(THD *thd)1 _! C- ~3 ]) G- Q& R  O, h
    {3 a( t7 t1 v$ u/ e. B4 C* p. i
         if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
    + }: n, [. [1 c                                     handle_one_connection,(void*) thd))){}
    / \2 U  b$ K# w8 m" P1 D}
    ( V  @$ y1 @4 Q& k# O) X5 L//触发回调函数  handle_one_connection
      O  {: l; W4 l) l" Q% |* G0 Kpthread_handler_t handle_one_connection(void *arg)) r& B; w- X5 o/ g5 s, w% h5 f
    {
    + c  q9 X' a( |" D+ @     do_handle_one_connection(thd);
    9 W/ v- _% q2 C( v; k8 W1 N2 Y}) ^5 k4 R$ h) T& q& M( ~0 ?
    //继续处理
    * y& Q6 ^3 q# T/ s7 q0 x3 |! Uvoid do_handle_one_connection(THD *thd_arg){
    1 |0 H3 H* u- ?6 t    while (thd_is_connection_alive(thd))
    2 @' j9 a8 n6 T" q    {
    5 }+ x4 Q: V+ r/ k      mysql_audit_release(thd);! `& w! K" @+ @7 l$ S( i0 G' d
          if (do_command(thd))  break;  //这里的 do_command 继续处理
    ) T3 k9 D8 c( L    }
    * g* t- u1 @, I. w' o% @: P}
    5 O  g' O! O& n9 W//继续分发3 Q0 o& |: k: ]" ]3 {; ~0 d* W
    bool do_command(THD *thd)
    , d, \; y7 g  B0 e* t{
    : R/ Z) P* X9 ?7 D( V. n4 S    return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
    5 C& b" e4 ~8 \# G/ z  |}
    0 j* F  ^  ]- R5 H' Y6 E, `bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
    " C# q- ]- I3 G* A6 m( g$ s{7 A! C/ F  T) ]6 ^1 p+ V& ~' y
          switch (command) {
    ' {$ `8 C$ L1 a# x         case COM_INIT_DB: ....  break;
    0 ?. K; N; `+ Q6 o% a- L8 J         ...
    - b6 X: m( E8 N, b% D7 ^         case COM_QUERY:   //查询语句:  insert xxxx
    + A4 \/ N$ b0 J4 P! N, Z! Z, ~* S( C  \             mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析- z4 e8 z  g# |" G# h6 ^
               break;
    9 d4 b4 v2 ?$ Z0 R6 a" k8 w3 B      }
    4 s0 U' H& a( e$ [5 o! S+ I}7 V8 L* R4 j4 y, R) W+ K1 j' @
    //sql解析模块
    2 N( ^7 w! I& G, ~) @void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)$ N4 V4 y- k* z1 }9 r8 w
    {2 X/ d0 u' a8 B$ }3 O& v3 o
          error= mysql_execute_command(thd);* u" S! f6 M; W% g" I' m% E
    }' Y" ]9 T& \( ?# ^. S1 `0 c7 w
    * f  f$ \% N' _  B/ Q
    * S( F! r8 P6 K1 \) b" _( Z
    <2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
    & X4 W3 E! q- b4 O$ N
    ; J. y& G' P& b" ^//继续执行
    : h2 ]  J& Y' C3 R% t: Tint mysql_execute_command(THD *thd)' E/ F  Z- w# m& K' ^' i* a
    {
    " y' c' m$ T- b: e4 E0 l3 `8 F$ a  switch (lex->sql_command)
    2 B0 m0 I, y+ n9 l$ {6 ^1 `3 l* x8 J  {
    # J! }5 v4 s  F: F. g* K* ]& n/ C      case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;6 A8 I" |$ u& ]- ]! Y4 l2 n

    . f1 h, q. K5 n  l) L! b/ \      //这个 insert 就是我要追的9 Y& L" i2 B/ z$ M% ]  W7 x
          case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
    1 \1 W. w7 L# K3 X  n+ s7 ~9 C2 o                                              lex->update_list, lex->value_list,
    0 H3 h8 p- b) b( c9 F4 m                                              lex->duplicates, lex->ignore);
    ! Y. L& ~! j  s' }/ f4 q& _  }5 B- R- t1 ]5 T! q
    }
    $ p0 Q* c8 ?9 O2 B! l1 y* ^//insert插入操作处理
    * O- i" @! o/ B! e& e. U3 l& h4 Tbool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
      ]# f) i& F7 b8 o  @                  List<Item> &update_fields, List<Item> &update_values,
    ( ?, L- B( k% `' z) K7 F+ S; k                  enum_duplicates duplic, bool ignore)
    / X! j  F4 M- M( u. q" y. M( V% g3 X{7 g  G& v! P2 a
          while ((values= its++))+ K- s' p. n, L+ h3 B; |' S
          {
      V: z6 j* U' h6 s9 N           error= write_record(thd, table, &info, &update);
    ; z1 c  H( {- c7 K4 u      }& _% y3 ]3 g+ J, d: N9 R8 C( z( m( s5 d
    }
    - k0 j4 J2 R6 Z2 j/ v* B7 Q1 g//写入记录
    * t2 d# h3 S9 u3 mint write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
    7 z/ W. R; n; e$ H# [  s; y& N2 t. e{
    3 p% D# K+ J1 M  `5 V    if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE): ?5 ~* E4 J& U' s' `
        {1 h& f6 G$ [2 S/ {, y
             // ha_write_row  重点是这个函数/ c2 _0 L8 z4 S' l
             while ((error=table->file->ha_write_row(table->record[0])))
    8 _9 z4 T. J4 E( |% ]         {
    . K& Z- v3 Y1 T             ....
    ; K+ E5 o5 f5 G         }. Y3 L+ |1 `- @( E% B
        }2 U! \, G  [% n$ d
    }+ Y6 @4 |' k7 ~! L! K$ u" n; ^

    + {2 s5 ]  l0 ^  R, p- |
    8 P+ W! O7 z+ @3 J, L; J6 Y3 }
    2 J; E4 \% W4 [) l. r! e0 v( O3 j可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
    6 s7 G, X6 v8 u. ^) b& D, n% |. G+ B/ \3 a7 R& K3 C/ ]% t
    <3> 继续挖 ha_write_row2 K* n* W+ A4 V/ u
    ( Z" D6 {( [) m
    int handler::ha_write_row(uchar *buf)
    9 z* ^3 o1 k8 v2 u4 ?{
    ) a: m7 a; g$ b) e2 {& m9 n4 ?    MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })9 A7 m: G1 t. ]" a# N' @& p0 `
    }% t" q9 X% b  ?! A
    ' r7 _0 h5 V6 n, s
    //这是一个虚方法
    $ ]1 r9 b" r: Y' X  Evirtual int write_row(uchar *buf __attribute__((unused)))& m3 G( y7 d% N( a6 H, n1 A  g
    {7 R1 ~- {: G0 {5 ^: H9 U" T
        return HA_ERR_WRONG_COMMAND;  p1 h3 a8 e$ w+ u2 P7 h% |5 ^0 u
    }
    : Q: h( O" j+ P- Y* |
    9 @/ {$ Q) [# y: V; I: T  e8 L1 [2 x7 ]
    看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;
    3 h  N/ n1 Q4 g1 [3 W# C) W3 r# H4 u. P) X; V! H) M# v& H
    3. 调用链图
    6 ]4 _4 A7 h: x, E+ d+ T# k9 a: A这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。8 @% ~& A5 i3 |3 o# ^2 K
    1 K& `# R. i6 S  L" j

    / P* W, l" `6 i1 ]
    4 B. T( f0 r" D) X' d& l三:总结& I- n" I# d8 {
    大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。& F: O: J+ Y* ]* \, H
    ————————————————: A. W$ I6 c8 f! H; o/ E/ M
    版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    % {; b2 o( a4 S$ J7 v原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
    , o& c8 T7 s& V% C
    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-30 20:25 , Processed in 0.811103 second(s), 52 queries .

    回顶部