QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3563|回复: 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
    一:背景
    $ @+ ^7 Z* Q% e% G; t! _1. 讲故事0 C0 j4 U$ C7 N  J9 f* ^1 m8 r
    最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
    , N1 D- |+ B6 q" C% y8 q) q! f- K9 t$ Y6 R' A
    二:了解架构图
    8 t! P+ }8 T3 x3 mmysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
    * {5 ^! `) Y  n* }: O+ d
    4 A) `7 }% B% C  W- V7 T" `1. 从架构图入手
    2 n# h; m+ \2 D大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
    ) e$ @+ A  y  V2 h) Z' o& ?7 l+ Y1 _8 R! X! M$ A  A3 i
    9 Q6 }7 p: F, \+ `6 z
      W! K7 V; J& g5 F  A
    其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~$ s' u1 h5 T/ E

    7 s- |- V0 {0 P; U* H8 d7 m6 C2. 功能点介绍0 r5 `0 X$ F8 R! U4 Y4 S; v
    MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。/ Y( i3 ^- M0 M4 @2 s6 |
    & a, r: @1 N! }* `
    <1> Client
    9 M* F: E4 O( f; C+ U0 \4 O不同语言的sdk遵守mysql协议就可以与mysqld进行互通。: N4 \* @7 J0 d. _
    & W# Z# l  a( ?; j
    <2> Connection/Thread Pool' E8 J( P( z5 k* ?; c* A
    MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
    + i! k) y! G- F0 k- }% f/ Q1 p" u; Q. g- I, Y# O
    <3> SqlInterface,Parse,Optimizer,Cache
    8 p  T' i9 U0 {0 h, }) A对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。0 }; a" @+ M& }9 j
    ) T! |5 E. a& C# Y" A$ }# ?' J* M
    <4> Storage Engines
    " ^5 y! d! X" y: D0 d8 f, b负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。
    0 R+ x6 Z% U. B5 M5 y" g) @
    4 |. e& s' P" ]三: 源码分析
    # m/ a1 f- _# i- U4 i5 L4 i关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
    & u* ^+ m( ^' v6 d1 U6 c' K* p( }! U% g- D
    1. 了解mysql是如何启动监听的
    9 y8 Y7 [+ N; E4 f% C* a; y手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。4 I: S( R! P6 |, p. _% J( s
    & q3 w5 Z; o# G* `

    & S$ b) W9 W+ Q& ~& H9 m3 }( P( C# ~5 G: X5 D
    从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
    7 k# F+ `' J- y
    $ e( q. I; s) k5 K7 ?<1> mysqld_main 入口函数 => sql/main.cc
    6 b  _; H. o" d- f9 E$ r& Z" U, V

    * f3 u; `! S% l3 ]) j$ L! wextern int mysqld_main(int argc, char **argv);
    ! p( \# W+ J: h4 a
      [8 v" n: b9 f8 X$ z& Qint main(int argc, char **argv)
    $ H5 H4 z5 ]( Y{
    ' L2 P8 b7 ?/ M( G: P; x  return mysqld_main(argc, argv);
    7 v# G: \$ B' ~& j2 V}
    . O  J2 e' ~/ \  v0 N% y6 F) W3 \) _$ W2 z4 B! a
    $ Y% ^" d4 Z/ c) S- ?' }  V2 I8 s
    这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。% T/ d! ?. b  f# y

    3 b4 f7 O6 g2 g9 A; S<2> 创建监听
    3 [, m2 b' y. Z; p" F* h- A" w& k5 @: Y0 \9 K: i
      |- L( Q: J( e  j
    int mysqld_main(int argc, char **argv)4 \( F# m7 e# E' X
    {
    * U" _. D5 P$ x6 M$ P+ X    //创建服务监听线程
    4 ^4 E4 {7 W, o# h; H: ?# B    handle_connections_sockets();
    - Y/ J9 x& `3 N2 ?}
    1 P3 r, ?) e! E/ e1 g- z
    3 ?" |9 ]* |4 U7 l6 s5 M8 vvoid handle_connections_sockets()( `. D! r+ Y3 ^9 U7 q+ J2 f
    {
    % c( V; d% H% F" F     //监听连接
    9 k, m, W' D& z% l* ], i1 I% ]     new_sock= mysql_socket_accept(key_socket_client_connection, sock,
    ; p- r& g9 b2 ]! J$ k                                    (struct sockaddr *)(&cAddr), &length);* `  [6 Y8 S5 r; M7 q5 T, j" d! i9 P

    ) O( ~6 Z+ S, G4 F6 K$ h+ h. I    if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock)); J& Z$ H7 O+ d, u5 O- X: c
          thd->security_ctx->set_host((char*) my_localhost);3 r: j2 l5 z1 z0 z: X/ L  o8 }+ n
      C- R: r# D0 f! E
        //创建连接
    1 [9 b: r1 t. |# e7 p; M2 l    create_new_thread(thd);
    4 }# y& H1 a/ z0 q7 F2 x' s0 Y0 h}
    8 f) l; N5 ?$ m) [# S( r& a+ v, j" Q, b7 b
    //创建新线程处理处理用户连接5 }% A, [/ L) ]5 I8 v1 e; F
    static void create_new_thread(THD *thd){
    5 w) ~; D" d: F( T) q7 j+ R  ?. p: e6 _0 f1 i
       thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
    & G) c4 l3 O# u! `: Q: i
    8 W) k# q* ~+ J4 ]) g   //线程进了线程调度器0 A6 q) x' {+ @# W
       MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   
    0 c/ A: m4 u- |, W" q! J% h2 {}8 t, C. i# @; U6 a
    2 j" k" r( R) A& }! W, t% T2 c# H

      K: m# j9 q' K+ Z& \3 K5 _* ?/ |至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。; I* O" a$ o& w. `

    . r* d* d8 |% J+ x. C4 y: w
    9 ~* F) j- b/ f! [- @9 P; G2. 理解mysql是如何处理sql请求  }- C. k& X- L& c9 ~
    这里我以Insert操作为例稍微解剖下处理流程:
    : A* G: d4 o: C1 Q1 L% Q5 t! b( r6 |
    当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
    7 p5 l1 G. u8 h5 p) X8 E% X. F7 G' \3 f

    " F% d) G" \  _6 p: K+ J3 zstatic scheduler_functions one_thread_per_connection_scheduler_functions=5 y- y3 S" l# m
    {
    % ]& @7 o9 l3 J  b* @) u  0,                                     // max_threads
    . t7 c9 m  }6 P  NULL,                                  // init
    ' @. f- h: ]2 T2 M9 A: S  init_new_connection_handler_thread,    // init_new_connection_thread4 o6 Q( S3 Z8 t, _# }
      create_thread_to_handle_connection,    // add_connection
    $ a7 e* I8 W8 q' k- g$ S  NULL,                                  // thd_wait_begin
    * B$ Q1 I! ~# i  NULL,                                  // thd_wait_end
    7 p$ j5 R" {$ n( y. r  NULL,                                  // post_kill_notification' i1 T% @8 s. G5 f: d1 J
      one_thread_per_connection_end,         // end_thread
    , f; C) U# X+ `  NULL,                                  // end, e7 w- v& g( j7 T+ Q/ V
    };
    ! b  X, e$ V2 Q  |, M- c+ c7 K- A' U1 K4 G& m

    $ E) s, n; F& M  L) C- ]从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
    1 B% t& y) R% ], Z& \1 Z( u4 o' \' ~- J6 |- \
    <1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪0 J3 f7 R+ r: g6 R* ?. |

    8 M1 _/ ?: ^9 x0 `' r% k7 Tvoid create_thread_to_handle_connection(THD *thd)
    " y! i8 I, B$ w{
    . k7 Q0 Y0 T" K* D     if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,* l5 l+ j4 x" n, s
                                         handle_one_connection,(void*) thd))){}
    ; S: e! f2 A# }9 \9 _- W" d* y$ `}
      f- H# }/ ?. J4 A; m//触发回调函数  handle_one_connection( l/ a3 l, j( O% F
    pthread_handler_t handle_one_connection(void *arg)
    5 K) Q% Y0 P- v  d9 ~- O, [{
    " U; X6 D  {% c6 ]7 E     do_handle_one_connection(thd);
    0 t( ~0 t8 ~6 U( @. i}
    ; e7 A* K3 D0 ~' s6 h6 W# g" Z//继续处理
    / L6 I% u6 ~! qvoid do_handle_one_connection(THD *thd_arg){
    8 K- g! k4 f! e$ S: _( |9 Z  b2 X. X    while (thd_is_connection_alive(thd))
    $ R( i+ J+ B9 y( O1 E$ y/ j% z    {5 P7 `8 V. h* N8 \
          mysql_audit_release(thd);
    1 E2 J& k; j. R      if (do_command(thd))  break;  //这里的 do_command 继续处理
    2 o. {" }' u9 h, l- R    }  H+ Z! e+ ]0 j* y2 t( T# [
    }8 k' r: @9 N6 Q: `6 }% S
    //继续分发* c% j" T, Q1 v! ^
    bool do_command(THD *thd)
    & v8 S4 P1 ?  w/ o8 V$ G' b{
    ! M% h- A1 G1 h5 A. T8 B! E  J    return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));1 E& c; [; ^: z- T* m( a
    }6 W; W' |$ k, Z! Y
    bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)9 C) |# b" ^) |
    {
    # U! L4 j: B. F' Y      switch (command) {
    " Y$ [; k. t$ d# O" B         case COM_INIT_DB: ....  break;# l% L: Q/ m4 r- i. o& m
             ...
    5 A# y# k$ U: j/ ?0 E; @         case COM_QUERY:   //查询语句:  insert xxxx
    1 \, U3 p6 B0 Q  o8 o+ s             mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析. P/ f# |4 O9 q+ Z7 C( R' g4 @; N! ]
               break;. _2 A) H, H* }  E1 D, O0 a: q
          }! |0 |$ ?0 \5 K" F
    }% l, F3 p5 T. t$ S
    //sql解析模块( K" e# \3 v' @1 I
    void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
    ( ?  f6 U0 A( U1 q" y- ~{5 O8 A" w$ R/ q* ]& h, t
          error= mysql_execute_command(thd);
    9 C+ U+ d/ U% q* h% k}* m+ [" d' z' l. l, _

    " ]; g  n' f+ h( G% x& N. L4 u5 J- e0 _! O# X! `$ f) ?
    <2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。7 B2 @+ z0 e2 q. B/ u% S

    8 k' W8 ?% h# _" s9 a//继续执行6 z8 L# k" \0 t- c: S( ~
    int mysql_execute_command(THD *thd)
    / q9 I; D" q$ e{
    & {2 _2 k3 _6 f6 H, e7 |  switch (lex->sql_command)
    0 @% A% z( j" _( S% n  {
    - `. h; P! M2 U7 k4 W      case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;: {, b3 W7 x% X$ ?0 G3 ]9 h0 x" }1 G

    3 x- c( }% m9 [) f2 _/ }7 V      //这个 insert 就是我要追的
    1 t1 @) g* c+ T3 g* ^( J/ I; L4 N      case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,! S+ c" k- q# o8 ?- S
                                                  lex->update_list, lex->value_list,  f( `' N* J* C) c
                                                  lex->duplicates, lex->ignore);) Y0 h+ V9 x9 ]+ t6 F7 Z, c2 R+ X5 D
      }
    4 O: c! _: i9 N" q# n}
    ; S: m) M) ]3 Q//insert插入操作处理9 N9 ?8 U# P" F7 y6 w
    bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
    # G0 k0 k& q' ]7 M7 ^4 b  T/ {                  List<Item> &update_fields, List<Item> &update_values,
    ) |- P: Y) {- F  d9 W                  enum_duplicates duplic, bool ignore)( D/ U, R1 H; P2 v6 ~
    {
    6 U5 y( N# |) X5 X3 q9 h8 d9 r7 F      while ((values= its++))
    5 t: S" h3 s% g! N' ?/ _$ j  u: l      {
    8 _, ]2 b3 ^8 k) c; E( a           error= write_record(thd, table, &info, &update);) `! x, f1 Z. d6 A& w& K2 `1 ]
          }
    6 b" b# L, G, x4 V' @}
    2 U) x( z( \* B% `$ p( L2 B& F//写入记录' b% ]! n/ x2 K0 `, ]
    int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)( g( ?( c6 i* \: R
    {. H4 h# }5 k8 N$ b. `
        if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE). j; g7 |" {/ t- e% Y7 w5 p/ G$ }+ B
        {: s( X( s% _0 M% s2 L7 g, y; I
             // ha_write_row  重点是这个函数: x( {' Y" ~4 ~! d4 A
             while ((error=table->file->ha_write_row(table->record[0])))
    ) x8 o) T: C- @! w5 l         {
    , D" K% f. D. ^6 {5 ?             ....* J3 m3 E& S  I& t: C* A
             }
    : c5 \+ l7 D; g; {  }" c' [    }
    / ?: @: p/ j  h+ d2 K, S6 D}2 z, l+ f1 s8 k/ _% |
    9 O  P/ s. v1 x* T' |7 ~/ k
    : c3 v+ z! p3 Q. Z, b; v8 v, `: ~

    / z# H) w$ f$ k6 K: D可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。/ ]7 I: X$ k6 q! {, O6 j  x6 H3 K
    ! U* A2 n, r7 w' `( m
    <3> 继续挖 ha_write_row
    , C1 G9 ?8 w0 E' O7 U! R. N" L3 m0 {+ [6 ^7 k  Y) L
    int handler::ha_write_row(uchar *buf)
    1 H: w4 p3 b  Q$ L  ?{/ L; `0 s3 G3 X/ {0 k+ k  W7 b
        MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })5 D* y9 v: b: H  W' K" e1 O
    }
    8 F% `/ `1 |* O8 \. K: ]
    7 N, `0 O1 z- c4 k) S$ A; u//这是一个虚方法
    , R! Z2 N' _5 S" w+ G- h: S  _( e/ ]2 svirtual int write_row(uchar *buf __attribute__((unused)))
    % Q. L9 A. ^2 t6 ^{
    3 [! ?- p1 C$ f) h    return HA_ERR_WRONG_COMMAND;+ N. n1 a5 D! K; J
    }
    ; Z* O# T7 G7 r0 V- D6 g' y, C: A8 j

    9 _/ p% d* F6 g  _9 d) {' X* q. d看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;* w4 y: L  F: I. X1 @
    - u& j, [1 C$ c; j
    3. 调用链图, T2 C0 g! a( j5 q
    这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。/ [" J; u& E4 t. o+ e( A9 F" u

    ( ?3 ]& L/ k6 J8 i
    : ]1 a6 e, f9 F  a4 A" U* B0 [3 S: u" ]/ H! W& A( L) u9 }
    三:总结( B$ H; w) P: K9 K: `- R
    大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。6 Y7 s9 C9 l8 V0 Z
    ————————————————0 l  T! S4 [! P. U
    版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    ! K* u/ j, p8 b. [( Q5 }# v6 K. P. _原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
    ' R2 t. v9 l. @6 Y
    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-8 02:39 , Processed in 0.522394 second(s), 50 queries .

    回顶部