QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3565|回复: 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
    一:背景
    1 i5 r9 d2 G6 g. v3 f: s1. 讲故事, [# @4 C+ d# Q& }) q
    最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。$ {( R" e$ L2 w7 C/ G/ n& T

    8 D! L* [$ L# e. {9 p二:了解架构图. w6 z' X) R5 ~2 \  W6 y
    mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
    7 \+ T+ c* b% ]; m% g' X2 _' N8 T! X8 V  n- [6 k
    1. 从架构图入手2 S  {; w, Z" n$ W9 @7 Y
    大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
    3 v' V2 L' K- R5 B. a: n- u" ]4 I
    9 i' {7 @* j( J1 B5 V
    & S) k9 z/ Q; d- s. N, g: r* X& F8 M9 z  G5 |6 l) D
    其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
    / `* [! g  z& h3 e5 w% U: W9 H
    9 N- e1 I1 u7 J  t( U% j7 P2. 功能点介绍
    9 ~4 V: ~0 E2 `MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
    ) s* r: d. q5 b7 c# ]% i# C" t- x. c! D- Z4 Z: m
    <1> Client
    ) _8 y: J  L' f6 i不同语言的sdk遵守mysql协议就可以与mysqld进行互通。/ A" E% z& h" v, m% R
    9 o6 z' M/ q+ w- M
    <2> Connection/Thread Pool6 `& I, P. `8 p
    MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。% L& V# P$ ]! }3 Y3 k" F
    % |; f, T5 J$ b+ O6 ~: i: R
    <3> SqlInterface,Parse,Optimizer,Cache, g( O" A$ Y+ b$ l6 w/ x4 v4 Y+ P. n( ^
    对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
    8 _: N$ p9 _- a8 ^* C+ H: c- c0 p& ?( I% [( ^7 V
    <4> Storage Engines  p. A2 q1 I! E0 U6 F
    负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。
    0 j4 W) I( u. O5 e% \8 a
    6 G# k8 ]- O, ~* h% ?三: 源码分析
    : F" t9 |; i0 J/ b0 ]关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
    0 x" D- \* |! V8 x# f( K( l0 }5 h% k6 M7 c; ?
    1. 了解mysql是如何启动监听的
    * C+ q( r% P5 C7 o8 t手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
    & L5 v! t' k  S' o/ \" J9 i3 @5 W* @6 E: h9 M9 v
    0 ?2 R: `& u' i2 E' o9 ?5 P

    0 M, N8 P7 C$ [从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。& K4 B0 v* t5 n: P" u5 Q3 F8 F

    : M) ^! K- {* ]$ U( r$ Z2 b<1> mysqld_main 入口函数 => sql/main.cc
    & j% K3 N5 L4 g' K0 M9 `* K; Q# T5 a: D
    2 u' n6 {! a; O0 \
    extern int mysqld_main(int argc, char **argv);* n: u" O$ q6 I$ x0 A
    9 O' |, V) l. m+ G; i" a
    int main(int argc, char **argv)
    2 |5 a0 n, Q% }8 e+ \7 S! F, ~, S{: p6 Y% I. k7 ~
      return mysqld_main(argc, argv);; y. x) ~& k- A5 m# S
    }
    0 w5 D  K4 ]5 a# v- Y" D& w1 r& _  @1 q+ Z: l
    5 I2 x8 @  ~7 Y/ {7 u
    这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
    * g0 V/ t4 f9 i
    2 I/ [0 j" g4 J- z, M. c. K0 Z<2> 创建监听
    - f/ o0 O" i0 i( }7 _2 R+ L
    * W2 j+ n" o; \. m. O8 [5 q; s) Q* v% k0 h: F
    int mysqld_main(int argc, char **argv); H8 Q* w2 U* }6 s, G
    {* R  o: G; ~3 ^& _6 C8 J: x
        //创建服务监听线程
    ' F: |: j: N: y3 I    handle_connections_sockets();
    1 f- h) ~; ~. ~: T: i$ a! s0 U, G. F}
    9 F7 f7 j5 `6 e$ {
    ! }7 h& e; N# l0 xvoid handle_connections_sockets()
    ! T1 t* V' g! T$ N) R1 e{4 m! D" O4 U: ]' K" [
         //监听连接
    6 g; ^' |2 C4 f3 b# u2 x( V     new_sock= mysql_socket_accept(key_socket_client_connection, sock,
    $ o5 j: H+ T0 s; R                                    (struct sockaddr *)(&cAddr), &length);
    . H6 r+ i( q; }. Z& _1 I+ F# Y  ]2 H( Z2 z
        if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))) |- Y5 N) M' f6 W
          thd->security_ctx->set_host((char*) my_localhost);( f1 E& d: q0 R

    8 j0 L; A7 l. @9 `6 ^    //创建连接1 s2 G6 O9 u* ^
        create_new_thread(thd);
    " f$ t* L* Z6 P% h: h6 V1 m% L}8 [# \* ]- W6 f( ~: |6 ?2 H
    / Y, H8 `& i; G; W
    //创建新线程处理处理用户连接
    9 G2 W9 q+ \7 `. K* w% D0 s3 ~static void create_new_thread(THD *thd){% m* e9 g. S4 O/ ?( Z* ]" d, E

    $ g# P9 X& t6 x& D8 `0 J   thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;' Q: y( e. U% D

    0 e( V; M$ t' {5 c2 k: h   //线程进了线程调度器
    " K+ i+ @0 x# |3 _2 L; k# n2 o9 D   MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   
    , k- a8 [7 V' m) O}+ u" Z( M( V. t5 W/ {
    + U8 L9 O8 B( L# J( I- v  \

    6 V* _2 M( @( e至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
    0 S4 O! D6 K  {: E! h) b/ C  l7 T7 {; G) w8 Y8 P' o+ z

    - `, Z5 j. F- |2 n5 s9 f, \& e; ]2. 理解mysql是如何处理sql请求8 Q; R/ e9 s5 m7 m  J/ X
    这里我以Insert操作为例稍微解剖下处理流程:( }) b& l8 {  x2 E
    + ], a  y1 X4 r6 r
    当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
    ; k& _& P2 M  G6 |$ _/ ?- D
    8 o+ }* H3 \# K( p) b
    % J/ z/ F' H6 |; Y3 Mstatic scheduler_functions one_thread_per_connection_scheduler_functions=
    # k; m+ p" t0 I# B{
    . q2 |9 |6 b5 @( S! R# r/ t  0,                                     // max_threads. B( J( r& g5 Z7 f" Z4 m
      NULL,                                  // init
    / H9 s. i7 A, ^0 ~  init_new_connection_handler_thread,    // init_new_connection_thread8 u9 z# w( `; t$ o6 Y8 ?
      create_thread_to_handle_connection,    // add_connection
    / g; g+ {4 l9 k) @, h  NULL,                                  // thd_wait_begin
    . h. J. a3 B& }) w  R6 h/ |  NULL,                                  // thd_wait_end
    : n' p2 }; g+ Y( s- S' B9 F  NULL,                                  // post_kill_notification
    / x( O$ z+ ~& `" W/ j/ g: q, d7 G  one_thread_per_connection_end,         // end_thread4 a1 ^  F" a$ n5 A3 z8 T2 b
      NULL,                                  // end
    & W# F9 X/ \  m- s+ m" t' i};
      }% ]2 H6 n8 J; I) z) g
    $ v. l; h6 e* W; j0 w, o, }' m7 E1 K& J/ y8 ]( w: [
    从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。, I0 Y% I9 g% t) B
    ; Y  z- h1 U8 l$ W9 p
    <1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
    " n* g6 A" \  @$ b
    % V' v) B- Z4 F, mvoid create_thread_to_handle_connection(THD *thd); X9 M% }$ s2 @( J# N( e5 {6 Z
    {8 o  l( V" X  K# V. s
         if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,/ M7 [5 F4 @; G3 O8 l
                                         handle_one_connection,(void*) thd))){}
    ( L* X0 t* ?3 Z6 V}
    $ W$ M$ w3 k7 H6 Z* A' j//触发回调函数  handle_one_connection1 x0 T/ y2 ?. y8 `
    pthread_handler_t handle_one_connection(void *arg)
    " W6 ~5 E2 l  k{; [; ]+ Y6 m5 Q8 D8 I1 f7 R
         do_handle_one_connection(thd);; P/ b: j/ _' [0 Q) c9 ?
    }
    - c6 o3 y1 Q5 K# z1 n2 N//继续处理1 c0 v+ \& w2 Z  W0 c3 Q- Z
    void do_handle_one_connection(THD *thd_arg){& g+ {" Z# e6 c6 B0 @
        while (thd_is_connection_alive(thd))( i1 A3 A/ Z: t
        {9 V  N3 E4 o; h, _, }6 J9 l( `. e
          mysql_audit_release(thd);; o2 Y9 Z' B- s* Y, |4 E1 i8 F; @
          if (do_command(thd))  break;  //这里的 do_command 继续处理+ v3 p# s* d# M/ s8 n
        }
    - A6 u2 u2 @6 v2 I% y/ C: M  Q}
    $ Q% H. {" p# W+ u//继续分发
    0 u" B8 H1 j7 y2 U1 K$ sbool do_command(THD *thd)5 y+ D- |' m, {/ M( q% Q/ \* I
    {- s% y3 f9 |' y- ^3 e9 I
        return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
    , C& D* F% T0 [, ~+ n}
    5 z9 o+ Y' p' v5 t5 N  M. i+ j1 ubool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)  z% i4 u' u: P0 d6 @$ j
    {
    # d) K" D0 L/ \" Y' ^# B- {      switch (command) {5 F# m" v: d) Y! \+ J
             case COM_INIT_DB: ....  break;
    * A# W  r4 _, l- R; U4 }' e, A         ...! c2 p! D: S9 ?, u! b! m3 X( T7 {
             case COM_QUERY:   //查询语句:  insert xxxx- l- C7 R2 \" x3 N
                 mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析
    ; }  y; I' z  }8 v0 X           break;
    ) Q+ Z0 z7 S, O; [4 n, d      }: [' \/ p5 E( D6 K4 ~5 Z
    }! q- ~% F7 N% O0 ?
    //sql解析模块
    * |, Y9 ?2 e. U0 Rvoid mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
    2 ^: k9 F$ n+ j+ o6 N& g; F{4 v, Y) [8 @9 B/ N& \' T& s, j' G
          error= mysql_execute_command(thd);; ]. @  k/ r5 s/ `
    }
    * p0 D' P1 v0 g5 x$ }2 s# a. f) v9 B7 e" T6 A1 N
    , ?  c# L; f7 c" i3 M3 j7 G
    <2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。) U* d9 O& f$ k$ t( I, |% N- T' m5 `1 C

    0 ?0 A% a. ~: o/ H//继续执行
    * G8 ~1 S0 V# F1 w7 uint mysql_execute_command(THD *thd)7 h5 }" [6 U2 k7 _
    {
    2 r7 z5 h6 ~4 p5 \/ d: l  switch (lex->sql_command) ! P/ E$ O. u# d4 q
      {
    5 }5 W1 a4 W/ s, D3 [4 r      case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;
    8 q8 B% N. x/ {* L! g2 t3 l/ U; W' R! ?5 g
          //这个 insert 就是我要追的
    ) Q8 g: D: f* h* V1 l  A      case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,# \; k4 b4 U0 u- t
                                                  lex->update_list, lex->value_list,' u2 e0 F2 P4 C  \, Z) o$ c
                                                  lex->duplicates, lex->ignore);
    3 F! c) ^; D. {8 {* q' a- D  }
    ! N  D2 ?8 {( g/ m0 R5 B}
    7 z. c+ }2 r+ ?4 A0 G# M//insert插入操作处理2 ^- P( [( c. E: L+ Z
    bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,1 A+ p2 P! C5 ~. A2 R
                      List<Item> &update_fields, List<Item> &update_values,
    7 R7 {, |- O  h8 l: j* q                  enum_duplicates duplic, bool ignore), I' l6 g& ~' [6 i0 l
    {  y% a7 `& f: z4 m
          while ((values= its++)); e7 c* R3 a  n
          {
    - W1 K8 p, d/ y/ W8 U0 Q% O           error= write_record(thd, table, &info, &update);
    # B4 q3 U- d. w1 g+ U  [% Q, j      }0 i" T; W. R( n" o: |$ i9 Y
    }
    - q7 E7 T1 L4 ]( `1 A' @//写入记录
    6 w8 a# b9 B1 K7 W5 M  O' Fint write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
    4 x/ g. F" P4 {( W  h( r* h{6 H/ N! z' v1 O: R
        if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
    1 V6 ?) j" N1 b* G1 k7 j( S& [# C" p    {+ H6 X, b7 h! k8 E" m/ v
             // ha_write_row  重点是这个函数
    7 m1 p$ z9 R: h; K+ e         while ((error=table->file->ha_write_row(table->record[0])))2 L8 z. ?+ @6 w0 @8 V  b% [
             {
    # Z0 s( Z5 i+ C4 e/ K% D7 v             ....
    3 Q/ V2 j, [  S! i$ r         }
    / P# Q3 W5 p  N1 b' C    }
    6 c) u9 J; d0 Z- g}
    * y( F* u8 L8 [9 E
    8 v+ Z7 R2 l2 J* E4 @
      x9 F6 H, j0 }: p1 U' ?- t0 [& S9 p2 [, ]
    可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。4 G- j4 _+ C; k
    $ V) h% N% r" q+ J3 U, R- G8 T: {
    <3> 继续挖 ha_write_row
    7 E* G) R5 |$ c8 z- ^$ |/ g2 @2 ^/ X+ _
    int handler::ha_write_row(uchar *buf)) L0 z# S1 g; J6 v' |
    {3 H" _& A% D9 {# v5 i+ K7 d4 b
        MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })* ]. @/ e/ c9 B8 t
    }
    - n  n" k/ z2 j% U& C* P' u1 L
    7 |; s. S; l4 R* h5 V; B//这是一个虚方法
    6 i( S0 h, J5 t8 z2 F, hvirtual int write_row(uchar *buf __attribute__((unused)))% n/ l! U  W) [
    {
    ; L( ~- R4 J+ Q/ I    return HA_ERR_WRONG_COMMAND;+ A! r# j& _+ x# s! ~4 E
    }
    # |1 J- _3 X! n3 r
    : b" c9 }3 b% M" k! Q) @9 [
    & \, r/ j; V* b) A& g5 ^  N. s; ~% ]看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;' F" `  m. z% `6 Q( l0 b' r- D
    ; v- e/ d' X$ G8 f% |* }) n
    3. 调用链图
    ! x+ e8 J' f4 U这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。* o8 Y" M  u9 \5 U

    # j4 \0 c5 ?6 e" s2 X' A. f
    4 `+ w, t+ p2 |
    4 ?3 q' I- ?( [# P三:总结
    & u- w* E9 b4 X, C大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。
    * N1 v( ~& R: ^5 z) x2 N————————————————
    ; ^0 {  F% e# Y版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    " U/ C4 U6 @( X$ s原文链接:https://blog.csdn.net/huangxinchen520/article/details/1064874153 I- {/ ~& |$ R) M# V$ ?8 l& W
    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-9 06:06 , Processed in 0.724707 second(s), 51 queries .

    回顶部