QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3554|回复: 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
    一:背景. r) ~2 O6 }" e% M$ c
    1. 讲故事
    # H4 e$ c' P9 X1 Q3 ?3 q  t- B2 L. ~最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。1 n1 y4 ]4 }0 E) J: c# I4 r( ]6 h
    , y& g, l  _6 N% l
    二:了解架构图# b8 Z$ }2 r" V. T5 z
    mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。7 o2 ?+ X& S9 t7 c6 o# O
    ' m+ V( R0 U( g7 l
    1. 从架构图入手
    & N0 q6 Y( ?9 V" f大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。# w4 a3 E# @" d

    3 [' w7 W% d+ N6 V# W$ O' H8 F& n/ t5 N/ d2 O; \0 G

    ! J" C1 F, Y6 h% f/ b6 k* ^其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~4 W! k" j; y+ {' V- J+ B

    # I% G5 @/ }& X9 u! `2. 功能点介绍3 A. G  j# f) w% X# l
    MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
    5 t+ I; m4 M9 v7 R* P  R# B% W* T" t4 P2 U% w9 h4 |0 V0 W
    <1> Client
    % D* B3 I* U0 i% H$ k. p- s3 s不同语言的sdk遵守mysql协议就可以与mysqld进行互通。
    . y+ A! _  E5 z8 M( b9 S8 Q  e2 U0 a2 C1 N, L1 r4 t3 x5 H
    <2> Connection/Thread Pool
    + |, x- {! ?: w5 `MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
    . l: g2 B+ f; y: y  Q% |: a1 y  @  r5 g8 T
    <3> SqlInterface,Parse,Optimizer,Cache3 U( G- |6 A5 x& ^; F5 m
    对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
    " F% e8 F% ^9 m
    # z2 g6 C4 y( k  _. I: ?8 F, a/ v<4> Storage Engines8 M/ t4 N! E+ F  P2 J2 i) m
    负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。/ t) p  o8 X- I7 p: G; X2 Z; R
    3 e: U% |  @8 I- |
    三: 源码分析5 q5 N  N! y; U  R' y: I. ~, ~# q
    关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。9 M) G- I! Z' T( ?; V6 J
    + _8 ?& Z1 Q1 J. V: o9 P" C4 N- j
    1. 了解mysql是如何启动监听的% ^. F( L2 ~* b" L# d1 K
    手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。7 \% X8 \, ]: g' `; J

    6 b9 z: Z# m. `7 \4 |& C8 ?: w1 R3 ]; P3 X5 ^

    3 _! C, e* c+ C+ s& T9 q从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
    & S3 b% q2 R! ^/ ]6 l* `2 n- D% q% i1 _- b/ q. U9 U: a+ t+ U2 c0 S
    <1> mysqld_main 入口函数 => sql/main.cc
    8 z/ R% b5 d* \3 p( p
    0 t0 t( F# t4 ]" _5 ^9 o
    $ J" x. D. E- h/ o' K$ Pextern int mysqld_main(int argc, char **argv);
      @  [) n0 h; @0 C: Q
    + s: _- U9 u, l# \% i  g/ E! y6 {int main(int argc, char **argv)" J" a1 x7 h6 @. o$ ?8 L; U: h
    {
    / h& h& H1 o& v6 a  return mysqld_main(argc, argv);7 r. B# P, a% J* F6 P8 [* h# @5 q4 _" b
    }
    ! }7 o0 y+ S7 I8 f0 L  D. \: X$ K) I2 p/ ^3 T& L

    ! H6 q; W! b1 W7 ^7 Q这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
    / G; I; O8 }  ?% {& I
    6 [# j. T. X& L1 [<2> 创建监听
    7 \6 c# J$ N% \+ B+ Z1 h" p7 ?4 b6 L* P2 ~8 O  \2 k6 w

    # l) c" _" e' T2 Zint mysqld_main(int argc, char **argv)
      w9 P! d+ h9 u, [{/ b/ J/ J( e, E
        //创建服务监听线程  Z0 y7 s0 V! A
        handle_connections_sockets();
    3 Z" }! \* z) D# @1 O}
    0 H  W* B6 F. u& y5 F* s/ c. |" Z
    . X" U, f" C. N  f) ?: a7 C- Evoid handle_connections_sockets()
    + g% e' d, x( M$ j) H{0 Q2 \/ H2 o2 v  \6 t6 R
         //监听连接
    & v' t+ U( y4 R     new_sock= mysql_socket_accept(key_socket_client_connection, sock,
    5 O+ n  J# O3 e8 J  ~1 C& W+ z                                    (struct sockaddr *)(&cAddr), &length);
    1 O' p- S5 Z. U4 v# j" y4 \5 M5 M+ t+ _1 ?
        if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
    4 T& B9 n# |  e) p5 w      thd->security_ctx->set_host((char*) my_localhost);
    , q' v! M7 k" g+ I6 F) w, j# y8 H  U& E" V$ c* Z# g0 |
        //创建连接
    + n1 f! W& ~5 q' b6 X" J4 j2 E- G5 ?    create_new_thread(thd);8 K% [2 R$ q" H, m5 [1 Y6 `
    }
    % @* y; @: ?# W1 Z$ @2 {7 V
    & w0 h! T4 I* n( Q* Z6 O//创建新线程处理处理用户连接$ i9 t! }6 ^( Z* N
    static void create_new_thread(THD *thd){
    4 }2 w4 K3 a6 y4 O  X1 W/ |& h2 g- p, h5 O6 ]6 M9 x3 i0 K
       thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;( w2 l7 o  R! c' z$ d( @0 m- L

    . ^8 Q4 P8 b- M$ k. l% X' T  J   //线程进了线程调度器
    5 d- f, u; |( I% W   MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   ( p. m- ?8 S4 j
    }
    ) u% J1 ], x5 Y6 W& |) [3 S
    7 S, ^. L2 V8 Q% K0 v3 M- ^# T# r$ B& w: q
    至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
    * @; F; _! Y- I/ _  O/ L0 B" r1 U& L# G' [4 ?+ [0 o8 Z9 a* g
    5 K6 Z0 S4 s5 N4 `6 C& s& ?
    2. 理解mysql是如何处理sql请求
    9 E& d  L$ j2 d& C0 L: }这里我以Insert操作为例稍微解剖下处理流程:- t! B; a% g" v; q' b: q$ @5 A5 {
    * _8 |+ Y0 a: S
    当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
    ; v' g. I/ w  @) C  d  ~$ C
    3 P- @8 t4 u& X0 N
    4 i! k& N" x! T' ^) Qstatic scheduler_functions one_thread_per_connection_scheduler_functions=
    ) b* I" H3 {. W) j1 q" p{% i3 b4 i1 ]. K2 A  l8 k# e
      0,                                     // max_threads6 c3 ?: q& x( P( Q0 j% \
      NULL,                                  // init
    * `' E  ^# ]% `/ |. S8 W/ q" L6 E  init_new_connection_handler_thread,    // init_new_connection_thread
    * @+ _+ L1 F% Y# r3 l" M. Q+ O  create_thread_to_handle_connection,    // add_connection; a% h3 C$ A4 U" K, D" ~4 q1 O5 h3 d
      NULL,                                  // thd_wait_begin
    / R! B- f& W$ |3 C3 i  NULL,                                  // thd_wait_end9 z# m3 H7 P) n
      NULL,                                  // post_kill_notification2 A7 X/ {4 B$ m: C5 {
      one_thread_per_connection_end,         // end_thread
    ' t$ Y. o. j8 B  NULL,                                  // end
    9 i- j, o% z! k. ^' @2 y5 ?- C& S: n};
    0 n/ J/ N. c2 J: C! @' l4 K" H% m0 n- u$ j4 x) r+ {% u

    - h7 r- J& @: E0 D' _从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。) W2 u( o# G- `
    ) H/ W" C: U/ `9 [% v
    <1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪$ w; X1 b6 r3 J' G4 l

    - J6 e+ [, R' s) G  _5 @void create_thread_to_handle_connection(THD *thd)( {9 o5 _- x9 I/ Z- [
    {
    - g6 S5 L  z; a$ _, z% g     if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
    + |' {/ z& {3 F6 i- T                                     handle_one_connection,(void*) thd))){}
    : K5 M5 P5 p' N8 c% \}; G3 q/ V  P+ O( b7 P$ V/ z
    //触发回调函数  handle_one_connection' w5 g- w% g$ j7 j4 a
    pthread_handler_t handle_one_connection(void *arg)2 }) r* W, T; g
    {
    ; W8 h4 s9 e6 I/ x' Y* _     do_handle_one_connection(thd);& U/ m$ P* x* I( C* n0 L1 v8 g2 g
    }
    5 A8 E6 R2 w3 a1 r: x6 G//继续处理
    0 Z& P% g$ f2 v3 D5 ]8 f. a) D. y4 evoid do_handle_one_connection(THD *thd_arg){  u) j% C- j( e
        while (thd_is_connection_alive(thd))
    : K$ q6 [4 ?9 ]1 C% |    {/ K) W4 ]. G! P- x
          mysql_audit_release(thd);
    # a. d1 e" y3 \& h      if (do_command(thd))  break;  //这里的 do_command 继续处理- U9 ^& D' b+ i+ d0 ]
        }) S% z1 C' t, [5 e$ Y# R
    }7 _; n5 S& J* e0 q4 a% ?
    //继续分发' L6 m& D2 B/ U. b3 d
    bool do_command(THD *thd)! l1 @0 z( R4 m; h$ c+ d
    {
    5 X/ D1 Y: I: |% y: l4 p' v* |, E; `    return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
    ) b2 V9 z3 ?& z: `& B}
    & V% u2 K9 ]% l0 ?; A0 ?, abool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)' U7 J# Q+ [# K. I3 k
    {
    & J0 D: ^  E, N# f$ s# _. Y3 p      switch (command) {
    $ U2 |3 r# g9 R" q         case COM_INIT_DB: ....  break;, z8 d# E1 A, f& ~
             ...0 n" O9 l: ?4 t3 a$ V# m
             case COM_QUERY:   //查询语句:  insert xxxx
    . x: p0 S9 m/ ~9 S6 N2 M             mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析
    8 e. `) ^/ F' M( e8 u! m9 T           break;
    4 M8 g  ^5 X" s# c! [& y      }
    ! O1 R* h7 r% p& ]% ]}
    5 P- q( |1 _0 `# x4 }( J- Y1 p//sql解析模块
    $ X1 d5 ^% l4 U+ c) o( gvoid mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)2 Z) f& c# i7 N( F9 W8 h+ s
    {
    * u; R& ?+ [, q      error= mysql_execute_command(thd);0 v. Z5 Q! _6 ~' e, C, W7 ~
    }
    4 I) p7 O3 ?2 }' G
    ; k- M; `. `& t% B$ K9 c) ~8 ]: Q' H
    <2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
    ' |- d! V/ {: B' F7 R  |$ o1 V
    # R, {7 i5 t6 u* t8 r//继续执行/ h2 s4 l. c; t0 u7 y
    int mysql_execute_command(THD *thd)9 u* z  x6 X! i2 N; a  E
    {1 J( {7 y3 L, B" F( j  R6 {
      switch (lex->sql_command) / C1 z, f) J# f7 @
      {
    # @5 J: D& r+ V9 J) S      case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;
    ; |+ Z. d3 k! e: x' [2 i! B  C' r' c, h0 i) n1 f
          //这个 insert 就是我要追的
    ) F( |" f5 g* w! U8 A      case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
    " y" q% ]3 S* {* H7 ]# @                                              lex->update_list, lex->value_list,
    9 i0 V  E. l" x1 I                                              lex->duplicates, lex->ignore);0 e' ?0 s( \# X; \9 D: `! s( ~* E
      }) U$ P7 D7 h! y+ V$ K3 ?5 l# W
    }
    # |& \% V; v. q3 `//insert插入操作处理
    8 R! k- d4 w4 f) p% k% K  a8 abool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
    3 k8 T# C1 U6 {% a% |                  List<Item> &update_fields, List<Item> &update_values, & T$ f& r+ X6 k3 I
                      enum_duplicates duplic, bool ignore)
    / }5 d" M1 o" N9 G! @7 |{2 n4 @( s. H3 k# H# t  {$ @/ j
          while ((values= its++))- f! {5 r. w2 d) q' |' L/ {
          {0 X4 l% V7 E' Y1 z/ |
               error= write_record(thd, table, &info, &update);
    ! {$ t( T# x+ y      }
    $ d' x$ y, c8 n}1 s$ V3 L2 K; p1 Q
    //写入记录
    4 {, Y8 g% l9 S1 X$ G& e$ Y' oint write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)7 q2 j# Y, U" s, ~! @9 I
    {
    " D# F6 z  P1 u/ L$ I# i" [6 s    if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
    0 n* U5 l" l' `( t" Y6 b    {) w- f- h/ ]9 S
             // ha_write_row  重点是这个函数
    ( s( h5 |$ ?$ V* a* `% A, n5 L; |* |         while ((error=table->file->ha_write_row(table->record[0])))
    : Q  q1 o& K2 E  F& l  l         {
    # \4 `) C  [- f8 g- r- n             ....0 @1 b; Z# a6 N/ k' H  m
             }
    ( v: x$ i5 I$ [; u4 g    }
    + @* T: y( v( y# X! I% z  A}
    3 U# @  K- E0 p- _) t/ H: S4 z; u% ?, T: E) R7 N

    ! B- M% S2 x6 I# T  Q& l' i& |  o( q& u, W3 f, F6 p
    可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。" |& X% B+ ]  p6 p/ `; u
    ( `2 u  g  X6 H) R
    <3> 继续挖 ha_write_row5 P5 t& t' W, k2 a1 y/ O
      H  d  ?9 w8 Q7 a
    int handler::ha_write_row(uchar *buf)
    ) X* o, z% m; B{$ x8 A2 ^5 _0 F1 c0 j0 I$ Y8 c
        MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); }). i& A9 H6 _. g; \6 w
    }
    ' M5 v: Y3 b% t7 ]: Z# P0 q. U* [; S  K" I% ^# o# T
    //这是一个虚方法
    , `8 F( q" G6 D$ m" a* ivirtual int write_row(uchar *buf __attribute__((unused)))$ h6 z: E$ _1 l& _3 O# t3 B3 s
    {; E1 U& t& V; A, ^& C9 Y" a" s
        return HA_ERR_WRONG_COMMAND;  C6 j& z4 A* a$ S. e6 }" S# I
    }, L3 H7 y, I& H! U' {4 F
    2 |/ L1 F$ Q6 E1 `+ j0 N# i- i
    9 F- H! G1 `) x; }2 t  p5 M
    看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;' r2 v$ y, o* Q. l
    ) g" y' ?1 d+ t  x0 E
    3. 调用链图
    4 d9 W3 Q( {/ i7 n这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。3 S; x4 P8 S- i2 Y1 v6 t# `! }8 B

    * z4 P# c, n0 w' k0 v& B+ C/ ~" G1 T8 Z4 H9 T& E4 E) a3 g

    + s% y; m3 z5 ?5 j& p) N9 X三:总结
    $ m; N0 Z& \1 N5 Q) s0 Z& N% v& a大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。
    1 B: m5 Y& `; K% Z. P( P) i/ H————————————————
    ; I9 A+ S3 G6 {, r+ H版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    1 Z  K9 M& a4 c* i  n, n原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415! x/ [$ [9 O4 X
    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-28 06:15 , Processed in 0.585687 second(s), 50 queries .

    回顶部