- 在线时间
- 661 小时
- 最后登录
- 2023-8-1
- 注册时间
- 2017-5-2
- 听众数
- 32
- 收听数
- 1
- 能力
- 10 分
- 体力
- 55580 点
- 威望
- 51 点
- 阅读权限
- 255
- 积分
- 17625
- 相册
- 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 田老师国赛冲刺课 |
一:背景
% H( F" o' _$ R1. 讲故事4 ^' m, W0 K/ a) n: l8 o0 u% L
最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。
a2 ?( h) N% W4 M0 Q( [. ~8 B1 g" f# Y7 i0 M7 w
二:了解架构图
2 L. c+ B+ a# hmysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。
0 b# [2 Y* u6 c: q1 K3 Q- M. c' @6 J+ {) g6 g8 O3 y- S
1. 从架构图入手# B7 ]( |, I& R. ?8 m
大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。5 H; Z7 T1 q0 q) c
1 R( {2 y L/ u% Y/ u- c
e4 \; d+ L) K
# Q1 H8 _7 R: Z9 x8 n其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~
& R* s+ E. a# t6 B/ q2 m% ]; E+ I6 E* M4 f* a& V" u+ Q% @
2. 功能点介绍4 f) X% `0 Q: j/ V( l# A, _
MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
; S7 r+ R+ m# y1 c( O& G- s" u
! M% j% Z8 T9 O' [8 K& V9 H<1> Client0 ]- A! w6 T4 v. d4 Q+ Q! P+ ^
不同语言的sdk遵守mysql协议就可以与mysqld进行互通。* }, p) R2 p* F6 z
5 Y4 Z/ e; i) E3 q* \* L. R
<2> Connection/Thread Pool: W. S8 a& A( n b! Y
MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。8 _4 W7 X; T/ j
; w0 N* w7 L' y p% u" |
<3> SqlInterface,Parse,Optimizer,Cache+ }3 X+ H P6 g1 @3 a2 h* ]
对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。- T. s& z# S* a/ \4 w5 H
w) @ p1 V R% P# _1 v<4> Storage Engines
0 a. r: J7 y" D7 {负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。
6 B) X9 c( R$ S/ B# M0 w0 h+ {) S( `3 b" w3 _; i
三: 源码分析1 q: J; _9 c8 P1 e# c5 V7 q
关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。& W6 X' ^5 z1 ~* h; }" {4 k, _
) `$ s( R: d+ O6 w$ H# j- X2 N* W0 K) e
1. 了解mysql是如何启动监听的
0 ?! v- j; P. d/ |. H手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。" J' K2 [; X' u" h
![]()
5 h& K& j9 n2 L8 t# y5 r0 x2 }5 m: F. G+ a/ _
. }: } @3 D6 K* Z: w: G
从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。! A& e0 A4 G: C. r3 n
/ s4 K. K- P* Z& S/ R
<1> mysqld_main 入口函数 => sql/main.cc
* C l+ q/ ^" T# d2 j5 g
w3 F1 [/ x, T3 ~8 `
. s+ k0 |2 K3 ^9 lextern int mysqld_main(int argc, char **argv);2 ?& y$ e! R, j' T! ^/ x
z6 q7 {9 O& C9 pint main(int argc, char **argv)
: W( p# ?% d! N8 H" s3 h4 C{
) C* j; z+ F( Z8 T# \ return mysqld_main(argc, argv);1 G5 Z! l, Q8 \. r
}1 K/ ~- T \8 c
) ?; v5 r: X, ?. b
, F3 C; s- X0 \9 I" j
这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。
" h7 B$ D3 I! h0 ~$ m# ^4 l9 d( ~4 @' v, U8 Z. {4 g
<2> 创建监听
+ L& {# N! [* V8 r0 `/ Z9 p! @3 r3 b6 F
* H, A2 I8 C5 ?( X1 U
int mysqld_main(int argc, char **argv)
q" m& g$ H* x3 N{* r; C4 V( N9 H( b& u; }
//创建服务监听线程* x% M: Z, M, K0 n r* u2 _
handle_connections_sockets();+ t4 E' W3 y& M( T
}1 I+ j1 i/ \( ]* E
( I9 ?7 G" y0 {. D7 q
void handle_connections_sockets()
f- x1 `3 ^" _" P{ r1 _. E6 _9 S% O$ I5 J
//监听连接
0 N+ t0 i7 K' a2 I% U# A new_sock= mysql_socket_accept(key_socket_client_connection, sock,
6 i o& z) u4 z (struct sockaddr *)(&cAddr), &length);
3 C- A8 O9 L+ h9 l4 [! T. U# m
5 @# d) {/ x" U ]9 s% u: C if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
( A7 j& |# e" j, F/ w3 @+ w, L thd->security_ctx->set_host((char*) my_localhost);
) ]" m A! l7 @5 O! {6 U! o7 W' e8 I( a/ R, C
//创建连接' l4 T) R$ \$ |; m% q
create_new_thread(thd);
0 b" ]" B' t! }8 U8 ?4 G' H1 ]}
. b1 G1 M: O8 G+ B( T, q
h5 M/ ]; |# f# d" r//创建新线程处理处理用户连接2 B1 i! L& i5 Q; `' }+ ~/ i9 N
static void create_new_thread(THD *thd){$ [$ ]/ ~9 a% }5 d" n
& ~& f# V8 c1 T5 z% @8 \/ S. K
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;- I' }! F9 G! S
* j7 r4 k; g- o1 w
//线程进了线程调度器- |" M5 M/ H! ?
MYSQL_CALLBACK(thread_scheduler, add_connection, (thd));
J( o. H9 x& k5 r: m4 v4 D}* w, W+ x) q# P* o# q( I. N/ Q: R
1 g* A5 C) ]9 j1 y, M% |' U6 m3 |, ^! N+ e$ @3 f2 Y1 I
至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。2 ^% U4 m# A2 O
5 M& R. J8 ]' M0 s- l% Q
( i1 P2 J6 s! y( R0 n6 D2. 理解mysql是如何处理sql请求+ X0 w& M% ]4 c) p& ^" X6 t
这里我以Insert操作为例稍微解剖下处理流程:
2 o3 w. a9 E5 v: p" r3 c3 o8 ~) |$ Y& N5 h! W2 X! p$ t4 r
当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。+ X( H5 v/ [+ p/ q& c3 A0 h
G; ?0 J1 l- I4 i* X
( Z8 E8 a& a0 Y8 |static scheduler_functions one_thread_per_connection_scheduler_functions=
* W6 l! A9 c0 w{5 o8 J8 k6 Q! n& O5 H6 q @$ R+ h
0, // max_threads" a7 M$ K& f N7 g
NULL, // init
0 m0 [) |! Q4 d P init_new_connection_handler_thread, // init_new_connection_thread
9 h3 V- l% ?1 r2 d) a9 Y create_thread_to_handle_connection, // add_connection
7 q+ W1 K. c* H( J# N/ e NULL, // thd_wait_begin' R! [ [3 ?" ]! q
NULL, // thd_wait_end
# o$ L/ x9 P: z( M& u9 x NULL, // post_kill_notification+ g" l2 y# ^2 U2 J' V
one_thread_per_connection_end, // end_thread
- ^ |& J/ |9 C) z2 w NULL, // end+ h s8 i' l* F" O: O, I) g+ L7 \7 Z
};+ D3 v- m4 O6 Y3 M
" U; b/ t8 s( t8 X- c
" n' x2 r }9 X3 B) V% ~" }+ V% o从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。, N7 @4 U( z1 g/ W- n2 m
7 s/ R5 \4 Y$ J; S2 X( E8 c& @<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪' A+ I; p4 y) E- x0 J Z
+ m( u: U4 P) M* a1 {' a
void create_thread_to_handle_connection(THD *thd). t/ Q. R9 q, }( v; f: q( y" b' C
{
: K6 C: f K8 h8 C0 u( w8 ^6 K9 i6 C* T if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,$ y' e7 _7 s( c0 |. d. d
handle_one_connection,(void*) thd))){}
- O- o3 s) A$ c2 \/ _& |3 i- Z+ B" e$ G}
4 Q4 P7 X4 X5 o//触发回调函数 handle_one_connection& [/ G. H( b# K: ^
pthread_handler_t handle_one_connection(void *arg)
( z7 h- `; A7 Y' ? M: B{7 O* g. f1 ` L) _
do_handle_one_connection(thd);
9 v8 t# h8 E" v}
! A9 H: e. ~% t C& A: C//继续处理
; Z5 p* z4 ?! O4 B/ b. {void do_handle_one_connection(THD *thd_arg){
7 R0 n1 u( p+ `0 x while (thd_is_connection_alive(thd))
! I) W' O( {$ {. z+ P1 z; m {2 c- a, u/ d% R
mysql_audit_release(thd);
# ?( }2 _( S8 R& T5 D8 o if (do_command(thd)) break; //这里的 do_command 继续处理
5 J0 h- d0 y6 e5 [* F" M) M }
# P% i0 w" o/ k* |+ y4 j}: K! S1 J! Y3 F, P5 [
//继续分发. c/ b7 c% b6 }7 h
bool do_command(THD *thd)7 ?, j, ]" H% X+ ^, ?7 c
{0 a! L7 Z# B. ^# M w8 q7 G8 f
return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));( X: S7 z$ H; c0 v8 }: y; S, n0 J
}9 O- ]( D* e6 |) H0 B5 O
bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
- V# p) m: [" p- |{
; N/ W4 T, T! U" v/ ~6 \ switch (command) {
: u' ]% w0 ]; B9 V' X! b, c8 N case COM_INIT_DB: .... break;+ p% ?0 O/ r4 o6 x
.../ R# o/ h* H$ f$ {2 e# M
case COM_QUERY: //查询语句: insert xxxx
! }7 R/ p5 i% E: V mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析* R Y% Z* H* k: p
break;: k9 ?) i( t8 m+ o# T! v# H
}$ g+ k' x& t) C, m
}
" _" N$ e- }( h2 t//sql解析模块
% z8 N. M- R$ B% I K1 p5 |void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
- U1 O9 h# m4 U4 c8 N, x' p{
3 H4 Y& P( @( ^/ |- w4 \* n, o8 f error= mysql_execute_command(thd);( J) Q; y1 Y- {3 L3 m3 a# d; x
}
, z/ x: H5 D$ ?6 f* Z+ E9 q( r z, B+ [" H- z. I# k" U/ U4 P* D- q
; i/ D+ c+ e; h<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。$ A8 R, H+ D o/ ^1 Z) @
2 L& K5 {+ H& N! m- O/ [
//继续执行; Y0 p) V% g5 u( o! m1 ?
int mysql_execute_command(THD *thd)
2 A7 r: q4 d3 j0 ]{" b* b5 x0 F0 M! W& a
switch (lex->sql_command) ' O$ ^3 r D; s( [4 ]+ y4 T+ w
{
( N) [+ c4 V% \ c- h/ [: ]+ z case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;
, S( D; K6 }3 U. G, k- f) U
" z/ h# u& m5 M: [# m0 _/ ? //这个 insert 就是我要追的
, V% a3 w, j) ~1 z9 s: p case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,
2 F; }5 H Q0 B5 l lex->update_list, lex->value_list,
' G5 z2 C. |1 C% H: Y" { lex->duplicates, lex->ignore);3 i& h+ w) N3 @
}! w# t6 [% m0 i2 M6 I1 U4 o
}. u2 ~9 [3 K% j: V1 p
//insert插入操作处理7 z, E3 {+ G. t c6 p5 P
bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,, q; c5 h. K8 V: p( f# X: F
List<Item> &update_fields, List<Item> &update_values,
4 t" t* Z/ U" ]5 ]0 J2 i enum_duplicates duplic, bool ignore)
; H3 f( w- l. \{
0 W/ R2 ? m" m! r7 C% q) ] while ((values= its++)), O- K$ Z1 ~7 P6 X& |" f) b
{
5 J- A) i! S @8 Z6 U error= write_record(thd, table, &info, &update);, G, p G& N; O% u2 D7 m) e3 P$ q
}! A0 n& {7 g2 h3 A
}+ ]; b" S% ~- C. ~$ w
//写入记录
$ Q" C6 X# `7 y6 F6 P: Mint write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)
9 u0 Q3 O7 _, W/ d0 j, L9 v4 ^& D{. o$ u1 H! p8 P
if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)
- m: H' p, b4 ]( H3 K* P {
: `6 Z `2 V" e: R. g* x // ha_write_row 重点是这个函数; C* v0 k- b& C# S- m
while ((error=table->file->ha_write_row(table->record[0])))
3 E6 c$ J- Z" x2 H6 [6 T7 u) W3 j6 _ {* B, Q, |3 ?' _8 j5 n" q
....
9 M. w( ? d- P8 n }$ @9 F# ?5 M! F- F' h
}3 Q4 n' x" F( R3 x
}
$ X% [- W5 j5 G, n! n8 E3 u* [# P7 `# S
( A/ T; f2 H( r1 r- t/ D
v4 n3 v! k$ m' i, G2 N# @* g/ W% J
可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。1 ~( d- I) s. p" N7 I
$ {+ M- i) w0 ~1 |' j& ]<3> 继续挖 ha_write_row
) X( O$ L4 |% |0 Q+ z" K; m7 D. ]
int handler::ha_write_row(uchar *buf); A9 e0 z+ V3 V. O' z& ^
{
- S1 j6 x1 D5 V MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })& Q! Z6 E1 v& z2 q/ F) K' ~
}
! V% R B4 [4 r7 {8 K1 Q/ r+ c. K6 V5 A2 J2 j4 P
//这是一个虚方法
2 f! H# u: ~$ G" p! uvirtual int write_row(uchar *buf __attribute__((unused)))! q! [/ ]2 t$ a+ d5 r
{3 z9 K; A e! n4 m! e) i
return HA_ERR_WRONG_COMMAND;
, r3 O4 z1 U$ _" P: Z( Y}
* _) b7 G9 b9 H+ m5 }0 r7 s9 L8 f: Q* v
. Z$ p X5 A/ S& ^2 l
看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁
% I% L5 f- ~: M. M0 T* ?7 l0 R) A7 i) M; m) d
3. 调用链图& y3 Y- z- X" E3 ^
这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
) T& R6 s( t' p$ x5 q![]()
4 G, X2 R" `# i M* A- K( _) g
2 U( R4 Y, q L2 s9 [- @% R) O4 d- v, a7 [
三:总结
0 @8 l$ d0 u' d大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。" E+ N: ?* \. m7 q+ \+ Z4 c# |
————————————————7 L; I, V% b2 ]% E. k
版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。' L$ C9 T' F }- ]$ s# \$ l
原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415+ p' y8 C1 X" w' D# E8 M) a1 m# q
|
zan
|