- 在线时间
- 661 小时
- 最后登录
- 2023-8-1
- 注册时间
- 2017-5-2
- 听众数
- 32
- 收听数
- 1
- 能力
- 10 分
- 体力
- 55557 点
- 威望
- 51 点
- 阅读权限
- 255
- 积分
- 17619
- 相册
- 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 田老师国赛冲刺课 |
一:背景* g/ d+ z! h$ N; _& o8 ?6 p
1. 讲故事
6 T! g. u5 t; q最近看各大技术社区,不管是知乎,掘金,博客园,csdn基本上看不到有小伙伴分享sqlserver类的文章,看样子这些年sqlserver没落了,已经后继无人了,再写sqlserver是不可能再写了,这辈子都不会写了,只能靠技术输出mysql维持生活这样子。* ^: M) `7 @. U8 w
7 d( r( w9 ]1 h- w+ e; ?1 h2 ]
二:了解架构图, j7 l4 U) l2 d8 W2 l7 h7 C; @' d
mysql最大的好处就是开源, 手握百万源码,有什么问题搞不定呢? 这一点要比sqlserver爽多了,不用再dbcc捣来捣去。: ]% E, p6 F% M; J; E
! b1 G5 k5 P, q/ o) Q5 _3 A1. 从架构图入手, B% `' q7 u) ~: ] F1 m3 T
大家都知道做/装修房子都要有一张图纸,其实软件也是一样,只要有了这么一张图纸,大方向就定下来了,再深入到细节也不会乱了方向,然后给大家看一下我自己画的架构图,画的不对请轻拍。
8 s! E. R$ r& Y![]()
+ ^) W X! {! `0 \ c4 U8 u! ?7 L/ `+ l
9 \1 g% R4 y- L% h- i4 ?5 P其实SqlServer,Oracle,MySql架构都大同小异,MySql的鲜明特点就是存储引擎做成了插拔式,这就牛逼了,现行最常用的是InnoDB,这就让我有了一个想法,有一套业务准备用 InMemory 模式跑一下,厉害了~, g1 j M* o9 R% _+ u' r6 ?! l, R- g
- [+ t3 @4 s) J6 {2. 功能点介绍# n* Z* f: ]: S
MySql其实就两大块,一块是MySql Server层,一块就是Storage Engines层。
2 M2 B1 S, o' i
( C$ b8 Q% B! k0 s4 x" v<1> Client
* n C9 T3 E& Q$ M! A. u D不同语言的sdk遵守mysql协议就可以与mysqld进行互通。7 }( g- s! [0 M7 z. i+ A$ y, w' J
8 W* m! `( S2 w' m
<2> Connection/Thread Pool% G4 p9 o; m5 K) g$ `0 t2 e6 z7 B
MySql使用C++编写,Connection是非常宝贵的,在初始化的时候维护一个池。, _$ R: h9 B7 y7 }( e! E/ G
& k3 @ t6 N' \7 q" Y8 y7 J, ?
<3> SqlInterface,Parse,Optimizer,Cache
' V4 Y! F, j$ [0 C. {! u对sql处理,解析,优化,缓存等处理和过滤模块,了解了解即可。- g) u8 w% s6 g
! B v# |4 S/ D7 j1 t3 M0 M' j2 D/ c
<4> Storage Engines- ]* S ]4 ^( ^, s g
负责存储的模块,官方,第三方,甚至是你自己都可以自定义实现这个数据存储,这就把生态做起来了,🐮👃。2 q# A" D# L1 B6 Z! s! B
) }) F: a& r! h+ c3 k8 a8 @
三: 源码分析
, Q# q" y( D+ ]7 T7 t关于怎么去下载mysql源码,这里就不说了,大家自己去官网捣鼓捣鼓哈,本系列使用经典的 mysql 5.7.14版本。 H2 ]& s' o4 m9 z2 V
, c2 V# \0 y; i$ e, G& q6 R+ H8 H
1. 了解mysql是如何启动监听的* o5 ~% D8 B4 E/ b2 I$ T
手握百万行源码,怎么找入口函数呢??? 😁😁😁,其实很简单,在mysqld进程上生成一个dump文件,然后看它的托管堆不就好啦。。。2 U# a# u p( c& k' U l0 V, U6 P
! M I( i( N6 s0 Z+ k8 I
0 V! n% a! X' J4 k
6 V6 O J' F0 f( l+ y) |! f5 `0 V从图中可以看到,入口函数就是 mysqld!mysqld_main+0x227 中的 mysqld_main, 接下来就可以在源码中全文检索下。3 q8 l2 E4 s8 O0 A
: L0 K8 G6 Y- x& P<1> mysqld_main 入口函数 => sql/main.cc) t! y& Y& F, T: O. x! {
; b8 g; [" C1 ~% t
% e7 Q% ^+ L2 |% r3 n, H* T% P
extern int mysqld_main(int argc, char **argv);
% R$ j* G, `6 i! h4 v2 J: i# Q: x, e( x5 \( q f" x) N
int main(int argc, char **argv)
0 t I& j n' a{
, k8 g2 \3 G7 V* d: i+ M* `$ K return mysqld_main(argc, argv);. R4 |. `& g0 U6 O* j
}9 D# D3 c1 z$ |: }
/ C k6 X" I" X" b
6 y6 |$ `9 n3 T: v) `2 p7 q# u
这里大家可以用visualstudio打开C++源码,使用查看定义功能,非常好用。 I1 q$ ?0 Z! @; M- d
' O( Z9 [4 `% _2 l8 c/ p<2> 创建监听
8 ~4 W* p1 H% F' F' W" ^% y! B+ b' k! X J5 L4 q2 N9 s6 }
4 Q, L# f5 R k" f" m% H
int mysqld_main(int argc, char **argv)1 e0 x& Y) P% V T6 ~" e2 g
{: \( N( x6 ]3 h O" C$ u
//创建服务监听线程
. Q% N( `/ [6 Q( v" i2 X handle_connections_sockets();
$ d$ Y5 R, C9 z( j9 F. t+ b( e}8 t% Q- J' B- f2 f* {3 a
* V. V/ b& J1 y6 K wvoid handle_connections_sockets()
0 y5 H6 Y% ^# g. k{, @+ Z+ x- o9 H$ Y) `5 `4 i {% }
//监听连接1 j; I- L) m2 G# M5 o1 m q$ z3 p$ ]
new_sock= mysql_socket_accept(key_socket_client_connection, sock,
9 p- S6 b6 A5 z( z1 D (struct sockaddr *)(&cAddr), &length);
2 Q2 A% q' ~" W' t
( X" c+ a9 P) @; n5 w9 } if (mysql_socket_getfd(sock) == mysql_socket_getfd(unix_sock))
! O. f1 t. Z: m8 m% d thd->security_ctx->set_host((char*) my_localhost);
" f1 }* N. g3 l: ~/ P# I. e$ g$ d. o; A1 m. E# D: T0 T4 h
//创建连接
- ~$ {& w: V( x# |! l' {5 ]5 } create_new_thread(thd);+ h& N# b5 z# D- K2 ]
}+ e" w$ x+ A' ^0 [8 @8 }8 Z) c
& o4 R! b+ F' b( v# `
//创建新线程处理处理用户连接, O/ n) k" @8 q
static void create_new_thread(THD *thd){
1 \& Y! ?8 _& G+ a3 e+ C' I& S' v% c$ o; f8 D# o
thd->thread_id= thd->variables.pseudo_thread_id= thread_id++;
3 a0 u& _" Y; n" h5 v7 z- q' v: @ k. [7 F N) s. r
//线程进了线程调度器
( e; S5 V; s( B7 O$ P/ i MYSQL_CALLBACK(thread_scheduler, add_connection, (thd)); ; N6 q8 b$ d4 Z
}
3 y' O: F6 l8 R4 x2 R' P
$ s, [9 h- f' H; r9 y6 ^& `& s! {# w; S/ j+ b: C& P
至此mysql就开启了一个线程对 3306 端口进行监控,等待客户端请求触发 add_connection 回调。% o' y1 V4 u& m; l/ n* L3 ?
& J8 s1 V$ P0 f: s% \% h* q
5 K* e$ X: t4 r" Q
2. 理解mysql是如何处理sql请求
0 I2 @5 d6 [) S3 q% b1 C这里我以Insert操作为例稍微解剖下处理流程:9 e- W: z5 U% W8 R" @
5 Q- S7 n: e3 H" i" A, O* |
当用户有请求sql过来之后,就会触发 thread_scheduler的回调函数add_connection。
5 r0 Y6 [( Z2 O7 y5 W/ u; O+ T- T7 I R
0 Q' G' R! I# S9 J( x
static scheduler_functions one_thread_per_connection_scheduler_functions=6 F( {* {7 Z! |5 B" m+ }. I
{* ?8 z( h6 v' g) v1 j9 Z, |7 G
0, // max_threads
9 p) l0 Y7 K; x- {; T- E( c NULL, // init S5 [4 u2 d- O( R$ \' m2 N4 a
init_new_connection_handler_thread, // init_new_connection_thread+ W1 H: G N( ~2 o7 G: I
create_thread_to_handle_connection, // add_connection+ d5 E7 p3 S/ {: G; E7 K
NULL, // thd_wait_begin9 V& W9 F2 o$ b9 ^* D& q% b
NULL, // thd_wait_end! B0 q: G! B6 a t& D) t8 |* I
NULL, // post_kill_notification0 v* g- I: z! x
one_thread_per_connection_end, // end_thread
+ P/ `3 [4 x+ C; B: ]7 t, P6 \ NULL, // end3 c' u! ?7 ^- A
};
& n. _& T! v8 f- n% ^( i+ ~
D% r! N; L# @. r* a! `6 i/ ?( O( x1 e* ^' }4 _
从 scheduler_functions 中可以看到,add_connection 对应了 create_thread_to_handle_connection,也就是请求来了会触发这个函数,从名字也可以看出,用一个线程处理一个用户连接。
T0 [, ?: W+ D6 X0 l: B6 ^- G0 L% V
<1> 客户端请求被 create_thread_to_handle_connection 接管及调用栈追踪
# r5 Y$ k7 k# S1 G
* g; A" B2 W8 e/ a L nvoid create_thread_to_handle_connection(THD *thd)/ s, q7 D: K7 q
{
5 O0 o7 z5 f; `. L% _9 Q) S if ((error= mysql_thread_create(key_thread_one_connection, &thd->real_id, &connection_attrib,4 }5 O7 i: n& @1 P6 B
handle_one_connection,(void*) thd))){}
* U" \' q, l2 I$ k}
* }; `! N1 L1 |% M# {5 P//触发回调函数 handle_one_connection
+ Y, X. ]2 d/ a/ G% I' T7 tpthread_handler_t handle_one_connection(void *arg)" J0 M' w% U1 a4 d6 q
{
% U4 `) q$ t6 E0 E+ L8 m do_handle_one_connection(thd);
5 `9 S2 H* _5 q- x}2 u2 h9 W! D9 x H5 w5 e6 p4 p
//继续处理
' Y9 p. G! B, W _void do_handle_one_connection(THD *thd_arg){. ?- ]" |% Z+ z% g A4 X' r9 P
while (thd_is_connection_alive(thd))# O% U/ q: n; d2 G
{' E0 d9 Y1 m: A& K" _
mysql_audit_release(thd);' @4 [* Y) q( x, ?7 A5 q5 O" J
if (do_command(thd)) break; //这里的 do_command 继续处理
4 H6 a* Y1 ]# E( ^5 k& U }# m/ A' \; E! }+ m( Z
}
3 r: Y3 F5 V8 g6 _" {* h//继续分发9 m- O1 Y; Q5 ]5 [- \% b
bool do_command(THD *thd)0 c4 l- f _( y# b" X; a
{
8 \* M6 u7 J: `4 U5 G m# W return_value= dispatch_command(command, thd, packet+1, (uint) (packet_length-1));
" F5 u& ]% P6 Q5 X( ?+ O# V}3 e$ b9 j4 h6 s% p* x3 r D- i; k
bool dispatch_command(enum enum_server_command command, THD *thd, char* packet, uint packet_length)
/ U# a$ f$ e2 O% H7 x5 C5 B{ B, u) }2 N( |0 n% \0 o$ U! w
switch (command) {! o( q1 B+ P5 U) u C
case COM_INIT_DB: .... break;3 M* m) t+ a( k( [2 @
...
v* Z; s) h1 b7 L8 T( A, R( j case COM_QUERY: //查询语句: insert xxxx
9 z5 c2 N( }8 D2 ~+ c mysql_parse(thd, thd->query(), thd->query_length(), &parser_state); //sql解析
& j. h5 {. _$ w3 q; Y break;
' o$ W2 c5 d( q }, R/ N/ U* x9 Q
}
% L; z- B, ~- c* v: A; F, f' _8 a//sql解析模块
, k) A4 t3 A% I! @, @void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state)
! p/ }! A4 z; ^{0 ]$ E x( T" T) K" _
error= mysql_execute_command(thd);0 F# D) {( P) f/ L( X
}1 D' u) O4 a% Y w5 @
# D* N, @" Z4 \
4 B5 i8 `; ?% y2 Z<2> 到这里它的Parse,Optimizer,Cache都追完了,接下来看sql的CURD类型,继续追。。。6 f' r1 _! O( m9 I& s9 Q) ~+ @# t
s! {. i5 O; q/ t" Z& Z+ P# }7 W//继续执行
% w/ w; i/ K8 i) B% e7 qint mysql_execute_command(THD *thd)
% g5 \$ |4 ]4 Y{
$ K8 g7 G( O6 B) G2 M switch (lex->sql_command) 3 g7 b$ X# w' O( X+ s. K, {& Q) O- d
{0 O H' @! n4 q8 h/ @; S2 a
case SQLCOM_SELECT: res= execute_sqlcom_select(thd, all_tables); break;/ I4 S5 a) \4 ~9 h7 P
J' }" I+ ]8 b# B0 v //这个 insert 就是我要追的, L) f$ e" f' z% ~; y$ _- Q
case SQLCOM_INSERT: res= mysql_insert(thd, all_tables, lex->field_list, lex->many_values,& d N2 Q# \; s
lex->update_list, lex->value_list,
1 o4 ?$ F. E+ @ lex->duplicates, lex->ignore);$ P j, u+ s$ J& X8 z2 w. p
}
- ~' x! T& d- Y}: ^+ E+ Y9 E1 n! k- ^; F f
//insert插入操作处理% x! F9 S6 r/ F; v4 d- ?* {
bool mysql_insert(THD *thd,TABLE_LIST *table_list,List<Item> &fields, List<List_item> &values_list,
6 |# C$ @$ K: n9 I3 Z0 Q' I List<Item> &update_fields, List<Item> &update_values,
* M, U2 _9 X- A+ c0 X4 R" i9 P enum_duplicates duplic, bool ignore)
7 c2 A2 p! i$ K% ?4 A- ^{( D6 F# h4 Z2 W+ E8 _
while ((values= its++))# |/ Q l: {5 i$ L3 @1 A
{
( i* W4 n+ r% n5 o) B+ j, m9 c, R error= write_record(thd, table, &info, &update);3 h" V3 N. i+ M
}5 t1 @3 l+ o0 D$ P t: r: `) t& S
}( n3 i2 c+ v3 L! n
//写入记录* M7 }" K& G# L9 y
int write_record(THD *thd, TABLE *table, COPY_INFO *info, COPY_INFO *update)- K7 G# X3 I J T7 q: ?& w
{! r9 ]5 j& [, }4 O4 Q% ^
if (duplicate_handling == DUP_REPLACE || duplicate_handling == DUP_UPDATE)6 c' R# G5 h1 U q3 ?' a4 i
{
" y- t' a1 a1 h0 T // ha_write_row 重点是这个函数
]4 [# x. y. `, I3 A while ((error=table->file->ha_write_row(table->record[0])))& p! l0 {, z2 N/ C' A
{3 R8 v/ D8 `( y5 p! T9 G- F( ]9 f
....
! _: H! j- P* l7 b0 {6 ]/ Z- U }
. c$ t6 o& C. |/ l Y0 R }* a( U/ N Z' E5 [
}
, `2 f, `; d1 c4 N% g2 p
; }' x* x% @, Y6 {1 R
' O. _' Z2 O8 F0 J: z4 g
5 ]/ I+ m7 ^9 S) z可以看到,调用链还是挺深的,追到 ha_write_row 方法基本上算是追到头了,再往下的话就是 MySql Server 给 Storage Engine提供的接口实现了,不信的话继续看呗。。。
% ~4 M4 ^, ~ E( H7 b) u6 `& y/ F8 Z) s
<3> 继续挖 ha_write_row5 X; Y/ n8 B O) K2 U1 I
7 A& ?" @. {3 z
int handler::ha_write_row(uchar *buf)" n: l) q$ X" L& |
{; Z8 v% C2 s J, O# ^ y7 v
MYSQL_TABLE_IO_WAIT(m_psi, PSI_TABLE_WRITE_ROW, MAX_KEY, 0,{ error= write_row(buf); })" E. D) z7 K) T/ ?4 M
}
4 ~6 C6 P H& _/ q7 o1 O6 @6 g, s" I8 \
//这是一个虚方法; U7 @! Z: u/ ^/ w# Y
virtual int write_row(uchar *buf __attribute__((unused)))5 F& L7 Q: h7 z
{
) y9 c) @1 W. i3 ] return HA_ERR_WRONG_COMMAND;
# H, Z( ]; d. _, h5 q+ E$ o8 K' u7 \}
# q" K2 d! E% ?6 }# [. H" X' ]) r d% {" W( R
b* k$ `7 b3 A7 P# V4 z看到没有,write_row是个虚方法,也就是给底层方法实现的,在这里就是给各大Storage Engines的哈。😁😁😁
$ {1 E/ j3 j" a: I" b: a5 M1 R" w2 o5 n
3. 调用链图# J X$ y( G- q4 p$ A
这么多方法,看起来有点懵懵的吧,我来画一张图,帮助大家理解下这个调用堆栈。
/ l5 T t% n) i4 J; j+ R3 {![]()
/ O: T# A3 X7 v: z r" N3 k0 p/ B) F8 Y$ ?- W
% `/ x3 P$ f, Y9 D) D3 I三:总结/ x, h1 h0 T7 m! C9 i# b( e
大家一定要熟读架构图,有了架构图从源码中找信息就方便多了,总之学习mysql成就感还是满满的。1 F( f4 P' d% u* H5 t0 |. M
————————————————* O+ A/ g# x, W# s6 o! o7 r9 W
版权声明:本文为CSDN博主「一线码农」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。6 J+ G/ ?4 ]5 ?7 b
原文链接:https://blog.csdn.net/huangxinchen520/article/details/106487415
' |4 O+ Q2 @# s# T* j) K* Q |
zan
|