QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3560|回复: 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 s+ q4 E* m3 V' p1. 讲故事
    . y9 [7 p' E# j8 N" u7 F最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。2 w  Z7 f# D# ]* X9 n% i% N5 d
    : m/ d! A: p# L
    二:了解架构图$ Z8 T1 j( r4 F, ]
    mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
    # ]: I  {# L6 ^5 I
    2 s9 n9 u0 \* X! v' p% P1 @1. 从架构图入手( R( D- O3 d# Y' o4 C4 e3 `
    大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。2 D( _6 |6 K* @" d

    % D# N. \+ T+ j# l5 _  C* [, B$ I7 ~& R  W; W+ u* w
    1 A( h3 y- R; y: y/ k, H
    其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
    9 r" }" T! C$ A' l- J# I! ^& U0 j# I, Z. L
    2. 功能点介绍
    5 w3 I- ~& c$ G' l. i1 t8 t# RMySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
    : m& O' W4 G4 I0 C8 @1 e) s) B0 j, n& F4 ^
    <1> Client7 X6 ^' c5 r; r
    不同语言的sdk遵守mysql协议就可以与mysqld进行互通。7 G. O  o; a! F, ]
    # Q+ r1 N+ ?8 s2 S
    <2> Connection/Thread Pool6 ?/ Z; b3 \% l  Q6 \7 {# ~$ g
    MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
    0 j2 ^. b, ]" J  a, M; l( X1 \
    * W- y/ @/ ?4 m) B' M* |4 w<3> SqlInterface,Parse,Optimizer,Cache- {1 F7 e6 r# p4 v. h& c- l
    对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
    3 [9 a( I9 u& x- o& F: c% L& e# m, j6 d0 [( p( q
    <4> Storage Engines  ^$ C$ w$ m8 o4 @8 v! g& W
    负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。
    ) E& X2 [: e3 R3 ?: j
    ' w  C# }7 L' V3 @: s6 y5 \- i% N三: 源码分析- {1 W" S" X% X* c5 ^; \
    关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。! a1 w, y' E( w0 \0 K- Q0 V( w" h

    ; k# C: [: X3 u3 i. e, p/ _& i1. 了解mysql是如何启动监听的/ V5 i4 K/ M* Q4 u) c, v5 X% C
    手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
    ! ?( J# ~) h7 K5 y: p5 ]* C2 @) d0 R! _# ~# v1 D  T" I
    ( k# p/ k1 N4 |) n- q6 B2 w: c, G  `

    - K) F9 @1 ~# z2 c从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
    : k7 O# `! ?2 \
    + I* S5 T7 x8 [<1> mysqld_main 入口函数 => sql/main.cc
    . s' W7 c( K% q
    7 ]  m5 l+ n" L- }  K% {& i; B& Q1 C$ Y
    extern int mysqld_main(int argc, char **argv);
    # s8 E, G$ ?" u3 |/ ~2 G0 f& o  u
    - O4 v! i$ N* w5 Q$ w' d; sint main(int argc, char **argv)
    " |# l6 f6 @9 e9 K+ b' |{
    , L# g5 z; z7 V4 P+ m/ K9 C# E  return mysqld_main(argc, argv);" L/ D; O# C, U; M4 e9 s; O
    }
      O# D$ Q! I* v0 `2 E
    ! O( x" f3 l$ v8 A% ?# H- j  {' N3 h: U6 p8 Y6 X' d9 n
    这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。) l/ y/ ?+ G8 l4 e8 O2 x

    % V6 p/ Q9 N/ U3 V( v7 j$ H# e; z<2> 创建监听1 E) D/ `! D- \1 L
    - Q+ s1 x) I" D# N' |0 T. m( c
    4 B7 s; M0 O) k2 o/ V# W# k
    int mysqld_main(int argc, char **argv)2 o. F3 x  k6 @* h/ x- T; {
    {
    ; z& ~0 Y3 U* ~$ A! U0 A    //创建服务监听线程; N: d# F3 f2 ~9 T0 I3 {0 T5 M5 p& r
        handle_connections_sockets();
    4 a  }: S8 K8 e6 i+ F: }}& i. k4 S. J, V2 ]

    ' r3 w. l' j! G$ _9 [4 |& j' Uvoid handle_connections_sockets()' g6 @% a8 Q- v7 ~. ~8 c8 L
    {) K2 f3 U1 n! u# X9 i+ S
         //监听连接" y& u. d4 B& n- \- k/ l2 _" M& T6 c
         new_sock= mysql_socket_accept(key_socket_client_connection, sock,
    * Q+ P9 j8 Y9 M1 F                                    (struct sockaddr *)(&cAddr), &length);
    / I& ^) u1 ^6 p% `# X4 B, ]6 l4 v
        if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock)): `7 C5 h: \4 Y7 R
          thd->security_ctx->set_host((char*) my_localhost);
    8 t! Q. |& F  ^, d% `" c$ i6 w7 S3 c4 x5 ]
        //创建连接; Z6 R. `' h* a1 Q
        create_new_thread(thd);
    ' W. L* c& T$ T6 d8 K}; Y. ~( A: l; ^
    5 A! p/ E3 g9 ]! Q5 y* R
    //创建新线程处理处理用户连接
    5 a- @0 S) ?: U3 d6 i, vstatic void create_new_thread(THD *thd){) F( Z) E& E$ N

    1 ~( N+ k& n6 K   thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
    5 ^1 d& x3 p% C. w
    ' `6 |$ F: D% |# i: i' k   //线程进了线程调度器) ^$ A2 q/ j  ^( h( C. t
       MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   
    ! S" W3 v7 E6 T6 N% H: M  S0 Y3 x}
    & x: v/ q3 n+ h. X3 v0 X5 s; d, ]6 M5 m1 p

    , ^& w; p3 e9 I8 K0 S6 Z5 P至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。6 ]4 G* @' N7 S
    " O$ r% S% M1 g3 j: Q
    ! s6 @5 h5 q+ h
    2. 理解mysql是如何处理sql请求
    3 J! Z! q- k- n; o+ X: \这里我以Insert操作为例稍微解剖下处理流程:! o" Q  \5 ~' ?; z' y% D

    . L' @+ x( p# o! R: K当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。5 h7 d! Q& G0 n: _
    4 j" d2 K7 }! b, S
    # Y8 P: U- R. o; J
    static scheduler_functions one_thread_per_connection_scheduler_functions=
    # e1 w( f+ P  p  f* J' Y{
    5 k, H! X3 O( u5 ^+ ?  W6 h8 g0 S  0,                                     // max_threads
    + z  V, U0 F' ^: T  NULL,                                  // init
    2 p) O  d$ p1 D6 I+ k0 i  init_new_connection_handler_thread,    // init_new_connection_thread
    0 [7 f6 I- \' H' u  create_thread_to_handle_connection,    // add_connection
    . _+ s8 s2 r* A* r2 a  NULL,                                  // thd_wait_begin% H1 ?3 e3 x) D$ z9 p
      NULL,                                  // thd_wait_end$ W" c! _7 d  |0 g, r  N  l* }  X
      NULL,                                  // post_kill_notification
    8 k$ g& K0 B" i; `  one_thread_per_connection_end,         // end_thread
    % s) ]- k6 ^' K1 w+ `# G  NULL,                                  // end
    ) w, x5 y$ z! c  ~};$ B/ m7 [7 @- H, E4 g& {$ K. _
    ' k9 x! h; G9 D3 _! d; J8 J

    1 p4 r& E5 H5 G& {: s从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
    % |0 t& `! r  {& u% Z& d* T# S0 k' L* B4 X
    <1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
    6 Y9 e# q9 p) W7 ?% g7 u6 R0 _) `( K% X+ h5 K
    void create_thread_to_handle_connection(THD *thd)3 q/ n6 G' Z0 X7 g) {
    {  `: [; P3 y4 A) d6 u! o; l
         if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,8 ]' Q0 |& `$ W9 s
                                         handle_one_connection,(void*) thd))){}2 B! @2 Q# r* _9 W+ C3 F/ s
    }
    " P/ l5 \" w, J. b* b//触发回调函数  handle_one_connection
      @9 K! w8 m$ gpthread_handler_t handle_one_connection(void *arg)0 q  T7 U8 x+ [4 \
    {0 L2 ^+ B2 y2 H5 h; s) n* l
         do_handle_one_connection(thd);3 T& x/ }+ w- C1 H: k2 i7 t
    }* ^- \# h9 B3 V2 w& I9 p
    //继续处理
    + X( f1 H* }. D* uvoid do_handle_one_connection(THD *thd_arg){
    , [0 O7 M9 T  U4 U* P; T    while (thd_is_connection_alive(thd))
    ! V5 U1 A8 g- W) A  J) ^% h    {' h. [1 ]+ Z$ V! ~; A
          mysql_audit_release(thd);% E2 Q$ p- ]; e1 O1 y$ p6 e
          if (do_command(thd))  break;  //这里的 do_command 继续处理
    ) c: t9 Y6 K3 D( ?9 l* ^$ }6 q    }
    - d4 \/ }2 o3 P3 J}2 [( {- Z1 r9 u: B; a9 U. o
    //继续分发$ n+ D  V) [: `0 Q
    bool do_command(THD *thd)
    $ f, |8 c- `' W; L4 \2 {7 U{) t0 ?, |# r  z6 A
        return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));" ]4 H, C: R; O  V& S( W
    }" C8 U5 `2 [2 G
    bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
    ! D( _6 F& i7 W. M{- l6 g1 ~7 z4 _0 K5 ^3 [
          switch (command) {
    ; i, Y9 `% a, c6 T7 U. R  w, p& `0 ?         case COM_INIT_DB: ....  break;
    9 D$ a& x; O* h) w& T/ Y  P+ I         ...
    + t. O& D% L" i) ^% p# ?. s6 I& d         case COM_QUERY:   //查询语句:  insert xxxx
      x* B+ g! f& T' n             mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析
    5 U( h" c; h) r: q5 F           break;
    2 F* s  ?$ ^# K! H      }: r3 Y2 @* }* Y2 _+ o( J
    }  Z2 J- o7 Z! h% C+ l' J
    //sql解析模块
    4 F6 {, U" h+ R# n/ k2 @void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)4 T, K+ J5 ]6 x: @! I* x
    {- g9 ^! v* [0 J5 a' `
          error= mysql_execute_command(thd);
    % X% R  ~& g- u9 ]2 G7 w* P}
    / g9 a6 e0 w* ]9 i9 Z
    9 y6 J5 s' G' u: U' U& r4 w; V( K1 J5 I" f0 _4 [
    <2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。5 f+ P0 U, c  V( H# t
    3 L  A! T5 B1 U1 ]3 g8 W% [
    //继续执行
    : J. D) S: I% X# Gint mysql_execute_command(THD *thd)
    3 ^' t  i& E. @* ~7 L( e& V3 H* y{' p! N. [7 n1 ?  p' n+ q
      switch (lex->sql_command) 8 g4 F% L) H; K1 N
      {6 U& [/ b% I9 {
          case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;: b# N/ N  N1 m7 N( Q1 \% V
    1 i7 l& U) K4 n5 l' ?0 l% H- u
          //这个 insert 就是我要追的) b# M! h7 d- q
          case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
    # M9 s5 ]! N+ S0 l5 Q% X                                              lex->update_list, lex->value_list,5 z5 ^; ~4 ?( G% N: P  k6 B3 ~
                                                  lex->duplicates, lex->ignore);
    ' V& D# _: m' [; a+ e$ P  }
    : F2 I) e9 H9 j2 ~" y}5 G: Y3 o/ W( I! l
    //insert插入操作处理
    4 d, S, G5 U8 n9 M5 abool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,' P7 G, s1 C9 T  v  {9 b) U
                      List<Item> &update_fields, List<Item> &update_values,
    ) ]# g; Z, d0 b( p) {& Z3 ?                  enum_duplicates duplic, bool ignore)
    ( J( t7 E3 @# {4 N: N{2 p# P, F% F0 g) J0 z
          while ((values= its++))
    4 |1 r2 b  W8 q- P9 u      {7 Y. S- }* ^4 k  R" k
               error= write_record(thd, table, &info, &update);3 a" j" @4 @2 `
          }
    5 \) e7 I9 ~" x2 ?9 k  l) _  |}, o; E/ u3 [6 i# U$ B: k+ V
    //写入记录' |! @. i2 @! f4 _. }
    int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)3 ^4 H1 F" s2 m, v8 {
    {; k. x- y+ o$ u* O: p; b8 s! n
        if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)8 K2 ^7 B7 G  `
        {
    ( B1 u0 o. M, k. u/ i" c         // ha_write_row  重点是这个函数
    % F# g) J& x: B4 S0 s8 X4 f- o         while ((error=table->file->ha_write_row(table->record[0])))
    9 P  M  {9 p, J% |  w, {3 Z         {4 L( N' Q. _7 x6 H4 t
                 ....
    * w# n9 V8 n! U# g' [' x         }4 a/ {: E9 J  y% {, L
        }
    $ T9 w( k( Z8 E}
    % j$ p5 k  e) _& z  c
    & n1 z6 r& j1 J' t
    - }; x/ d' R) W; L' E! v
    & ]; D) J$ g7 f; `  [2 B5 f7 ^可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
    9 v6 E, o: {9 m/ T9 N& \; Q, W4 Z' K9 a
    <3> 继续挖 ha_write_row
    8 R9 Y5 X7 K; a1 ?) E* x8 R- |9 Z6 J' X  n
    int handler::ha_write_row(uchar *buf)7 ?/ I- n+ K7 j1 ~6 w% R  r
    {4 _0 i/ @5 H! R- m
        MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })9 c0 C2 Q2 Y8 |% d: S: f2 j; G
    }
    4 \) w) ^0 f. ~+ t% P% z& @# a/ n0 k0 \
    //这是一个虚方法
    $ V  M8 U7 S" i4 m$ b( J3 `virtual int write_row(uchar *buf __attribute__((unused))): t# e9 d3 t; o  M* u% l+ I
    {
    2 {' r% P, l, t' I5 F    return HA_ERR_WRONG_COMMAND;. T6 g1 A$ r2 ]! n9 }6 J
    }
    * H0 R9 Q5 C- L
    & _) ?- B( A9 D2 _' x' T4 D; e: ]4 f2 S# I( F$ Q1 v
    看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;1 V& p& ]4 ]3 i* [7 \

    & Q. r  S$ |# F- P1 z4 `3. 调用链图) `7 C% t! N; K( ~/ ~: Q! w
    这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。) \% [+ ]3 t2 i; B

    . {+ @  L2 ?* ?6 G6 [8 Y8 u
    3 U3 Q/ m* l8 n+ {, r' {' b: ~4 r. T: C: q
    三:总结
    ( P. G' `1 ?! u7 W. h9 \- y* X大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。/ u3 T) I) p4 {; [, {* k
    ————————————————
    6 G0 l; |$ Z: [6 w3 t8 \1 W版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    ' ?) f- M1 K6 u: Y原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
    2 `  `) G  v; u: H( u- z+ }, g
    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-8-2 05:48 , Processed in 0.356061 second(s), 51 queries .

    回顶部