QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3528|回复: 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
    一:背景
    : }, q* o1 H3 }2 h# Q5 w! \9 N9 {9 b1. 讲故事, G- t; U4 [, Z) v: h
    最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
    : r# |$ d, j% {) V5 P4 a6 ~
    & b! q7 B( H2 Y5 T3 p' N二:了解架构图
    ; a6 S* {6 O1 ]mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。# D) T5 p, i& i! m* p+ T5 m* p
    , j5 P  t8 ]! M* k5 M) V
    1. 从架构图入手6 @, u8 ^. ]) k* q/ F4 p
    大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
    ' e5 g: b3 K" ]4 S, z7 q& q# M4 Z5 Z$ A' p0 f

    1 T' Z; u' s9 S  c! D* ~* N. r6 G) k6 y) i3 j
    其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~. Y: Z) Z. }' C' c5 b

    , x  t) e, b* |' h; D  S9 }' z2. 功能点介绍
    4 ~9 a" I9 C0 m- b; T& {MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。+ H+ X" S7 b* v$ m' b/ u9 E6 u1 I

    8 A/ T5 g% i+ q$ f( q& Y- q<1> Client, I* v/ z$ M0 ]) |% D. n9 @
    不同语言的sdk遵守mysql协议就可以与mysqld进行互通。% z& [( z9 g% j- r
    0 ~; _" @& i, d8 W6 S
    <2> Connection/Thread Pool
    / }$ i, |, A: c9 b0 J+ h  qMySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。6 u, J: L; j* u7 E4 H1 `7 X. w# q
    $ n4 [9 N! D* Q% k! h% [
    <3> SqlInterface,Parse,Optimizer,Cache
    ( ?9 `9 \1 X. U/ b0 k对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
    1 F& I( p, S& L" }
    8 s! v8 k; T+ I<4> Storage Engines$ O: g% e! S6 B5 Q! O
    负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。0 e* ]5 O2 @1 L' S

    ' Q  a3 c% `* d/ _- I  X三: 源码分析
    1 P' O$ V  `6 M6 Y( Z关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
    7 n0 T6 E. A& X  Q* k6 J
    ) l: b  o3 f" c4 f7 G" a% _1. 了解mysql是如何启动监听的
    / J, e, \( t9 V手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。3 E4 U' B! g1 j8 i/ [( T
    0 Y( N1 _. M+ J: j

    6 u6 b+ w( C4 |# O3 O% y- h9 m& D- l" k9 ]8 T, y9 U
    从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。/ H7 P! C" }( J$ R
    ' p/ f( ?0 z2 c/ ^/ h$ q, ]! C% y
    <1> mysqld_main 入口函数 => sql/main.cc0 l7 W7 t- g, b5 W' ]$ L
    ; q$ J/ r/ {1 v1 S. s
    3 Q. ]3 _/ Z+ e; H& h
    extern int mysqld_main(int argc, char **argv);
    3 y% ]6 y3 A7 k, {/ E; v% ?8 v# E" i" U3 Z' l) l. a
    int main(int argc, char **argv)( q( X. x# b8 l+ m+ [  L6 t) v
    {
    5 B; u' D4 j3 e7 T9 {4 f  return mysqld_main(argc, argv);
    ( d) ]9 ?$ C/ Q: @' }# h/ N}
    : n+ u" N' a3 I. {9 ~& b( g
      v4 F, F- P6 t- [9 Z# C" Q
    " g- f7 V0 g" S# n这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
    9 m/ w; W+ j+ r3 h& b& [6 |+ E* }. l" ?; h* b1 P8 v: w; _$ F
    <2> 创建监听
    ' O* F3 w* `' Q. ^& f1 Z4 e. G
    & I. j! _+ j, ^
    0 e* S7 ^" E# T5 @5 i  vint mysqld_main(int argc, char **argv)
    3 Q: M  w, ?) I9 S  F* j{
    5 V6 H9 Z7 d" A9 v- L+ O    //创建服务监听线程1 }0 Z, x1 q" U
        handle_connections_sockets();
    $ T% x- Z! f8 K1 E; I. |7 g1 a6 v}
    9 e2 }4 ^+ Q- m# S2 I- a- [5 v" z( c9 L% y2 m) x8 |1 O- }
    void handle_connections_sockets(). d; n4 ?2 w) G9 E9 r9 B& Y
    {
    6 m, }7 b: u/ I( d3 J: ?% ~  K     //监听连接
    4 N9 T1 k/ f" f( e     new_sock= mysql_socket_accept(key_socket_client_connection, sock,
    ! Q9 Q! s# D% U6 Q) p6 P$ _                                    (struct sockaddr *)(&cAddr), &length);* O- L) L$ ]$ R3 F5 \2 f
    / O! O: H" I  t# }& r
        if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))3 Y; E% M6 S5 n; S: H+ P. l
          thd->security_ctx->set_host((char*) my_localhost);
    # ?; z) c# }4 q8 F/ U- z. }' {4 C. B& C
        //创建连接# j# j- E' Y# N4 C: p* k
        create_new_thread(thd);7 {7 L8 d& A( I6 n
    }: ?2 `* D9 T$ d% B5 l) S! a
    , @: q7 o4 J% y2 _; M2 E/ g& c# Q0 d
    //创建新线程处理处理用户连接% M+ k3 N' A2 z  [
    static void create_new_thread(THD *thd){  h, J$ @( ^" V2 m9 M4 r$ u
    % d2 I+ Q& H; J/ W
       thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
    % }+ [, q  h/ D2 A4 {& U# G8 E$ a) k! H; S
       //线程进了线程调度器+ g, W: k4 C, S
       MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   
    4 t6 L: X9 R7 Y3 J6 L}
    # \* a. v- Q# t8 V9 r$ O- H1 T7 N. l7 U. b0 l

    + w6 S$ u7 \  U1 q至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
    " v6 M) u; f6 h5 T9 b" u" o1 p) G9 \& T
    7 c+ E) g; e6 Y. i) j" N! X
    2. 理解mysql是如何处理sql请求
    . j: F+ P4 f' ~这里我以Insert操作为例稍微解剖下处理流程:
    ( E# E& S8 I2 }
    * t: v) U% X0 B4 R* F8 f+ ^6 v当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。8 }8 _2 h/ _4 K" X) \6 Z

    0 ]! o, g7 j/ M& W7 U/ w) Q" A
    ! P9 s1 Z9 c- }% F* Z) Mstatic scheduler_functions one_thread_per_connection_scheduler_functions=- ?2 Q) Z' C, n0 a4 Q: L  F: g& K
    {
    6 {: u5 A& s2 }) N  0,                                     // max_threads
    9 r/ D) }' F6 _8 f  NULL,                                  // init
    - f" ?! m4 W: K/ v  init_new_connection_handler_thread,    // init_new_connection_thread1 u9 [) ^7 F" w" F
      create_thread_to_handle_connection,    // add_connection
    $ B8 N6 a5 h8 D3 i+ W  NULL,                                  // thd_wait_begin
    ( R+ ?; {, A( a  NULL,                                  // thd_wait_end0 h7 @. Z/ V4 d' b# F; u0 p0 x! U
      NULL,                                  // post_kill_notification
    * U/ L4 L( ~' {7 A" |9 `/ c0 S  one_thread_per_connection_end,         // end_thread
    8 e' Q2 I  O# B+ L7 G  NULL,                                  // end
    8 ?; k4 ~6 E, F0 ?/ p& c1 s};
    . N4 \9 `. R! f$ g7 z
    4 O4 W' ]: m% y- K
    " z0 E, ]1 |: ?! i% W" H2 ?从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。) ^7 ?# D  U( u* N4 D

    ' _; C8 {" ]! G: q% C" A3 m6 b<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪& p: P+ h  k) L7 ?. g
    : O1 W1 e" W- T! Y' R
    void create_thread_to_handle_connection(THD *thd)
    * t4 w1 {1 j  r$ C# h{4 L' {5 e( s/ Z' u
         if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,# D3 Y! O4 y6 s% H" @
                                         handle_one_connection,(void*) thd))){}
    ! r. D8 F; d& Z' G}
    1 R9 Q9 E' a8 Q. G: T, a//触发回调函数  handle_one_connection
    1 m% H7 M; h9 spthread_handler_t handle_one_connection(void *arg)
    5 o1 f4 m, S) _3 _+ |) ^! \" ~{2 o( ?% ]4 ^/ @: Q9 _
         do_handle_one_connection(thd);$ k. V& l0 {4 L* D! f
    }( o+ U' J2 X6 n4 k( ]1 M( \
    //继续处理
    ! u- O% _7 T2 ~' Xvoid do_handle_one_connection(THD *thd_arg){, Q5 h; c* p' g3 L& @; A
        while (thd_is_connection_alive(thd))
    % h8 d( u# v9 D    {
      O( R4 m1 f3 E7 n' g* i6 U      mysql_audit_release(thd);% W) z% V7 E: W5 w+ b
          if (do_command(thd))  break;  //这里的 do_command 继续处理) l' W1 x  g, ~8 o
        }
    % p% h1 W, L/ Y% F2 l' ^}
    ) R4 j% L- x' F1 u9 u4 o//继续分发" J2 ?0 X9 ]: |2 S5 }+ {3 t, V& S
    bool do_command(THD *thd)
    + V$ C, p; N6 l{
    & G5 n, i' p5 A! W  f8 @3 \4 I    return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
    2 q) N9 i. h+ F2 T  q/ [6 f}% s* b' ]; A! }) E1 k' S$ \  W
    bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
    ; N, W: Z4 j- j7 v; R& E* v6 H# }{
    / H, ]. j; l; Q$ q. D# ]      switch (command) {  K  c, s' M5 [" o9 n: C
             case COM_INIT_DB: ....  break;
    - P4 K" A* G: S9 L         ...7 {3 ^2 J* ~) l) t
             case COM_QUERY:   //查询语句:  insert xxxx
    0 n8 x& p( @- |( P. F* J" n  g! z             mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析" z% u( ?2 a) F( a$ l; `
               break;! n+ L8 [& _$ ?# T1 [; ?
          }5 b( `* C0 F+ [/ a' C
    }
    0 d8 D7 ]/ y2 {//sql解析模块* ~9 a' z+ A, [# Z. ~' Y: `
    void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
    & b; J, ?4 u# H  Z5 a! ^{
    3 ?$ U, x, a/ \$ U  Y      error= mysql_execute_command(thd);' ^5 `/ B# {# a2 d( F
    }
    / E0 Q, C/ N* `- v4 b1 q0 [" g9 w7 K% [
    8 u" C+ Q- ]4 D- Q( r
    <2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。
    * k) C7 |3 y" `8 @+ a3 n: l) p: ~  p$ H) z) I
    //继续执行/ N% ?, j  t3 q3 w; A4 B% J7 V2 Q
    int mysql_execute_command(THD *thd)
    3 b* w- W* |. V) F& t{
    . S, G( \' ^3 c" E% C+ w  switch (lex->sql_command) 5 x. {/ \$ i) U! w; {
      {
    " O+ q% l8 n# I7 D      case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;, w% h" R1 }; H
    : x6 V; \  T4 ^+ p- F
          //这个 insert 就是我要追的
    0 Z9 q. `$ k4 L7 b1 A$ b, y" t" J      case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
    2 {4 i- D7 g, p9 Q! Z: A                                              lex->update_list, lex->value_list,
      ?6 l2 O7 a: Q3 }                                              lex->duplicates, lex->ignore);
    3 p4 s5 @+ ^# b) L- k% w$ E# _( b  }
    3 J2 N! S- S( y, [" r}6 `3 N2 i! T) @
    //insert插入操作处理/ O7 ?8 N2 b. C; a
    bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
    4 X$ \" |3 n9 e! z4 `                  List<Item> &update_fields, List<Item> &update_values, , T) F0 Q7 C8 R3 v/ w2 j
                      enum_duplicates duplic, bool ignore)
    - |3 q6 U2 l' o{
    5 [) O) J0 \; a6 S8 w- f; \4 i      while ((values= its++))4 a( D* y5 r2 @$ s& X4 h/ V; a& E
          {
    ! m9 @2 Y. {" t           error= write_record(thd, table, &info, &update);
    1 w% P! b: Q* H" B6 a! e      }' G5 {3 W( P0 _# N2 ]
    }2 d$ J4 E1 a2 F0 I9 j
    //写入记录
    ( H( o4 W& |1 H5 Z  l# Z5 @int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
    ; B* F( f, Q0 I1 L8 S$ e{; w$ `% b; n5 @- q6 t
        if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)6 [2 ?  J& T+ t- a  N  F" m9 `. N
        {
    5 D9 @% r8 N" P0 H         // ha_write_row  重点是这个函数4 q8 U* P* C  r- N
             while ((error=table->file->ha_write_row(table->record[0])))" k$ e2 a* R! f2 S9 l7 {
             {
    , O+ j$ l5 f$ `7 V. L( m             ....# ?8 z) ~/ u) }1 c
             }/ U" b, j. }2 ]  K
        }
    ' p- _  Y1 X6 l8 b! p8 ~/ w! ?}
    * d; u" b# R! g3 C* S& t
    0 R4 X" L, Z- r& L1 z4 q) H( C6 p7 u2 R) v5 _5 j0 i- n' k

    5 i' h: T4 R; O1 S$ f可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
    7 `: T5 b/ V8 {+ Q. l" i$ U+ H$ y# v( _2 Y$ ^( h
    <3> 继续挖 ha_write_row
    3 o" A/ G. p; T$ ]8 [7 [, r6 D- c$ S% B
    int handler::ha_write_row(uchar *buf)4 G$ T5 {! O* \
    {
    6 c1 k3 F6 F7 `5 a    MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })% L9 D/ F, L2 t
    }
    . D1 Q0 b0 V+ V. b+ }7 }4 @& t3 {% G( g+ H
    % H% S: ~0 f! D9 L" W' O//这是一个虚方法/ O2 {5 g+ x. Y) Q$ c5 |0 U
    virtual int write_row(uchar *buf __attribute__((unused)))
    * {; f8 {# Z# g) m( d{
    7 B$ Z: O/ H; ^    return HA_ERR_WRONG_COMMAND;
    $ s# t6 Z+ C+ A2 u* _! v}
    ! Z2 b8 C% P: }( v1 J/ I# p  e3 {9 Z& d8 l$ @1 ~& }
    8 e* r! l5 m8 F3 |; P# d+ ~+ e1 d9 m
    看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;
    : p7 {4 S9 B1 Z" M+ z" S
    1 X8 q( a3 W. m% A7 }# I% Z; U3. 调用链图; s! T5 r) P- B9 d
    这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。/ C& V( b! u8 m( ~

    + W" d; u' ~4 i; e% @! r) v
    3 |6 H* o/ T0 b8 y0 _" [. t4 J& L) R% ^; g% k
    三:总结$ n2 L) S* w/ P& Y7 \5 X; p+ ]
    大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。& g7 Z# g. S* @& l' `
    ————————————————
    6 ]6 y+ H  z9 p/ M, y/ u版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    2 @! {' X) y6 X. K" O: v% M原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
    * v% h6 E6 Q& B
    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-6-13 00:49 , Processed in 0.457322 second(s), 52 queries .

    回顶部