QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 3636|回复: 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
    一:背景! ^; C2 W/ |; W3 t7 s5 {1 y4 [1 U
    1. 讲故事
    3 [7 _+ }, a* s9 m1 c最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
    * p$ G8 `& {$ P2 I& x+ t2 m; i" z0 L; K+ ?5 ]. G" Y2 a
    二:了解架构图/ T) h; l; N" e. {! `
    mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。' f& M/ F: y/ Y* C
    4 D4 w/ |  }& ^  s# i
    1. 从架构图入手
    - z! f" |! @; h7 b$ o大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
    / o, d0 r  `# G7 a- w
    7 ?0 E) V8 L: d& U. D+ S  n+ ]: Y5 h, a  X9 f4 {: n  e( z
    - R0 |! V  A; P( j! n" g1 ~
    其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
    5 ?, e- l# c& l8 x7 I3 Y0 w* z4 K+ o) X1 `
    2. 功能点介绍( C. h6 Y+ e" [
    MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
    " Q, l/ _# z$ v4 l. c: C
    ) g8 R/ @# {# L" u<1> Client  K+ A0 I* K# Y* W+ }2 X
    不同语言的sdk遵守mysql协议就可以与mysqld进行互通。  k+ r; x: f* t. X
    - a/ g. v1 Y' R% b; ~9 V, W9 e
    <2> Connection/Thread Pool' q" W! M& P( A, ]' a
    MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。
    ( g8 X1 @/ \6 X5 {
    ( b! D/ X! e* X<3> SqlInterface,Parse,Optimizer,Cache
    2 M$ [  j* }3 Q0 U4 |1 c5 W对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。
    + s5 `6 A2 L6 D* ~8 U# j( q! y: k8 h$ r4 R# Z3 A/ P+ f
    <4> Storage Engines% R( S# C3 S/ Q5 f  j" _! V8 z
    负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,&#128046;&#128067;。) F# R# o9 o, e

    : v9 S0 `) f  }7 s+ M  Y+ P三: 源码分析6 x1 ~* k* O9 f1 B, _, i5 S/ q# N9 r- k
    关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。
    1 h+ d9 J5 X" H
    1 M6 q  K. C( T0 Q9 b! @1. 了解mysql是如何启动监听的
    % e- _% ]! ?! B手握百万行源码,怎么找入口函数呢??? &#128513;&#128513;&#128513;,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
    9 b& |" v# P" H4 w1 F1 A
    ) ~  n9 f3 C6 _, H, d  k, y8 m
    5 ^8 F& a: w9 O: k0 j; I+ Z, f8 c$ [; P  ?( R7 r! a
    从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。4 ~+ i' i; @' ?7 M0 M" R) g
    / H; T$ r! L7 T9 b" p
    <1> mysqld_main 入口函数 => sql/main.cc
    8 t) U( q& ~7 m4 E0 j  o
    # {; U* m3 m: j& H0 O- X* j/ g! D1 @( g7 ^# v2 y# S
    extern int mysqld_main(int argc, char **argv);
    7 \& J& y5 p/ _2 ?2 p
    . }+ T; [# m' s6 o9 n, f  c6 v7 dint main(int argc, char **argv)+ @- w5 _/ K: u' G$ ?& N9 [2 U/ S; n
    {
    - E1 v8 F+ I9 p$ {& {  return mysqld_main(argc, argv);) w3 G) y! a0 B/ k$ w3 d. S2 _( ~" k
    }* y4 Q% r2 r) f5 y
    1 n* J0 K+ M- i" L: t/ i
    ( y8 A+ J8 S- n  K& I4 J
    这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
    + y, T7 u6 a. ~* w% S
    9 K9 ^6 f/ X& ]# V' P6 S) z  |: j<2> 创建监听5 S' L& B! w9 N
    0 {/ g( v- H5 {+ B1 g. y  L

    % c/ @, l5 Y/ L) iint mysqld_main(int argc, char **argv)1 ^  y: k( {' i. O
    {4 F5 e! T$ N) @8 h$ ^/ ~
        //创建服务监听线程
    3 L: Z/ t# I) f    handle_connections_sockets();
    ) M# |' ]0 a+ v% w4 d: ~7 B$ C" ]}4 R5 E: J# e; G9 x$ s

    9 Z1 h1 {: _# p7 M7 Svoid handle_connections_sockets()/ }6 _/ A* [. f
    {/ C- A' T! e" _. s9 H' M
         //监听连接5 P* z$ R+ F4 l
         new_sock= mysql_socket_accept(key_socket_client_connection, sock,0 @' [2 e* _! W3 e
                                        (struct sockaddr *)(&cAddr), &length);3 @+ {9 }+ ]9 F! r1 h% |$ d  @

    0 b4 x' P  N- Z5 u: {    if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
    $ X5 n3 ?  D4 p      thd->security_ctx->set_host((char*) my_localhost);
    / ~0 e$ t, B% d8 z- o0 n# j. T: z9 B$ o
        //创建连接' h6 n$ j+ M7 b' O: o
        create_new_thread(thd);! B4 |) v- D0 ?8 R% y% n
    }1 Y( N- q) A$ ?- C4 {4 o

    % ^, _3 g8 z' S6 [' U! {- z4 p9 y//创建新线程处理处理用户连接' C4 w8 ^3 H5 e6 x
    static void create_new_thread(THD *thd){# i  a+ q  F' `% J! n' }7 g

    ! M3 [( A9 b) _+ ~) q   thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;5 v3 J7 F" B! g; g  C5 Y

    ' y% D$ [. r! ^( I5 `+ {6 G) i   //线程进了线程调度器" N2 [( o/ z+ B. n) k: ?
       MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));   
    1 [: k! l5 P8 |$ C5 T1 S2 U: [# q}
    $ s8 u! g: j& k( q
    - Y2 I, Y; l7 ~5 V; E1 V. e
    * Q3 T0 z2 R6 B# E至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。6 @: |3 @  ^3 L( C
    7 v0 J9 }' F3 d6 ^$ _4 ~

      E( N! r: V: l: {2. 理解mysql是如何处理sql请求  m- @) t9 w: ?& n  E" s$ I( D. v
    这里我以Insert操作为例稍微解剖下处理流程:
    9 q# h8 W# x  t0 j
    5 T( ]; b! N8 \当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。# ~/ c! F0 R. v3 T& }8 s1 U

    8 i+ a3 ^' G" G: a3 ?
    ; `$ Q( o0 k* {0 J& G4 xstatic scheduler_functions one_thread_per_connection_scheduler_functions=. S# z7 A6 G) y" o& J
    {  p3 _! U$ e- u! ]) {
      0,                                     // max_threads$ k1 V- X5 D  h4 h  ?7 }& P" S
      NULL,                                  // init- Q% u: @* T/ j: J6 ]
      init_new_connection_handler_thread,    // init_new_connection_thread
      N" w3 c) l5 K$ [8 f9 k4 r% Z  create_thread_to_handle_connection,    // add_connection
    , @1 P; o8 X! @) `& w, K: H  NULL,                                  // thd_wait_begin
    / y- G' S* w' Q. D* [# G  NULL,                                  // thd_wait_end; x9 d3 |& F) W/ x% N& b$ t
      NULL,                                  // post_kill_notification
    5 q, V8 p! t7 c, @+ L- A  one_thread_per_connection_end,         // end_thread0 V. ^2 R0 p! m  ^" n- `8 ?7 J
      NULL,                                  // end
    2 Y$ t8 A/ r. X6 O1 j8 W) k7 O& _) f) ?};7 L  J2 j1 T; q7 d, J

    * Y) P, S; C& T3 U2 u0 v: m+ ^" ]3 a$ _% j
    从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。9 u' i' v; G9 o  {1 g# D. d

    9 q9 q" f; }( U& P' w<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
    , s! ]5 j' D& ]0 g, K3 ]# G
    2 k: d7 G  k: F: U0 Wvoid create_thread_to_handle_connection(THD *thd)& s9 x; p6 K3 h4 {+ T, ]; r" Q& [9 r
    {/ c. [; Z* t" G3 f2 D
         if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,
    ; e$ L& c" ]! P8 T  f. F* a0 V                                     handle_one_connection,(void*) thd))){}
    & o# s) m. i2 r1 g1 O}
    & S( Y: x* n# {8 ~& l* }//触发回调函数  handle_one_connection6 w, u1 q, P/ ^. n
    pthread_handler_t handle_one_connection(void *arg)& [* l- V2 s  ~" t4 @
    {
    1 X0 ^4 z/ e4 I5 _; N0 W8 l: q     do_handle_one_connection(thd);7 Y' P$ V* b! e+ w0 D
    }$ }+ L: @* w  [
    //继续处理/ w/ z5 g6 U+ ~( }+ T% u! o4 X
    void do_handle_one_connection(THD *thd_arg){
    . u! z2 f' A2 R' F    while (thd_is_connection_alive(thd))
    8 O! |9 I" c) w* I    {5 {9 y; e9 q8 H  h* `
          mysql_audit_release(thd);+ k9 d% C) F4 D# ]; s  T
          if (do_command(thd))  break;  //这里的 do_command 继续处理+ q$ [% Y7 G$ a8 k7 H. j. c
        }7 S, y: R# q5 Z- d  P1 B1 Q9 a
    }5 K) t9 l7 S" t
    //继续分发
    * G+ o5 b- s) ^/ Q' M2 E" ebool do_command(THD *thd)/ A* a, ^/ C" S# m" p
    {
      M$ l- t5 h5 H    return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));" A" y7 N. v8 A) Z6 `2 j
    }, `8 }2 l7 }% [8 i7 Q/ A$ t* [( F
    bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
    4 ]3 k1 I0 C5 F5 a{
    & d4 D+ K8 V( l# E( P# R) A      switch (command) {
    # B: a) q" V" ~# ~3 V+ j         case COM_INIT_DB: ....  break;, a, s5 k: s9 l2 a
             ...
    4 O  D1 m; w/ J, b7 X         case COM_QUERY:   //查询语句:  insert xxxx
    8 q# v( I% l: g  o0 F. ?6 ]             mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);  //sql解析& ^/ }6 F7 U3 A: t7 ~: t
               break;- I3 [3 s2 _( ?4 _# g
          }
    & _4 I0 }+ ]: Z+ P0 K- z4 k}
    . S1 N% f7 L$ [8 ]$ u4 a. ]//sql解析模块
    ( F. F/ y6 |% v" w5 B; q- U! Vvoid mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
    8 K; M# l, p4 ?6 Z{
    8 ^: ^$ G5 H; X5 ^8 M% r0 d      error= mysql_execute_command(thd);$ e  h1 s! r5 ]5 W8 W! q& e" o
    }
    - x6 t& N+ `7 O" H' V+ s: P/ \5 {6 W" ?7 ]0 p0 z. _' k

    $ ~+ p9 f9 w6 ^$ V$ N<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。, L' r' Y( \, K! Q5 |' l& K0 m( X  s

    * C3 H( }2 L& Q! S$ p  q; P( ^1 z//继续执行
    6 K0 [& j. f( K7 h6 C& mint mysql_execute_command(THD *thd)
    ! ?: h4 Q9 Y' f& ^% q0 K4 t, `{, P8 k/ T& z, _) Z
      switch (lex->sql_command)
    / V& B/ k9 k( b  {$ w/ _% L* y6 H4 q4 `5 k1 b7 e5 I+ U6 q
          case SQLCOM_SELECT:  res= execute_sqlcom_select(thd, all_tables);  break;! j$ }9 @3 k9 ^" v& T! {! s+ H

    & X5 d+ c" t7 V      //这个 insert 就是我要追的0 a6 l  @  d4 S! d. ~' h4 A0 R) I- G
          case SQLCOM_INSERT:   res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
    / a4 i. j, n) {                                              lex->update_list, lex->value_list,' d# G% t2 y* U5 t, C
                                                  lex->duplicates, lex->ignore);# `/ z2 c2 D8 t( w; e
      }
    6 }0 I  @$ Z- A( Q1 w8 _% D  U4 o}6 \- |+ E: w+ u- j5 z# x
    //insert插入操作处理, D1 f0 [) E- E/ @6 b8 I
    bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
    " L+ \& F+ P4 @7 [                  List<Item> &update_fields, List<Item> &update_values, + E/ k" x4 @( h7 L
                      enum_duplicates duplic, bool ignore)
    0 O/ p5 p4 D" z" w4 \- A0 N1 v{  ^; i0 I% r/ p, o" l$ d9 M
          while ((values= its++))
    ! K" o. p6 N) W; ^      {
    % g/ t4 F4 d, J7 o3 m( X; z           error= write_record(thd, table, &info, &update);
    - ^. v5 {* \5 C) I: W( N      }
    # ^0 q2 W0 s, ^  U( Y$ w5 Q}' ]  V& S9 ^: y% |/ _
    //写入记录$ @" ^# ^- m7 x* E( D5 j+ G( r
    int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)( F( k' h% B( O1 P# c) r
    {
    ( `! ~' k& m  i0 z( q    if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
    8 m. l( `4 V- ?% M, m    {
    - _& |2 o4 [4 \; C) q2 ?         // ha_write_row  重点是这个函数7 p$ f5 L! [7 [7 g2 i) ~5 _/ S4 S
             while ((error=table->file->ha_write_row(table->record[0])))
    / W6 w! i* p. t3 f4 z         {
    7 m: d9 \) u  g4 f  Z             ....
    6 {1 h1 p0 n2 v7 f         }- g2 W6 p6 b$ ^* N
        }
    3 D4 W0 ?: T/ F5 J* Z3 w% ^}+ h; g1 K3 t; r2 J

    2 H  }& v8 Z; L& M6 k. P  |1 i9 P! V
    6 H  z$ J; ]! P: }% l
    " ^! i3 a3 J# y& e; K: e& [+ |可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。+ S* {; _( y$ [$ z# u5 S, A# y
    & \* P  S7 l0 S  j6 [
    <3> 继续挖 ha_write_row
    " [( e( e, ~9 U# j( r4 s# M! q5 k. _5 [% m
    int handler::ha_write_row(uchar *buf)
    6 H! }1 u" z& l0 c( e{
    . @& }8 `. ]! W# ?  P1 A  a    MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
    $ ~5 G5 @, k4 W0 _, B/ j}
      {8 |/ C8 ~& T, T
    ( H4 e2 _3 Y+ Q! P. W) I//这是一个虚方法7 _* i5 o+ t/ u
    virtual int write_row(uchar *buf __attribute__((unused)))
    9 t( O% G6 M" S{
    - t% B8 N- j2 V. A) I" t    return HA_ERR_WRONG_COMMAND;+ w; K+ V/ z( W4 d% ]7 [; J' D
    }
    8 Z5 t9 R- {/ ?0 {5 K3 h. V: j; J0 \* i" M$ o6 J
    + r. I  _' g& r1 v& K
    看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。&#128513;&#128513;&#128513;& O4 [$ ^: a% Z9 X

    ' k$ V  P# Y  Y1 X( c9 A3. 调用链图
    7 }; w. z4 V/ }3 [2 m  |这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
    # ^# |  T7 |8 y, M# b( C
    ) j" r8 H! \( w# |9 q! ]4 a3 ]' \& n

    ; J7 S- ]7 T9 _9 k  @8 u三:总结
    4 f. \* b. D: S: f. J0 W大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。6 T  q7 Q$ m0 U2 Q- X! _& p
    ————————————————, Z9 ~# r- M1 [, U2 X6 x- X
    版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    % T2 r& K, A3 s, R( W( G原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
    * t: _4 |1 _, ^! {# k0 F" ^
    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-9-27 13:28 , Processed in 0.529495 second(s), 50 queries .

    回顶部