- 在线时间
- 661 小时
- 最后登录
- 2023-8-1
- 注册时间
- 2017-5-2
- 听众数
- 32
- 收听数
- 1
- 能力
- 10 分
- 体力
- 55504 点
- 威望
- 51 点
- 阅读权限
- 255
- 积分
- 17603
- 相册
- 0
- 日志
- 0
- 记录
- 0
- 帖子
- 447
- 主题
- 326
- 精华
- 1
- 分享
- 0
- 好友
- 79
TA的每日心情 | 慵懒 2020-7-12 09:52 |
---|
签到天数: 116 天 [LV.6]常住居民II 管理员
 群组: 2018教师培训(呼和浩 群组: 2017-05-04 量化投资实 群组: 2017“草原杯”夏令营 群组: 2018美赛冲刺培训 群组: 2017 田老师国赛冲刺课 |
一:背景
0 `7 L! I; H4 Z4 B r# q @$ X+ f1. 讲故事
- |/ S: c' }* z8 d4 L3 ]: C最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
& K# M- s& ]0 M8 C ^1 F
7 ~! ^: {# ?# z4 w9 Y0 c3 n6 L二:了解架构图; d, e1 d& ^, W0 V+ [7 f) p- }
mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。" r; s( c9 X0 b9 s% T5 Z; e
, t1 P0 _! D! k1. 从架构图入手
) g% _8 d) F. |: u$ m$ t* x大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。3 a2 |7 j" D! E. Y# D: i
![]()
9 s6 \9 M3 D# W8 a& C1 N8 n/ n6 T$ G
8 z9 E; C# H+ C9 c其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
; p7 Y5 M- n& R& t# f! G
3 v- ^( p- H7 K9 w6 }: |2. 功能点介绍$ B2 |2 T9 b) o4 |7 F
MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
, @1 [$ L+ n3 _: E/ @7 W$ A, x1 u; y/ u. u* E2 c
<1> Client
" q8 ~: P' o1 h" G不同语言的sdk遵守mysql协议就可以与mysqld进行互通。5 H- e+ j; n4 K- J, D
- e- m8 D4 x% d% o( S
<2> Connection/Thread Pool
; K$ E [# Q+ oMySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。" H7 [, b$ }+ ^4 Z/ U" F
& ^# E4 {5 g( y<3> SqlInterface,Parse,Optimizer,Cache# r& O. @7 e& m4 k: J) m
对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。8 C2 K; Q( M% }, l. y
6 M) G& {5 O4 c* o0 y<4> Storage Engines, H; K( K$ g9 R6 ~( j d9 ~: d( d
负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。
/ ]1 x) H. J7 n$ s# u. d ~" m0 V8 `5 Z$ X& ]4 Z& H2 t6 _5 E" z
三: 源码分析1 m2 R) Y. y4 U6 f, c
关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。/ {5 u4 ^0 L, I( U7 e
, S! `3 ?5 m" T: v" U% S1. 了解mysql是如何启动监听的- P: b$ Z3 B& @5 q; _* n0 Q9 n% E o
手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。
. F7 h+ b* j6 \( A: _5 i, @5 ? - R- C: Z0 ?4 [3 x+ B( t
) y) {9 q+ k( @ i: c( j4 k( J9 v" P M# \5 j K8 r
从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。
. j! ?1 J. f2 L, K# S
* x+ V! I \& V! S3 v3 d2 k<1> mysqld_main 入口函数 => sql/main.cc
, B8 v& N8 z+ `( q+ W
- T* ^; u- {" l0 h* k4 \' v% Q: h+ s% O u& i( @2 C( I0 N$ Z/ s: P. C
extern int mysqld_main(int argc, char **argv);) F8 N/ Z; X# J6 F. E
6 v& i1 c. J( d0 A
int main(int argc, char **argv)" `9 |" Z" S; b
{( |! F" L! _- b' u0 t4 |
return mysqld_main(argc, argv);
7 u ]/ Y, [) X' f9 _4 a1 n}% _0 X4 ~- {8 E' J, H, D. B( Z
5 w( ]1 l0 _0 h H* |% t; \% v8 |( d5 H
这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
% j0 a( [1 O" |9 b/ C
1 j) I; K8 N- V3 q' q- c6 ?+ A5 B<2> 创建监听
. _( L6 r7 `3 f m0 n# J* z* z8 A8 C7 ?6 k" Z
2 L+ i1 A Y6 Q' I$ u) b8 _8 sint mysqld_main(int argc, char **argv)
$ @. D# J8 C5 ]6 d{
) S5 l4 {) S6 T- h- r* k7 o //创建服务监听线程3 u) c6 Q C% C: P
handle_connections_sockets();
/ q- N# c; b, E7 @}
5 J5 u, Q- K& N, J# G7 E5 |+ V2 f/ f! F, h/ e. H
void handle_connections_sockets()* O$ H7 f1 V8 U3 n6 c
{$ Z" ] [ e' O8 R: a( G) Q; r; K
//监听连接7 P! |. l* F$ t- r
new_sock= mysql_socket_accept(key_socket_client_connection, sock,
( F9 z7 ` r6 w+ i; O) o4 W (struct sockaddr *)(&cAddr), &length);4 z# J* D% C$ K2 ]) m
1 }/ y y8 N2 t! [* F
if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))& V4 {+ X+ Y0 i. R8 ]
thd->security_ctx->set_host((char*) my_localhost);
3 w @/ D6 i9 B9 q
& I4 S! m1 j+ G6 E/ I( ]. V. ~ //创建连接
' t6 E" E8 _6 v, j9 }% o8 k5 H) z create_new_thread(thd);5 M$ a5 J% N( }: L
}
0 f( Z% r# o8 l$ {7 @$ [/ e4 r9 N
& N7 p/ C3 A" \//创建新线程处理处理用户连接
4 r* _# K. m/ Y; j9 i% T; _static void create_new_thread(THD *thd){+ o+ m' W, @3 ? y* t
( V3 y( X( z/ `
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;4 S3 W: Q ]+ d$ l0 F! ^' O
" r1 j9 w9 e" @; Q6 X" f
//线程进了线程调度器
7 y# k; W4 i7 f MYSQL_CALLBACK(thread_scheduler, add_connection, (thd)); $ P6 h$ ^/ S$ e
}
! {- _ {" W" @* Z# _
5 i( W6 O* P+ r7 T: h4 Z" H
& m. `5 O" A! j. C2 k7 M) r5 ` r至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。
4 i/ G4 J8 P1 P$ K6 X5 z% A- C5 m2 \5 k) m- V* T
2 _$ m- ^1 {2 J1 \8 w
2. 理解mysql是如何处理sql请求
7 t" C# {6 \ i6 t8 I$ O. q这里我以Insert操作为例稍微解剖下处理流程:
2 @" o+ o5 H- \5 u9 H4 v/ q
. ^/ ?1 F% }; H: R, B3 w) b当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
# a) d+ r- T' ^8 a# W. p8 o+ A, P& \. n7 f
- e5 V3 w& {, b/ h' N9 ?* c6 g; Y& O5 Tstatic scheduler_functions one_thread_per_connection_scheduler_functions=/ n: I/ O1 G2 v, U9 k3 G. H5 \
{
6 L/ v+ e! _6 P9 h5 a6 L0 e 0, // max_threads
7 m. U% G e* L H9 n- E Z# i0 |- s NULL, // init8 c6 ^/ ]2 i1 Z" T$ B8 `/ w" l
init_new_connection_handler_thread, // init_new_connection_thread" s$ ~6 u! F/ r4 ]/ Y: G) K. X$ s
create_thread_to_handle_connection, // add_connection
: m7 c0 n# \3 V g+ Q% o% l& @ NULL, // thd_wait_begin
, b" A! Z1 f8 e8 [ NULL, // thd_wait_end) r; Y7 }8 M5 L- |1 z: n
NULL, // post_kill_notification3 _* D9 x. h9 v: Y7 u8 j7 ]
one_thread_per_connection_end, // end_thread, k: }9 o0 Q5 I7 a+ y( G
NULL, // end
5 S* _9 w5 R0 H0 l: r! m};: W, u* D: ?' ~& b% b9 w* g* r0 G5 [
, b+ t9 D. \# P, f) B
d& g5 z; U* G1 N
从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。2 f4 ?' j. L1 [ }! s
" F1 J% \5 d8 I4 ^3 W
<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
, L( r- q1 ^, f$ j5 j
) j( H' |1 w" D. Jvoid create_thread_to_handle_connection(THD *thd)
) E$ ?5 k& v: t" i$ K4 n$ r{- t, A* \0 M- }6 `/ d
if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,( K6 F7 L4 Y' Y6 G
handle_one_connection,(void*) thd))){}/ ?$ r% S( Q5 w/ [0 w2 [* z
}
, `* I& A! ~- U1 h: \//触发回调函数 handle_one_connection" j, R; G3 A1 S
pthread_handler_t handle_one_connection(void *arg)
4 g& x7 Y5 ^. h) r) k) L- ]{
8 Q5 E& ]0 @8 i: v, w do_handle_one_connection(thd);
, v8 C8 `8 E3 o, O/ N}
& g$ U1 T, e3 e% n) A//继续处理
9 }( m' ~3 c6 Evoid do_handle_one_connection(THD *thd_arg){
2 {1 Z. \. K$ H, a. I while (thd_is_connection_alive(thd))
, w: h/ q3 C1 v; i% n c0 Q. \, t. a5 f' w {9 O/ y9 y+ M9 o
mysql_audit_release(thd);
2 l& Y2 M0 R0 q) [2 }4 \( p if (do_command(thd)) break; //这里的 do_command 继续处理
1 d z8 c; c* Q2 B2 B: F }) f" R# Y! i4 R: @
}
4 Q; `' Y- f+ R+ r3 e/ @0 X//继续分发, z% Y$ ?$ D# J4 v5 w
bool do_command(THD *thd)( ^& t& u( ]8 m. \( [- Y6 V! ^
{
; \9 \ p) I& A; q, |& d/ q return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));) y- l$ s) ?/ D% Q. d4 c
}
/ L7 b) V# _* U4 f) Xbool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)6 [! E# \1 [# d. C) `
{
+ }' a, g: j# ~+ Y switch (command) {
( X. T+ J. E" _& e6 q& C$ e6 m case COM_INIT_DB: .... break;8 N* v5 N) E& ^9 m
...) c+ P5 v8 E9 {; y( a
case COM_QUERY: //查询语句: insert xxxx3 Q6 ~/ @9 |9 E. K
mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析/ c/ c2 a' j& G, l
break;7 ~: T# E: F( c. N) [* v
}3 G1 D0 p. |) ]# w
}
6 l! Q! w% t0 s% I4 D+ [" T; C {1 a+ d//sql解析模块
) W3 p5 B( `% k, c' \+ }# ~void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
( I9 |# { ^0 j `{
% u4 Z$ c7 J# I, [( [9 | error= mysql_execute_command(thd);
- p4 {/ i, F3 [$ n3 C$ _( K* l}
- E- S0 g1 G# o' |. \) N& K7 |; E! F8 { S
* ], [/ \; P3 ^
<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。% c: _/ ?# h1 F1 P
* y @0 Q& r" `+ q//继续执行0 d- Q* L. n/ D9 c4 C7 d* ?+ [
int mysql_execute_command(THD *thd)
9 X6 [" c8 z$ L( `{
/ g$ Z$ p- X) `2 j: i/ { switch (lex->sql_command)
0 B3 j& J D' G5 W& f% s% ~3 J {
2 f% Z0 q6 C4 N2 d, }5 E6 `# M case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;0 d& s8 M0 q' M& Z
: R+ ]5 e" D- z, b& m //这个 insert 就是我要追的
. Q- `6 U( N* w4 I" l case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,! [% J2 U5 b; N- m3 ]
lex->update_list, lex->value_list,
1 A% p3 x& I; J; a$ U lex->duplicates, lex->ignore);
5 s K3 N: `4 ^, d) J }
% o, U- Q" Z2 K}
0 G/ n& R' o: z//insert插入操作处理
1 I( }9 [* z7 N( K* Xbool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,/ x' r, j" f k, @7 \. f0 A. f
List<Item> &update_fields, List<Item> &update_values,
. ?; E9 o: k' u1 N# D% i3 B% R8 Y enum_duplicates duplic, bool ignore)- B! b) ~, D3 d X2 m7 X1 H
{- r" R1 m$ @$ d7 l' S; \
while ((values= its++))& P4 Z# `7 [1 h
{
5 w. A. Z! T' e$ G4 | error= write_record(thd, table, &info, &update);* W4 [5 M g8 j/ o; M% R
}
, w3 [$ i# n0 n' X}
2 x9 _2 f* C6 P! ?//写入记录
- e W, }+ q G0 J" U J" u; Gint write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)5 r3 m" m5 @4 X& n
{! m- e4 O: \1 Q% S* l
if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
/ r' t5 q& W1 _3 ~1 j: j4 \ {0 W+ S9 ]/ u; {2 M3 h. W
// ha_write_row 重点是这个函数; P7 n: x7 e, @0 s/ S k
while ((error=table->file->ha_write_row(table->record[0])))
. M6 V! e- a6 z3 `2 s ~, s) @3 @ {% ~ P: s/ c: W0 J6 m4 m& U% M& |
....
' a' H$ [- H0 c' |) z6 t }
( G: h9 S* h8 ]; h+ S+ ^. I }' O7 e5 i! D" |( f0 W
}: d+ t0 m) y1 G2 x7 _
0 H( @1 z6 N/ O6 ^, B7 ^. L. e# J" t
# o# J" g+ t1 V- \* q+ Q可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
2 g. y! G( C3 h
- ^& G8 u+ j3 ^& u<3> 继续挖 ha_write_row3 P6 a6 f! ]9 x4 Y* I- R
! h# j& o# z# Z' |9 Mint handler::ha_write_row(uchar *buf)
1 H/ U* [( Q u/ B: e/ j: i{
; s) B9 j8 A) P: w" a MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })
% h1 N" x2 G2 Q O- A9 ~' h+ d& I' X}
# u5 K, u# g8 L( ^0 ~
5 z. }8 x$ O! j) [/ j4 x//这是一个虚方法
4 ]* S% a! J$ Vvirtual int write_row(uchar *buf __attribute__((unused)))
0 N! R9 M- H3 R: f1 I& y$ A; W{
' J7 Y4 L+ c8 b8 U return HA_ERR_WRONG_COMMAND;
. j+ D' b! C5 Y+ F/ H}
- ?' x- i1 m3 J e9 F' M, u5 K% ]8 [
8 Z1 ?9 R( o4 X0 K) a
: I* \/ |+ j9 P# n+ k9 c% |看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁- q8 t5 v, D4 w: D
% ^1 T* T, [9 {/ t& V3 w
3. 调用链图& Y5 E/ v! p4 L% h7 b
这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
* o) L. q; g! J0 T% A! F6 a![]()
: W) B9 _ s' \- M# }: y6 r
' j* P! O. l4 X
! B( O$ D+ ^4 e0 O, {三:总结5 y/ K7 S6 S* R, M8 J
大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。
8 F( u* R# y2 b, i, W6 h————————————————
- u4 j3 K& V+ m X版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
, g2 i) S# D) G- b7 @! v原文链接:https://blog.csdn.net/huangxinchen520/article/details/1064874153 z* T- S) Z7 o2 I! Q" M
|
zan
|